From 5c42b7fb7cbd2a9cf51b56cbc099eb0e35d967c7 Mon Sep 17 00:00:00 2001 From: harttle Date: Tue, 20 Aug 2024 01:26:51 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20harttle/?= =?UTF-8?q?liquidjs@292a93b7384050c0d16d05cadf5036c5f1bd5f12=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filters/abs.html | 14 +- filters/append.html | 12 +- filters/array_to_sentence_string.html | 10 +- filters/at_least.html | 10 +- filters/at_most.html | 10 +- filters/capitalize.html | 10 +- filters/ceil.html | 18 +- filters/cgi_escape.html | 6 +- filters/compact.html | 28 +- filters/concat.html | 36 +- filters/date.html | 18 +- filters/date_to_long_string.html | 10 +- filters/date_to_rfc822.html | 6 +- filters/date_to_string.html | 10 +- filters/date_to_xmlschema.html | 6 +- filters/default.html | 21 +- filters/divided_by.html | 17 +- filters/downcase.html | 10 +- filters/escape.html | 8 +- filters/escape_once.html | 4 +- filters/find.html | 12 +- filters/find_exp.html | 12 +- filters/first.html | 16 +- filters/floor.html | 18 +- filters/group_by.html | 35 +- filters/group_by_exp.html | 35 +- filters/inspect.html | 19 +- filters/join.html | 8 +- filters/json.html | 16 +- filters/jsonify.html | 2 +- filters/last.html | 16 +- filters/lstrip.html | 6 +- filters/map.html | 14 +- filters/minus.html | 14 +- filters/modulo.html | 14 +- filters/newline_to_br.html | 12 +- filters/normalize_whitespace.html | 6 +- filters/number_of_words.html | 18 +- filters/overview.html | 2 +- filters/plus.html | 14 +- filters/pop.html | 13 +- filters/prepend.html | 12 +- filters/push.html | 14 +- filters/raw.html | 18 +- filters/remove.html | 6 +- filters/remove_first.html | 6 +- filters/remove_last.html | 6 +- filters/replace.html | 6 +- filters/replace_first.html | 6 +- filters/replace_last.html | 6 +- filters/reverse.html | 14 +- filters/round.html | 14 +- filters/rstrip.html | 6 +- filters/shift.html | 13 +- filters/size.html | 18 +- filters/slice.html | 18 +- filters/slugify.html | 22 +- filters/sort.html | 15 +- filters/sort_natural.html | 15 +- filters/split.html | 20 +- filters/strip.html | 6 +- filters/strip_html.html | 6 +- filters/strip_newlines.html | 12 +- filters/sum.html | 6 +- filters/times.html | 14 +- filters/to_integer.html | 6 +- filters/truncate.html | 14 +- filters/truncatewords.html | 14 +- filters/uniq.html | 7 +- filters/unshift.html | 14 +- filters/upcase.html | 10 +- filters/uri_escape.html | 6 +- filters/url_decode.html | 6 +- filters/url_encode.html | 10 +- filters/where.html | 65 +- filters/where_exp.html | 24 +- filters/xml_escape.html | 6 +- sitemap.xml | 1270 +++++++++--------- tags/assign.html | 14 +- tags/capture.html | 18 +- tags/case.html | 14 +- tags/comment.html | 8 +- tags/cycle.html | 22 +- tags/decrement.html | 10 +- tags/echo.html | 7 +- tags/for.html | 135 +- tags/if.html | 19 +- tags/include.html | 30 +- tags/increment.html | 21 +- tags/inline_comment.html | 27 +- tags/layout.html | 47 +- tags/liquid.html | 15 +- tags/overview.html | 2 +- tags/raw.html | 9 +- tags/render.html | 30 +- tags/tablerow.html | 87 +- tags/unless.html | 12 +- tutorials/access-scope-in-filters.html | 15 +- tutorials/caching.html | 23 +- tutorials/changelog.html | 2 +- tutorials/contribution-guidelines.html | 2 +- tutorials/differences.html | 2 +- tutorials/dos.html | 17 +- tutorials/drops.html | 116 +- tutorials/escaping.html | 24 +- tutorials/intro-to-liquid.html | 16 +- tutorials/migrate-to-9.html | 2 +- tutorials/operators.html | 2 +- tutorials/options.html | 25 +- tutorials/parse-parameters.html | 56 +- tutorials/partials-and-layouts.html | 28 +- tutorials/plugins.html | 18 +- tutorials/register-filters-tags.html | 52 +- tutorials/render-file.html | 57 +- tutorials/render-tag-content.html | 72 +- tutorials/setup.html | 37 +- tutorials/sync-and-async.html | 56 +- tutorials/truthy-and-falsy.html | 2 +- tutorials/use-in-expressjs.html | 33 +- tutorials/whitespace-control.html | 13 +- zh-cn/filters/abs.html | 14 +- zh-cn/filters/append.html | 12 +- zh-cn/filters/array_to_sentence_string.html | 10 +- zh-cn/filters/at_least.html | 10 +- zh-cn/filters/at_most.html | 10 +- zh-cn/filters/capitalize.html | 10 +- zh-cn/filters/ceil.html | 18 +- zh-cn/filters/cgi_escape.html | 6 +- zh-cn/filters/compact.html | 28 +- zh-cn/filters/concat.html | 36 +- zh-cn/filters/date.html | 24 +- zh-cn/filters/date_to_long_string.html | 10 +- zh-cn/filters/date_to_rfc822.html | 6 +- zh-cn/filters/date_to_string.html | 10 +- zh-cn/filters/date_to_xmlschema.html | 6 +- zh-cn/filters/default.html | 21 +- zh-cn/filters/divided_by.html | 17 +- zh-cn/filters/downcase.html | 10 +- zh-cn/filters/escape.html | 8 +- zh-cn/filters/escape_once.html | 4 +- zh-cn/filters/find.html | 12 +- zh-cn/filters/find_exp.html | 12 +- zh-cn/filters/first.html | 16 +- zh-cn/filters/floor.html | 18 +- zh-cn/filters/group_by.html | 35 +- zh-cn/filters/group_by_exp.html | 35 +- zh-cn/filters/inspect.html | 19 +- zh-cn/filters/join.html | 8 +- zh-cn/filters/json.html | 16 +- zh-cn/filters/jsonify.html | 2 +- zh-cn/filters/last.html | 16 +- zh-cn/filters/lstrip.html | 6 +- zh-cn/filters/map.html | 14 +- zh-cn/filters/minus.html | 14 +- zh-cn/filters/modulo.html | 14 +- zh-cn/filters/newline_to_br.html | 12 +- zh-cn/filters/normalize_whitespace.html | 6 +- zh-cn/filters/number_of_words.html | 18 +- zh-cn/filters/overview.html | 2 +- zh-cn/filters/plus.html | 14 +- zh-cn/filters/pop.html | 13 +- zh-cn/filters/prepend.html | 12 +- zh-cn/filters/push.html | 14 +- zh-cn/filters/raw.html | 18 +- zh-cn/filters/remove.html | 6 +- zh-cn/filters/remove_first.html | 6 +- zh-cn/filters/remove_last.html | 6 +- zh-cn/filters/replace.html | 6 +- zh-cn/filters/replace_first.html | 6 +- zh-cn/filters/replace_last.html | 6 +- zh-cn/filters/reverse.html | 14 +- zh-cn/filters/round.html | 14 +- zh-cn/filters/rstrip.html | 6 +- zh-cn/filters/shift.html | 13 +- zh-cn/filters/size.html | 18 +- zh-cn/filters/slice.html | 18 +- zh-cn/filters/slugify.html | 22 +- zh-cn/filters/sort.html | 15 +- zh-cn/filters/sort_natural.html | 15 +- zh-cn/filters/split.html | 20 +- zh-cn/filters/strip.html | 6 +- zh-cn/filters/strip_html.html | 6 +- zh-cn/filters/strip_newlines.html | 12 +- zh-cn/filters/times.html | 14 +- zh-cn/filters/to_integer.html | 6 +- zh-cn/filters/truncate.html | 14 +- zh-cn/filters/truncatewords.html | 14 +- zh-cn/filters/uniq.html | 5 +- zh-cn/filters/unshift.html | 14 +- zh-cn/filters/upcase.html | 10 +- zh-cn/filters/uri_escape.html | 6 +- zh-cn/filters/url_decode.html | 6 +- zh-cn/filters/url_encode.html | 10 +- zh-cn/filters/where.html | 65 +- zh-cn/filters/where_exp.html | 24 +- zh-cn/filters/xml_escape.html | 6 +- zh-cn/tags/assign.html | 14 +- zh-cn/tags/capture.html | 18 +- zh-cn/tags/case.html | 14 +- zh-cn/tags/comment.html | 8 +- zh-cn/tags/cycle.html | 22 +- zh-cn/tags/decrement.html | 10 +- zh-cn/tags/echo.html | 7 +- zh-cn/tags/for.html | 116 +- zh-cn/tags/if.html | 19 +- zh-cn/tags/include.html | 30 +- zh-cn/tags/increment.html | 21 +- zh-cn/tags/inline_comment.html | 27 +- zh-cn/tags/layout.html | 20 +- zh-cn/tags/liquid.html | 15 +- zh-cn/tags/overview.html | 2 +- zh-cn/tags/raw.html | 9 +- zh-cn/tags/render.html | 15 +- zh-cn/tags/tablerow.html | 87 +- zh-cn/tags/unless.html | 12 +- zh-cn/tutorials/access-scope-in-filters.html | 15 +- zh-cn/tutorials/caching.html | 23 +- zh-cn/tutorials/changelog.html | 2 +- zh-cn/tutorials/contribution-guidelines.html | 2 +- zh-cn/tutorials/differences.html | 2 +- zh-cn/tutorials/dos.html | 17 +- zh-cn/tutorials/drops.html | 116 +- zh-cn/tutorials/escaping.html | 24 +- zh-cn/tutorials/intro-to-liquid.html | 16 +- zh-cn/tutorials/migrate-to-9.html | 2 +- zh-cn/tutorials/operators.html | 2 +- zh-cn/tutorials/options.html | 25 +- zh-cn/tutorials/parse-parameters.html | 57 +- zh-cn/tutorials/partials-and-layouts.html | 28 +- zh-cn/tutorials/plugins.html | 16 +- zh-cn/tutorials/register-filters-tags.html | 65 +- zh-cn/tutorials/render-file.html | 45 +- zh-cn/tutorials/render-tag-content.html | 72 +- zh-cn/tutorials/setup.html | 25 +- zh-cn/tutorials/sync-and-async.html | 56 +- zh-cn/tutorials/truthy-and-falsy.html | 2 +- zh-cn/tutorials/use-in-expressjs.html | 33 +- zh-cn/tutorials/whitespace-control.html | 13 +- 238 files changed, 3824 insertions(+), 1819 deletions(-) diff --git a/filters/abs.html b/filters/abs.html index a7e9e7554..f3f7d8315 100644 --- a/filters/abs.html +++ b/filters/abs.html @@ -96,27 +96,27 @@

Liquid filter that returns the absolute value of a number.

Input

-
1
{{ -17 | abs }}
+
{{ -17 | abs }}

Output

-
1
17
+
17

Input

-
1
{{ 4 | abs }}
+
{{ 4 | abs }}

Output

-
1
4
+
4

abs will also work on a string that only contains a number:

Input

-
1
{{ "-19.86" | abs }}
+
{{ "-19.86" | abs }}

Output

-
1
19.86
+
19.86
diff --git a/filters/append.html b/filters/append.html index bb7de23e5..5d7693a65 100644 --- a/filters/append.html +++ b/filters/append.html @@ -96,21 +96,23 @@

Concatenates two strings and returns the concatenated value.

Input

-
1
{{ "/my/fancy/url" | append: ".html" }}
+
{{ "/my/fancy/url" | append: ".html" }}

Output

-
1
/my/fancy/url.html
+
/my/fancy/url.html

append can also be used with variables:

Input

-
1
2
{% assign filename = "/index.html" %}
{{ "website.com" | append: filename }}
+
{% assign filename = "/index.html" %}
+{{ "website.com" | append: filename }}

Output

-
1
2

website.com/index.html
+

+website.com/index.html
diff --git a/filters/array_to_sentence_string.html b/filters/array_to_sentence_string.html index 5eb1f08fb..908e7fba0 100644 --- a/filters/array_to_sentence_string.html +++ b/filters/array_to_sentence_string.html @@ -96,20 +96,20 @@

Convert an array into a sentence. Useful for listing tags. Optional argument for connector.

Input

-
1
{{ "foo,bar,baz" | split: "," | array_to_sentence_string }}
+
{{ "foo,bar,baz" | split: "," | array_to_sentence_string }}

Output

-
1
foo, bar, and baz
+
foo, bar, and baz

Input

-
1
{{ "foo,bar,baz" | split: "," | array_to_sentence_string: "or" }}
+
{{ "foo,bar,baz" | split: "," | array_to_sentence_string: "or" }}

Output

-
1
foo, bar, or baz
+
foo, bar, or baz
diff --git a/filters/at_least.html b/filters/at_least.html index 4a9f7c3ec..a81cd9b25 100644 --- a/filters/at_least.html +++ b/filters/at_least.html @@ -96,20 +96,20 @@

Limits a number to a minimum value.

Input

-
1
{{ 4 | at_least: 5 }}
+
{{ 4 | at_least: 5 }}

Output

-
1
5
+
5

Input

-
1
{{ 4 | at_least: 3 }}
+
{{ 4 | at_least: 3 }}

Output

-
1
4
+
4
diff --git a/filters/at_most.html b/filters/at_most.html index febd97e89..361ba97f4 100644 --- a/filters/at_most.html +++ b/filters/at_most.html @@ -96,20 +96,20 @@

Limits a number to a maximum value.

Input

-
1
{{ 4 | at_most: 5 }}
+
{{ 4 | at_most: 5 }}

Output

-
1
4
+
4

Input

-
1
{{ 4 | at_most: 3 }}
+
{{ 4 | at_most: 3 }}

Output

-
1
3
+
3
diff --git a/filters/capitalize.html b/filters/capitalize.html index b5cc84847..fe3989b44 100644 --- a/filters/capitalize.html +++ b/filters/capitalize.html @@ -96,21 +96,21 @@

Makes the first character of a string capitalized.

Input

-
1
{{ "title" | capitalize }}
+
{{ "title" | capitalize }}

Output

-
1
Title
+
Title

capitalize only capitalizes the first character of a string, so later words are not affected:

Input

-
1
{{ "my great title" | capitalize }}
+
{{ "my great title" | capitalize }}

Output

-
1
My great title
+
My great title
diff --git a/filters/ceil.html b/filters/ceil.html index b3db572bb..fbdf472d0 100644 --- a/filters/ceil.html +++ b/filters/ceil.html @@ -96,33 +96,33 @@

Rounds the input up to the nearest whole number. LiquidJS tries to convert the input to a number before the filter is applied.

Input

-
1
{{ 1.2 | ceil }}
+
{{ 1.2 | ceil }}

Output

-
1
2
+
2

Input

-
1
{{ 2.0 | ceil }}
+
{{ 2.0 | ceil }}

Output

-
1
2
+
2

Input

-
1
{{ 183.357 | ceil }}
+
{{ 183.357 | ceil }}

Output

-
1
184
+
184

Here the input value is a string:

Input

-
1
{{ "3.5" | ceil }}
+
{{ "3.5" | ceil }}

Output

-
1
4
+
4
diff --git a/filters/cgi_escape.html b/filters/cgi_escape.html index cd3170dae..d1b82c352 100644 --- a/filters/cgi_escape.html +++ b/filters/cgi_escape.html @@ -96,14 +96,14 @@

CGI escape a string for use in a URL. Replaces any special characters with appropriate %XX replacements. CGI escape normally replaces a space with a plus + sign.

Input

-
1
{{ "foo, bar; baz?" | cgi_escape }}
+
{{ "foo, bar; baz?" | cgi_escape }}

Output

-
1
foo%2C+bar%3B+baz%3F
+
foo%2C+bar%3B+baz%3F
diff --git a/filters/compact.html b/filters/compact.html index 0cbda80fb..bf988247d 100644 --- a/filters/compact.html +++ b/filters/compact.html @@ -97,21 +97,39 @@

Removes any nil values from an array.

For this example, assume site.pages is an array of content pages for a website, and some of these pages have an attribute called category that specifies their content category. If we map those categories to an array, some of the array items might be nil if any pages do not have a category attribute.

Input

-
1
2
3
4
5
{% assign site_categories = site.pages | map: "category" %}

{% for category in site_categories %}
- {{ category }}
{% endfor %}
+
{% assign site_categories = site.pages | map: "category" %}
+
+{% for category in site_categories %}
+- {{ category }}
+{% endfor %}

Output

-
1
2
3
4
5
6
7
- business
- celebrities
-
- lifestyle
- sports
-
- technology
+
- business
+- celebrities
+-
+- lifestyle
+- sports
+-
+- technology

By using compact when we create our site_categories array, we can remove all the nil values in the array.

Input

-
1
2
3
4
5
{% assign site_categories = site.pages | map: "category" | compact %}

{% for category in site_categories %}
- {{ category }}
{% endfor %}
+
{% assign site_categories = site.pages | map: "category" | compact %}
+
+{% for category in site_categories %}
+- {{ category }}
+{% endfor %}

Output

-
1
2
3
4
5
- business
- celebrities
- lifestyle
- sports
- technology
+
- business
+- celebrities
+- lifestyle
+- sports
+- technology
diff --git a/filters/concat.html b/filters/concat.html index 6329b83b8..68cec3006 100644 --- a/filters/concat.html +++ b/filters/concat.html @@ -96,21 +96,47 @@

Concatenates (joins together) multiple arrays. The resulting array contains all the items from the input arrays.

Input

-
1
2
3
4
5
6
7
8
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
{% assign vegetables = "carrots, turnips, potatoes" | split: ", " %}

{% assign everything = fruits | concat: vegetables %}

{% for item in everything %}
- {{ item }}
{% endfor %}
+
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
+{% assign vegetables = "carrots, turnips, potatoes" | split: ", " %}
+
+{% assign everything = fruits | concat: vegetables %}
+
+{% for item in everything %}
+- {{ item }}
+{% endfor %}

Output

-
1
2
3
4
5
6
- apples
- oranges
- peaches
- carrots
- turnips
- potatoes
+
- apples
+- oranges
+- peaches
+- carrots
+- turnips
+- potatoes

You can string together concat filters to join more than two arrays:

Input

-
1
2
3
4
5
6
7
{% assign furniture = "chairs, tables, shelves" | split: ", " %}

{% assign everything = fruits | concat: vegetables | concat: furniture %}

{% for item in everything %}
- {{ item }}
{% endfor %}
+
{% assign furniture = "chairs, tables, shelves" | split: ", " %}
+
+{% assign everything = fruits | concat: vegetables | concat: furniture %}
+
+{% for item in everything %}
+- {{ item }}
+{% endfor %}

Output

-
1
2
3
4
5
6
7
8
9
- apples
- oranges
- peaches
- carrots
- turnips
- potatoes
- chairs
- tables
- shelves
+
- apples
+- oranges
+- peaches
+- carrots
+- turnips
+- potatoes
+- chairs
+- tables
+- shelves
diff --git a/filters/date.html b/filters/date.html index 01151fcff..965cd0a6d 100644 --- a/filters/date.html +++ b/filters/date.html @@ -113,7 +113,11 @@ -

Examples

1
2
3
4
5
{{ article.published_at | date: '%a, %b %d, %y' }} => Fri, Jul 17, 15
{{ "now" | date: "%Y-%m-%d %H:%M" }} => 2020-03-25 15:57

// equivalent to setting options.dateFormat = %d%q of %b %Y at %I:%M %P %Z
{{ '1990-12-31T23:30:28Z' | date: '%d%q of %b %Y at %I:%M %P %Z', -330 }} => 01st of Jan 1991 at 05:00 am +0530;
+

Examples

{{ article.published_at | date: '%a, %b %d, %y' }} => Fri, Jul 17, 15
+{{ "now" | date: "%Y-%m-%d %H:%M" }} => 2020-03-25 15:57
+
+// equivalent to setting options.dateFormat = %d%q of %b %Y at %I:%M %P %Z
+{{ '1990-12-31T23:30:28Z' | date: '%d%q of %b %Y at %I:%M %P %Z', -330 }} => 01st of Jan 1991 at 05:00 am +0530;

TimeZone

-

Examples

1
2
3
// equivalent to setting `options.timezoneOffset` to `360`
{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", 360 }} => 1990-12-31T17:00:00
{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", "Asia/Colombo" }} => 1991-01-01T04:30:00
+

Examples

// equivalent to setting `options.timezoneOffset` to `360`
+{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", 360 }} => 1990-12-31T17:00:00
+{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", "Asia/Colombo" }} => 1991-01-01T04:30:00

Input

-

Examples

1
2
{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", 360 }} => 1990-12-31T17:00:00
{{ "March 14, 2016" | date: "%b %d, %y" }} => Mar 14, 16
+

Examples

{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", 360 }} => 1990-12-31T17:00:00
+{{ "March 14, 2016" | date: "%b %d, %y" }} => Mar 14, 16

Current Date

-

Example

1
2
Last updated on: {{ "now" | date: "%Y-%m-%d %H:%M" }} => Last updated on: 2020-03-25 15:57
Last updated on: {{ "today" | date: "%Y-%m-%d %H:%M" }} => Last updated on: 2020-03-25 15:57
+

Example

Last updated on: {{ "now" | date: "%Y-%m-%d %H:%M" }} => Last updated on: 2020-03-25 15:57
+Last updated on: {{ "today" | date: "%Y-%m-%d %H:%M" }} => Last updated on: 2020-03-25 15:57
diff --git a/filters/date_to_long_string.html b/filters/date_to_long_string.html index 99c20765a..fa1223819 100644 --- a/filters/date_to_long_string.html +++ b/filters/date_to_long_string.html @@ -96,22 +96,22 @@

Convert a date to long format. Same with Jekyll date_to_long_string filter.

Input

-
1
{{ site.time | date_to_long_string }}
+
{{ site.time | date_to_long_string }}

Output

-
1
07 November 2008
+
07 November 2008

Input

-
1
{{ site.time | date_to_long_string: "ordinal" }}
+
{{ site.time | date_to_long_string: "ordinal" }}

Output

-
1
7th November 2008
+
7th November 2008

Note that JavaScript Date has not timezone information, see date filter for details.

diff --git a/filters/date_to_rfc822.html b/filters/date_to_rfc822.html index 2ad5c4597..c7589038c 100644 --- a/filters/date_to_rfc822.html +++ b/filters/date_to_rfc822.html @@ -96,16 +96,16 @@

Convert a Date into the RFC-822 format used for RSS feeds, same as Jekyll filter date_to_rfc822.

Input

-
1
{{ site.time | date_to_rfc822 }}
+
{{ site.time | date_to_rfc822 }}

Output

-
1
Mon, 07 Nov 2008 13:07:54 -0800
+
Mon, 07 Nov 2008 13:07:54 -0800

Note that JavaScript Date has not timezone information, see date filter for details.

diff --git a/filters/date_to_string.html b/filters/date_to_string.html index 5025b49b1..7d641bf1f 100644 --- a/filters/date_to_string.html +++ b/filters/date_to_string.html @@ -96,22 +96,22 @@

Convert a date to short format. Same with Jekyll date_to_string filter.

Input

-
1
{{ site.time | date_to_string }}
+
{{ site.time | date_to_string }}

Output

-
1
07 Nov 2008
+
07 Nov 2008

Input

-
1
{{ site.time | date_to_string: "ordinal", "US" }}
+
{{ site.time | date_to_string: "ordinal", "US" }}

Output

-
1
Nov 7th, 2008
+
Nov 7th, 2008

Note that JavaScript Date has not timezone information, see date filter for details.

diff --git a/filters/date_to_xmlschema.html b/filters/date_to_xmlschema.html index 799d79193..bf8512562 100644 --- a/filters/date_to_xmlschema.html +++ b/filters/date_to_xmlschema.html @@ -96,16 +96,16 @@

Convert a Date into XML Schema (ISO 8601) format, same as Jekyll filter date_to_xmlschema.

Input

-
1
{{ site.time | date_to_xmlschema }}
+
{{ site.time | date_to_xmlschema }}

Output

-
1
2008-11-07T13:07:54-08:00
+
2008-11-07T13:07:54-08:00

Note that JavaScript Date has not timezone information, see date filter for details.

diff --git a/filters/default.html b/filters/default.html index 2ab43e4e4..1426057e9 100644 --- a/filters/default.html +++ b/filters/default.html @@ -97,38 +97,41 @@

Allows you to specify a fallback in case a value doesn’t exist. default will show its value if the left side is falsy or empty (string or Array).

In this example, product_price is not defined, so the default value is used.

Input

-
1
{{ product_price | default: 2.99 }}
+
{{ product_price | default: 2.99 }}

Output

-
1
2.99
+
2.99

In this example, product_price is defined, so the default value is not used.

Input

-
1
2
{% assign product_price = 4.99 %}
{{ product_price | default: 2.99 }}
+
{% assign product_price = 4.99 %}
+{{ product_price | default: 2.99 }}

Output

-
1
4.99
+
4.99

In this example, product_price is empty, so the default value is used.

Input

-
1
2
{% assign product_price = "" %}
{{ product_price | default: 2.99 }}
+
{% assign product_price = "" %}
+{{ product_price | default: 2.99 }}

Output

-
1
2.99
+
2.99

Allowing false

v9.32.0

To allow variables to return false instead of the default value, you can use the allow_false parameter.

Input

-
1
2
{% assign display_price = false %}
{{ display_price | default: true, allow_false: true }}
+
{% assign display_price = false %}
+{{ display_price | default: true, allow_false: true }}

Output

-
1
false
+
false
diff --git a/filters/divided_by.html b/filters/divided_by.html index 5f7fb0781..a21635604 100644 --- a/filters/divided_by.html +++ b/filters/divided_by.html @@ -96,31 +96,32 @@

Divides a number by another number. The result is the string obtained by JavaScript .toString() of the result number.

Input

-
1
{{ 16 | divided_by: 4 }}
+
{{ 16 | divided_by: 4 }}

Output

-
1
4
+
4

Input

-
1
{{ 5 | divided_by: 3 }}
+
{{ 5 | divided_by: 3 }}

Output

-
1
1.6666666666666667
+
1.6666666666666667

In JavaScript, float and integer shares the same type number and we cannot tell the difference. For example:

-
1
2
// always true
5.0 === 5
+
// always true
+5.0 === 5

You’ll need to pass another integerArithmetic argument to enforce integer divide:

Input

-
1
{{ 5 | divided_by: 3, true }}
+
{{ 5 | divided_by: 3, true }}

Output

-
1
1
+
1
diff --git a/filters/downcase.html b/filters/downcase.html index ce15956eb..2dea50e08 100644 --- a/filters/downcase.html +++ b/filters/downcase.html @@ -96,20 +96,20 @@

Makes each character in a string lowercase. It has no effect on strings which are already all lowercase.

Input

-
1
{{ "Parker Moore" | downcase }}
+
{{ "Parker Moore" | downcase }}

Output

-
1
parker moore
+
parker moore

Input

-
1
{{ "apple" | downcase }}
+
{{ "apple" | downcase }}

Output

-
1
apple
+
apple
diff --git a/filters/escape.html b/filters/escape.html index 68d268268..fd7455acc 100644 --- a/filters/escape.html +++ b/filters/escape.html @@ -96,7 +96,7 @@

Escapes a string by replacing HTML special characters with escape sequences. It doesn’t change strings that don’t have anything to escape.

Input

-
1
{{ "Have you read 'James & the Giant Peach'?" | escape }}
+
{{ "Have you read 'James & the Giant Peach'?" | escape }}

Output

@@ -104,14 +104,14 @@ Have you read 'James & the Giant Peach'?
 

Input

-
1
{{ "Tetsuro Takara" | escape }}
+
{{ "Tetsuro Takara" | escape }}

Output

-
1
Tetsuro Takara
+
Tetsuro Takara
diff --git a/filters/escape_once.html b/filters/escape_once.html index 8a670d4e8..66e6ed995 100644 --- a/filters/escape_once.html +++ b/filters/escape_once.html @@ -96,7 +96,7 @@

Escapes a string without changing existing escaped entities. It doesn’t change strings that don’t have anything to escape.

Input

-
1
{{ "1 < 2 & 3" | escape_once }}
+
{{ "1 < 2 & 3" | escape_once }}

Output

@@ -115,7 +115,7 @@
 
               
               
- +
diff --git a/filters/find.html b/filters/find.html index 3e7114d66..1135c8b94 100644 --- a/filters/find.html +++ b/filters/find.html @@ -95,17 +95,21 @@

v10.11.0

Return the first object in an array for which the queried attribute has the given value or return nil if no item in the array satisfies the given criteria. For the following members array:

-
1
2
3
4
5
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2014, name: 'Jack' }
]
+
const members = [
+  { graduation_year: 2013, name: 'Jay' },
+  { graduation_year: 2014, name: 'John' },
+  { graduation_year: 2014, name: 'Jack' }
+]

Input

-
1
{{ members | find: "graduation_year", 2014 | json }}
+
{{ members | find: "graduation_year", 2014 | json }}

Output

-
1
{"graduation_year":2014,"name":"John"}
+
{"graduation_year":2014,"name":"John"}
- +
diff --git a/filters/find_exp.html b/filters/find_exp.html index eb99eabc7..896233a80 100644 --- a/filters/find_exp.html +++ b/filters/find_exp.html @@ -95,17 +95,21 @@

v10.11.0

Return the first object in an array for which the given expression evaluates to true or return nil if no item in the array satisfies the evaluated expression.

-
1
2
3
4
5
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2014, name: 'Jack' }
]
+
const members = [
+  { graduation_year: 2013, name: 'Jay' },
+  { graduation_year: 2014, name: 'John' },
+  { graduation_year: 2014, name: 'Jack' }
+]

Input

-
1
{{ members | find_exp: "item", "item.graduation_year == 2014" | json }}
+
{{ members | find_exp: "item", "item.graduation_year == 2014" | json }}

Output

-
1
{"graduation_year":2014,"name":"John"}
+
{"graduation_year":2014,"name":"John"}
- +
diff --git a/filters/first.html b/filters/first.html index 9a9d30c63..7818fb917 100644 --- a/filters/first.html +++ b/filters/first.html @@ -96,23 +96,27 @@

Returns the first item of an array.

Input

-
1
{{ "Ground control to Major Tom." | split: " " | first }}
+
{{ "Ground control to Major Tom." | split: " " | first }}

Output

-
1
Ground
+
Ground

Input

-
1
2
{% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
{{ my_array.first }}
+
{% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
+{{ my_array.first }}

Output

-
1
2

zebra
+

+zebra

You can use first with dot notation when you need to use the filter inside a tag:

-
1
2
3
{% if my_array.first == "zebra" %}
Here comes a zebra!
{% endif %}
+
{% if my_array.first == "zebra" %}
+  Here comes a zebra!
+{% endif %}
- +
diff --git a/filters/floor.html b/filters/floor.html index 6a962a809..bdb0329f2 100644 --- a/filters/floor.html +++ b/filters/floor.html @@ -96,33 +96,33 @@

Rounds the input down to the nearest whole number. LiquidJS tries to convert the input to a number before the filter is applied.

Input

-
1
{{ 1.2 | floor }}
+
{{ 1.2 | floor }}

Output

-
1
1
+
1

Input

-
1
{{ 2.0 | floor }}
+
{{ 2.0 | floor }}

Output

-
1
2
+
2

Input

-
1
{{ 183.357 | floor }}
+
{{ 183.357 | floor }}

Output

-
1
183
+
183

Here the input value is a string:

Input

-
1
{{ "3.5" | floor }}
+
{{ "3.5" | floor }}

Output

-
1
3
+
3
- +
diff --git a/filters/group_by.html b/filters/group_by.html index 5365e8bbd..c3f98356e 100644 --- a/filters/group_by.html +++ b/filters/group_by.html @@ -95,17 +95,44 @@

v10.11.0

Group an array’s items by a given property. For members array:

-
1
2
3
4
5
const members = [
{ graduation_year: 2003, name: 'Jay' },
{ graduation_year: 2003, name: 'John' },
{ graduation_year: 2004, name: 'Jack' }
]
+
const members = [
+  { graduation_year: 2003, name: 'Jay' },
+  { graduation_year: 2003, name: 'John' },
+  { graduation_year: 2004, name: 'Jack' }
+]

Input

-
1
{{ members | group_by: "graduation_year" | json: 2 }}
+
{{ members | group_by: "graduation_year" | json: 2 }}

Output

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[
{
"name": 2003,
"items": [
{
"graduation_year": 2003,
"name": "Jay"
},
{
"graduation_year": 2003,
"name": "John"
}
]
},
{
"name": 2004,
"items": [
{
"graduation_year": 2004,
"name": "Jack"
}
]
}
]
+
[
+  {
+    "name": 2003,
+    "items": [
+      {
+        "graduation_year": 2003,
+        "name": "Jay"
+      },
+      {
+        "graduation_year": 2003,
+        "name": "John"
+      }
+    ]
+  },
+  {
+    "name": 2004,
+    "items": [
+      {
+        "graduation_year": 2004,
+        "name": "Jack"
+      }
+    ]
+  }
+]
- +
diff --git a/filters/group_by_exp.html b/filters/group_by_exp.html index dd8ce9c46..f965c9aa8 100644 --- a/filters/group_by_exp.html +++ b/filters/group_by_exp.html @@ -95,17 +95,44 @@

v10.11.0

Group an array’s items using a Liquid expression. For members array below:

-
1
2
3
4
5
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2009, name: 'Jack' }
]
+
const members = [
+  { graduation_year: 2013, name: 'Jay' },
+  { graduation_year: 2014, name: 'John' },
+  { graduation_year: 2009, name: 'Jack' }
+]

Input

-
1
{{ members | group_by_exp: "item", "item.graduation_year | truncate: 3, ''" | json: 2 }}
+
{{ members | group_by_exp: "item", "item.graduation_year | truncate: 3, ''" | json: 2 }}

Output

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[
{
"name": "201",
"items": [
{
"graduation_year": 2013,
"name": "Jay"
},
{
"graduation_year": 2014,
"name": "John"
}
]
},
{
"name": "200",
"items": [
{
"graduation_year": 2009,
"name": "Jack"
}
]
}
]
+
[
+  {
+    "name": "201",
+    "items": [
+      {
+        "graduation_year": 2013,
+        "name": "Jay"
+      },
+      {
+        "graduation_year": 2014,
+        "name": "John"
+      }
+    ]
+  },
+  {
+    "name": "200",
+    "items": [
+      {
+        "graduation_year": 2009,
+        "name": "Jack"
+      }
+    ]
+  }
+]
- +
diff --git a/filters/inspect.html b/filters/inspect.html index 8d621115d..5c07e038e 100644 --- a/filters/inspect.html +++ b/filters/inspect.html @@ -95,24 +95,31 @@

v10.13.0

Similar with json, but inspect allows cyclic structure. For the scope below:

-
1
2
3
4
5
const foo = {
bar: 'BAR'
}
foo.foo = foo
const scope = { foo }
+
const foo = {
+    bar: 'BAR'
+}
+foo.foo = foo
+const scope = { foo }

Input

-
1
{% foo | inspect %}
+
{% foo | inspect %}

Output

-
1
{"bar":"BAR","foo":"[Circular]"}
+
{"bar":"BAR","foo":"[Circular]"}

Formatting

An additional space argument can be specified for the indent width.

Input

-
1
{{ foo | inspect: 4 }}
+
{{ foo | inspect: 4 }}

Output

-
1
2
3
4
{
"bar": "BAR",
"foo": "[Circular]"
}
+
{
+    "bar": "BAR",
+    "foo": "[Circular]"
+}
- +
diff --git a/filters/join.html b/filters/join.html index 3708b1277..82e089d71 100644 --- a/filters/join.html +++ b/filters/join.html @@ -96,14 +96,16 @@

Combines the items in an array into a single string using the argument as a separator.

Input

-
1
2
{% assign beatles = "John, Paul, George, Ringo" | split: ", " %}
{{ beatles | join: " and " }}
+
{% assign beatles = "John, Paul, George, Ringo" | split: ", " %}
+{{ beatles | join: " and " }}

Output

-
1
2

John and Paul and George and Ringo
+

+John and Paul and George and Ringo
- +
diff --git a/filters/json.html b/filters/json.html index 37c3c445a..a6f389de4 100644 --- a/filters/json.html +++ b/filters/json.html @@ -96,23 +96,29 @@

Convert values to string via JSON.stringify(), for debug purpose.

Input

-
1
2
{% assign arr = "foo bar coo" | split: " " %}
{{ arr | json }}
+
{% assign arr = "foo bar coo" | split: " " %}
+{{ arr | json }}

Output

-
1
["foo","bar","coo"]
+
["foo","bar","coo"]

Space

v10.11.0

An additional space parameter can be specified to format the JSON.

Input

-
1
2
{% assign arr = "foo bar coo" | split: " " %}
{{ arr | json: 4 }}
+
{% assign arr = "foo bar coo" | split: " " %}
+{{ arr | json: 4 }}

Output

-
1
2
3
4
5
[
"foo",
"bar",
"coo"
]
+
[
+    "foo",
+    "bar",
+    "coo"
+]
- +
diff --git a/filters/jsonify.html b/filters/jsonify.html index 4a5e9f4f4..e4f84b3f7 100644 --- a/filters/jsonify.html +++ b/filters/jsonify.html @@ -98,7 +98,7 @@
- +
diff --git a/filters/last.html b/filters/last.html index 95cde6d3d..818fd2314 100644 --- a/filters/last.html +++ b/filters/last.html @@ -96,23 +96,27 @@

Returns the last item of an array.

Input

-
1
{{ "Ground control to Major Tom." | split: " " | last }}
+
{{ "Ground control to Major Tom." | split: " " | last }}

Output

-
1
Tom.
+
Tom.

Input

-
1
2
{% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
{{ my_array.last }}
+
{% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
+{{ my_array.last }}

Output

-
1
2

tiger
+

+tiger

You can use last with dot notation when you need to use the filter inside a tag:

-
1
2
3
{% if my_array.last == "tiger" %}
There goes a tiger!
{% endif %}
+
{% if my_array.last == "tiger" %}
+  There goes a tiger!
+{% endif %}
- +
diff --git a/filters/lstrip.html b/filters/lstrip.html index b50d93b9a..9d518aa7f 100644 --- a/filters/lstrip.html +++ b/filters/lstrip.html @@ -96,14 +96,14 @@

Removes all whitespace (tabs, spaces, and newlines) from the left side of a string. It does not affect spaces between words.

Input

-
1
BEGIN{{ "          So much room for activities!          " | lstrip }}END
+
BEGIN{{ "          So much room for activities!          " | lstrip }}END

Output

-
1
BEGINSo much room for activities!          END
+
BEGINSo much room for activities!          END
- +
diff --git a/filters/map.html b/filters/map.html index 47ff0a530..9914d4029 100644 --- a/filters/map.html +++ b/filters/map.html @@ -97,14 +97,22 @@

Creates an array of values by extracting the values of a named property from another object.

In this example, assume the object site.pages contains all the metadata for a website. Using assign with the map filter creates a variable that contains only the values of the category properties of everything in the site.pages object.

Input

-
1
2
3
4
5
{% assign all_categories = site.pages | map: "category" %}

{% for item in all_categories %}
- {{ item }}
{% endfor %}
+
{% assign all_categories = site.pages | map: "category" %}
+
+{% for item in all_categories %}
+- {{ item }}
+{% endfor %}

Output

-
1
2
3
4
5
- business
- celebrities
- lifestyle
- sports
- technology
+
- business
+- celebrities
+- lifestyle
+- sports
+- technology
- +
diff --git a/filters/minus.html b/filters/minus.html index 4cd045777..2dcdac3e8 100644 --- a/filters/minus.html +++ b/filters/minus.html @@ -96,26 +96,26 @@

Subtracts a number from another number.

Input

-
1
{{ 4 | minus: 2 }}
+
{{ 4 | minus: 2 }}

Output

-
1
2
+
2

Input

-
1
{{ 16 | minus: 4 }}
+
{{ 16 | minus: 4 }}

Output

-
1
12
+
12

Input

-
1
{{ 183.357 | minus: 12 }}
+
{{ 183.357 | minus: 12 }}

Output

-
1
171.357
+
171.357
- +
diff --git a/filters/modulo.html b/filters/modulo.html index cffb54750..88b28b5b6 100644 --- a/filters/modulo.html +++ b/filters/modulo.html @@ -96,26 +96,26 @@

Returns the remainder of a division operation.

Input

-
1
{{ 3 | modulo: 2 }}
+
{{ 3 | modulo: 2 }}

Output

-
1
1
+
1

Input

-
1
{{ 24 | modulo: 7 }}
+
{{ 24 | modulo: 7 }}

Output

-
1
3
+
3

Input

-
1
{{ 183.357 | modulo: 12 }}
+
{{ 183.357 | modulo: 12 }}

Output

-
1
3.3569999999999993
+
3.3569999999999993
- +
diff --git a/filters/newline_to_br.html b/filters/newline_to_br.html index f46518df4..2213f668b 100644 --- a/filters/newline_to_br.html +++ b/filters/newline_to_br.html @@ -96,14 +96,20 @@

Replaces every newline (\n) in a string with an HTML line break (<br />).

Input

-
1
2
3
4
5
6
{% capture string_with_newlines %}
Hello
there
{% endcapture %}

{{ string_with_newlines | newline_to_br }}
+
{% capture string_with_newlines %}
+Hello
+there
+{% endcapture %}
+
+{{ string_with_newlines | newline_to_br }}

Output

-
1
2

<br/>Hello<br/>there<br/>
+

+<br/>Hello<br/>there<br/>
- +
diff --git a/filters/normalize_whitespace.html b/filters/normalize_whitespace.html index aa0c919d4..7fcf2c007 100644 --- a/filters/normalize_whitespace.html +++ b/filters/normalize_whitespace.html @@ -96,14 +96,14 @@

Replace any occurrence of whitespace with a single space.

Input

-
1
{{ "a \n b" | normalize_whitespace }}
+
{{ "a \n b" | normalize_whitespace }}

Output

-
1
a b
+
a b
- +
diff --git a/filters/number_of_words.html b/filters/number_of_words.html index 8cd0e52b7..cb2dd4546 100644 --- a/filters/number_of_words.html +++ b/filters/number_of_words.html @@ -100,32 +100,32 @@
  • Passing 'auto' (auto-detect) works similar to 'cjk' but is more performant if the filter is used on a variable string that may or may not contain CJK chars.
  • Input

    -
    1
    {{ "Hello world!" | number_of_words }}
    +
    {{ "Hello world!" | number_of_words }}

    Output

    -
    1
    2
    +
    2

    Input

    -
    1
    {{ "你好hello世界world" | number_of_words }}
    +
    {{ "你好hello世界world" | number_of_words }}

    Output

    -
    1
    1
    +
    1

    Input

    -
    1
    {{ "你好hello世界world" | number_of_words: "cjk" }}
    +
    {{ "你好hello世界world" | number_of_words: "cjk" }}

    Output

    -
    1
    6
    +
    6

    Input

    -
    1
    {{ "你好hello世界world" | number_of_words: "auto" }}
    +
    {{ "你好hello世界world" | number_of_words: "auto" }}

    Output

    -
    1
    6
    +
    6
    - +
    diff --git a/filters/overview.html b/filters/overview.html index 1ee223e64..82f896254 100644 --- a/filters/overview.html +++ b/filters/overview.html @@ -129,7 +129,7 @@
    - +
    diff --git a/filters/plus.html b/filters/plus.html index 773ba2634..ca254088b 100644 --- a/filters/plus.html +++ b/filters/plus.html @@ -96,26 +96,26 @@

    Adds a number to another number.

    Input

    -
    1
    {{ 4 | plus: 2 }}
    +
    {{ 4 | plus: 2 }}

    Output

    -
    1
    6
    +
    6

    Input

    -
    1
    {{ 16 | plus: 4 }}
    +
    {{ 16 | plus: 4 }}

    Output

    -
    1
    20
    +
    20

    Input

    -
    1
    {{ 183.357 | plus: 12 }}
    +
    {{ 183.357 | plus: 12 }}

    Output

    -
    1
    195.357
    +
    195.357
    - +
    diff --git a/filters/pop.html b/filters/pop.html index 96cbe6cf7..af93a3548 100644 --- a/filters/pop.html +++ b/filters/pop.html @@ -96,14 +96,21 @@

    Pop an element from the array. It’s NON-DESTRUCTIVE, i.e. it does not mutate the array, but rather make a copy and mutate that.

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    {% assign fruits = "apples, oranges, peaches" | split: ", " %}

    {% assign everything = fruits | pop %}

    {% for item in everything %}
    - {{ item }}
    {% endfor %}
    +
    {% assign fruits = "apples, oranges, peaches" | split: ", " %}
    +
    +{% assign everything = fruits | pop %}
    +
    +{% for item in everything %}
    +- {{ item }}
    +{% endfor %}

    Output

    -
    1
    2
    - apples
    - oranges
    +
    - apples
    +- oranges
    - +
    diff --git a/filters/prepend.html b/filters/prepend.html index c39f1931c..be3ae42ef 100644 --- a/filters/prepend.html +++ b/filters/prepend.html @@ -96,21 +96,23 @@

    Adds the specified string to the beginning of another string.

    Input

    -
    1
    {{ "apples, oranges, and bananas" | prepend: "Some fruit: " }}
    +
    {{ "apples, oranges, and bananas" | prepend: "Some fruit: " }}

    Output

    -
    1
    Some fruit: apples, oranges, and bananas
    +
    Some fruit: apples, oranges, and bananas

    prepend can also be used with variables:

    Input

    -
    1
    2
    {% assign url = "example.com" %}
    {{ "/index.html" | prepend: url }}
    +
    {% assign url = "example.com" %}
    +{{ "/index.html" | prepend: url }}

    Output

    -
    1
    2

    example.com/index.html
    +
    
    +example.com/index.html
    - +
    diff --git a/filters/push.html b/filters/push.html index 7e8317cc2..54f93964f 100644 --- a/filters/push.html +++ b/filters/push.html @@ -96,14 +96,22 @@

    Push an element into array. It’s NON-DESTRUCTIVE, i.e. it does not mutate the array, but rather make a copy and mutate that.

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    {% assign fruits = "apples, oranges" | split: ", " %}

    {% assign everything = fruits | push: "peaches" %}

    {% for item in everything %}
    - {{ item }}
    {% endfor %}
    +
    {% assign fruits = "apples, oranges" | split: ", " %}
    +
    +{% assign everything = fruits | push: "peaches" %}
    +
    +{% for item in everything %}
    +- {{ item }}
    +{% endfor %}

    Output

    -
    1
    2
    3
    - apples
    - oranges
    - peaches
    +
    - apples
    +- oranges
    +- peaches
    - +
    diff --git a/filters/raw.html b/filters/raw.html index aa7ffa50e..eae0d02c8 100644 --- a/filters/raw.html +++ b/filters/raw.html @@ -99,32 +99,32 @@

    Input (outputEscape not set)

    -
    1
    {{ "<" }}
    +
    {{ "<" }}

    Output

    -
    1
    <
    +
    <

    Input (outputEscape="escape")

    -
    1
    {{ "<" }}
    +
    {{ "<" }}

    Output

    -
    1
    &lt;
    +
    &lt;

    Input (outputEscape="json")

    -
    1
    {{ "<" }}
    +
    {{ "<" }}

    Output

    -
    1
    "<"
    +
    "<"

    Input (outputEscape="escape")

    -
    1
    {{ "<" | raw }}
    +
    {{ "<" | raw }}

    Output

    -
    1
    <
    +
    <
    - +
    diff --git a/filters/remove.html b/filters/remove.html index 632492c37..714a450c5 100644 --- a/filters/remove.html +++ b/filters/remove.html @@ -96,14 +96,14 @@

    Removes every occurrence of the specified substring from a string.

    Input

    -
    1
    {{ "I strained to see the train through the rain" | remove: "rain" }}
    +
    {{ "I strained to see the train through the rain" | remove: "rain" }}

    Output

    -
    1
    I sted to see the t through the 
    +
    I sted to see the t through the 
    - +
    diff --git a/filters/remove_first.html b/filters/remove_first.html index 00f016234..3a8fe2b12 100644 --- a/filters/remove_first.html +++ b/filters/remove_first.html @@ -96,14 +96,14 @@

    Removes only the first occurrence of the specified substring from a string.

    Input

    -
    1
    {{ "I strained to see the train through the rain" | remove_first: "rain" }}
    +
    {{ "I strained to see the train through the rain" | remove_first: "rain" }}

    Output

    -
    1
    I sted to see the train through the rain
    +
    I sted to see the train through the rain
    - +
    diff --git a/filters/remove_last.html b/filters/remove_last.html index ae12fa23a..d3c269ac0 100644 --- a/filters/remove_last.html +++ b/filters/remove_last.html @@ -96,14 +96,14 @@

    Removes only the last occurrence of the specified substring from a string.

    Input

    -
    1
    {{ "I strained to see the train through the rain" | remove_last: "rain" }}
    +
    {{ "I strained to see the train through the rain" | remove_last: "rain" }}

    Output

    -
    1
    I strained to see the train through the
    +
    I strained to see the train through the
    - +
    diff --git a/filters/replace.html b/filters/replace.html index 9056a2028..ad42f3ea2 100644 --- a/filters/replace.html +++ b/filters/replace.html @@ -96,14 +96,14 @@

    Replaces every occurrence of the first argument in a string with the second argument.

    Input

    -
    1
    {{ "Take my protein pills and put my helmet on" | replace: "my", "your" }}
    +
    {{ "Take my protein pills and put my helmet on" | replace: "my", "your" }}

    Output

    -
    1
    Take your protein pills and put your helmet on
    +
    Take your protein pills and put your helmet on
    - +
    diff --git a/filters/replace_first.html b/filters/replace_first.html index d355db4a4..659bfb32e 100644 --- a/filters/replace_first.html +++ b/filters/replace_first.html @@ -96,14 +96,14 @@

    Replaces only the first occurrence of the first argument in a string with the second argument.

    Input

    -
    1
    {{ "Take my protein pills and put my helmet on" | replace_first: "my", "your" }}
    +
    {{ "Take my protein pills and put my helmet on" | replace_first: "my", "your" }}

    Output

    -
    1
    Take your protein pills and put my helmet on
    +
    Take your protein pills and put my helmet on
    - +
    diff --git a/filters/replace_last.html b/filters/replace_last.html index fc4af215e..9b58e4b3f 100644 --- a/filters/replace_last.html +++ b/filters/replace_last.html @@ -96,14 +96,14 @@

    Replaces only the last occurrence of the first argument in a string with the second argument.

    Input

    -
    1
    {{ "Take my protein pills and put my helmet on" | replace_last: "my", "your" }}
    +
    {{ "Take my protein pills and put my helmet on" | replace_last: "my", "your" }}

    Output

    -
    1
    Take my protein pills and put your helmet on
    +
    Take my protein pills and put your helmet on
    - +
    diff --git a/filters/reverse.html b/filters/reverse.html index 1cdbbc4d8..9bf3ba799 100644 --- a/filters/reverse.html +++ b/filters/reverse.html @@ -96,21 +96,25 @@

    Reverses the order of the items in an array. reverse cannot reverse a string.

    Input

    -
    1
    2
    3
    {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}

    {{ my_array | reverse | join: ", " }}
    +
    {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}
    +
    +{{ my_array | reverse | join: ", " }}

    Output

    -
    1
    2
    3


    plums, peaches, oranges, apples
    +
    
    +
    +plums, peaches, oranges, apples

    Although reverse cannot be used directly on a string, you can split a string into an array, reverse the array, and rejoin it by chaining together filters:

    Input

    -
    1
    {{ "Ground control to Major Tom." | split: "" | reverse | join: "" }}
    +
    {{ "Ground control to Major Tom." | split: "" | reverse | join: "" }}

    Output

    -
    1
    .moT rojaM ot lortnoc dnuorG
    +
    .moT rojaM ot lortnoc dnuorG
    - +
    diff --git a/filters/round.html b/filters/round.html index 59ecdabdd..19c17b508 100644 --- a/filters/round.html +++ b/filters/round.html @@ -96,26 +96,26 @@

    Rounds a number to the nearest integer or, if a number is passed as an argument, to that number of decimal places.

    Input

    -
    1
    {{ 1.2 | round }}
    +
    {{ 1.2 | round }}

    Output

    -
    1
    1
    +
    1

    Input

    -
    1
    {{ 2.7 | round }}
    +
    {{ 2.7 | round }}

    Output

    -
    1
    3
    +
    3

    Input

    -
    1
    {{ 183.357 | round: 2 }}
    +
    {{ 183.357 | round: 2 }}

    Output

    -
    1
    183.36
    +
    183.36
    - +
    diff --git a/filters/rstrip.html b/filters/rstrip.html index 73df9a58d..0daf20909 100644 --- a/filters/rstrip.html +++ b/filters/rstrip.html @@ -96,14 +96,14 @@

    Removes all whitespace (tabs, spaces, and newlines) from the right side of a string. It does not affect spaces between words.

    Input

    -
    1
    BEGIN{{ "          So much room for activities!          " | rstrip }}END
    +
    BEGIN{{ "          So much room for activities!          " | rstrip }}END

    Output

    -
    1
    BEGIN          So much room for activities!END
    +
    BEGIN          So much room for activities!END
    - +
    diff --git a/filters/shift.html b/filters/shift.html index 0fb08eb0f..3d83969be 100644 --- a/filters/shift.html +++ b/filters/shift.html @@ -96,14 +96,21 @@

    Shift an element from the array. It’s NON-DESTRUCTIVE, i.e. it does not mutate the array, but rather make a copy and mutate that.

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    {% assign fruits = "apples, oranges, peaches" | split: ", " %}

    {% assign everything = fruits | shift %}

    {% for item in everything %}
    - {{ item }}
    {% endfor %}
    +
    {% assign fruits = "apples, oranges, peaches" | split: ", " %}
    +
    +{% assign everything = fruits | shift %}
    +
    +{% for item in everything %}
    +- {{ item }}
    +{% endfor %}

    Output

    -
    1
    2
    - oranges
    - peaches
    +
    - oranges
    +- peaches
    - +
    diff --git a/filters/size.html b/filters/size.html index a5efacf24..7482a00aa 100644 --- a/filters/size.html +++ b/filters/size.html @@ -96,23 +96,29 @@

    Returns the number of characters in a string or the number of items in an array.

    Input

    -
    1
    {{ "Ground control to Major Tom." | size }}
    +
    {{ "Ground control to Major Tom." | size }}

    Output

    -
    1
    28
    +
    28

    Input

    -
    1
    2
    3
    {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}

    {{ my_array.size }}
    +
    {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}
    +
    +{{ my_array.size }}

    Output

    -
    1
    2
    3


    4
    +
    
    +
    +4

    You can use size with dot notation when you need to use the filter inside a tag:

    -
    1
    2
    3
    {% if site.pages.size > 10 %}
    This is a big website!
    {% endif %}
    +
    {% if site.pages.size > 10 %}
    +  This is a big website!
    +{% endif %}
    - +
    diff --git a/filters/slice.html b/filters/slice.html index e07cd31f7..a83c9e61a 100644 --- a/filters/slice.html +++ b/filters/slice.html @@ -97,33 +97,33 @@

    Returns a substring of 1 character beginning at the index specified by the first argument. An optional second argument specifies the length of the substring to be returned.

    String indices are numbered starting from 0.

    Input

    -
    1
    {{ "Liquid" | slice: 0 }}
    +
    {{ "Liquid" | slice: 0 }}

    Output

    -
    1
    L
    +
    L

    Input

    -
    1
    {{ "Liquid" | slice: 2 }}
    +
    {{ "Liquid" | slice: 2 }}

    Output

    -
    1
    q
    +
    q

    Input

    -
    1
    {{ "Liquid" | slice: 2, 5 }}
    +
    {{ "Liquid" | slice: 2, 5 }}

    Output

    -
    1
    quid
    +
    quid

    If the first argument is a negative number, the indices are counted from the end of the string:

    Input

    -
    1
    {{ "Liquid" | slice: -3, 2 }}
    +
    {{ "Liquid" | slice: -3, 2 }}

    Output

    -
    1
    ui
    +
    ui
    - +
    diff --git a/filters/slugify.html b/filters/slugify.html index 08aa29676..7ec0cb93f 100644 --- a/filters/slugify.html +++ b/filters/slugify.html @@ -108,33 +108,33 @@
  • case: boolean. The default is false. The original case of slug will be retained if set to true.
  • Input

    -
    1
    {{ "The _config.yml file" | slugify }}
    +
    {{ "The _config.yml file" | slugify }}

    Output

    -
    1
    the-config-yml-file
    +
    the-config-yml-file

    Input

    -
    1
    {{ "The _config.yml file" | slugify: "pretty" }}
    +
    {{ "The _config.yml file" | slugify: "pretty" }}

    Output

    -
    1
    the-_config.yml-file
    +
    the-_config.yml-file

    Input

    -
    1
    {{ "The _cönfig.yml file" | slugify: "ascii" }}
    +
    {{ "The _cönfig.yml file" | slugify: "ascii" }}

    Output

    -
    1
    the-c-nfig-yml-file
    +
    the-c-nfig-yml-file

    Input

    -
    1
    {{ "The cönfig.yml file" | slugify: "latin" }}
    +
    {{ "The cönfig.yml file" | slugify: "latin" }}

    Output

    -
    1
    the-config-yml-file
    +
    the-config-yml-file

    Input

    -
    1
    {{ "The cönfig.yml file" | slugify: "latin", true }}
    +
    {{ "The cönfig.yml file" | slugify: "latin", true }}

    Output

    -
    1
    The-config-yml-file
    +
    The-config-yml-file
    - +
    diff --git a/filters/sort.html b/filters/sort.html index 169c4c92b..eaf85b907 100644 --- a/filters/sort.html +++ b/filters/sort.html @@ -96,17 +96,24 @@

    Sorts items in an array in case-sensitive order.

    Input

    -
    1
    2
    3
    {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}

    {{ my_array | sort | join: ", " }}
    +
    {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}
    +
    +{{ my_array | sort | join: ", " }}

    Output

    -
    1
    2
    3


    Sally Snake, giraffe, octopus, zebra
    +
    
    +
    +Sally Snake, giraffe, octopus, zebra

    An optional argument specifies which property of the array’s items to use for sorting.

    -
    1
    2
    3
    4
    {% assign products_by_price = collection.products | sort: "price" %}
    {% for product in products_by_price %}
    <h4>{{ product.title }}</h4>
    {% endfor %}
    +
    {% assign products_by_price = collection.products | sort: "price" %}
    +{% for product in products_by_price %}
    +  <h4>{{ product.title }}</h4>
    +{% endfor %}
    - +
    diff --git a/filters/sort_natural.html b/filters/sort_natural.html index 78f53066b..3d46af705 100644 --- a/filters/sort_natural.html +++ b/filters/sort_natural.html @@ -96,17 +96,24 @@

    Sorts items in an array in case-insensitive order.

    Input

    -
    1
    2
    3
    {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}

    {{ my_array | sort_natural | join: ", " }}
    +
    {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}
    +
    +{{ my_array | sort_natural | join: ", " }}

    Output

    -
    1
    2
    3


    giraffe, octopus, Sally Snake, zebra
    +
    
    +
    +giraffe, octopus, Sally Snake, zebra

    An optional argument specifies which property of the array’s items to use for sorting.

    -
    1
    2
    3
    4
    {% assign products_by_company = collection.products | sort_natural: "company" %}
    {% for product in products_by_company %}
    <h4>{{ product.title }}</h4>
    {% endfor %}
    +
    {% assign products_by_company = collection.products | sort_natural: "company" %}
    +{% for product in products_by_company %}
    +  <h4>{{ product.title }}</h4>
    +{% endfor %}
    - +
    diff --git a/filters/split.html b/filters/split.html index ebfaf5075..e3cc544f6 100644 --- a/filters/split.html +++ b/filters/split.html @@ -96,14 +96,28 @@

    Divides a string into an array using the argument as a separator. split is commonly used to convert comma-separated items from a string to an array.

    Input

    -
    1
    2
    3
    4
    5
    {% assign beatles = "John, Paul, George, Ringo" | split: ", " %}

    {% for member in beatles %}
    {{ member }}
    {% endfor %}
    +
    {% assign beatles = "John, Paul, George, Ringo" | split: ", " %}
    +
    +{% for member in beatles %}
    +  {{ member }}
    +{% endfor %}

    Output

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11




    John

    Paul

    George

    Ringo
    +
    
    +
    +
    +
    +John
    +
    +Paul
    +
    +George
    +
    +Ringo
    - +
    diff --git a/filters/strip.html b/filters/strip.html index 26dfd7d13..9eb726038 100644 --- a/filters/strip.html +++ b/filters/strip.html @@ -96,14 +96,14 @@

    Removes all whitespace (tabs, spaces, and newlines) from both the left and right sides of a string. It does not affect spaces between words.

    Input

    -
    1
    BEGIN{{ "          So much room for activities!          " | strip }}END
    +
    BEGIN{{ "          So much room for activities!          " | strip }}END

    Output

    -
    1
    BEGINSo much room for activities!END
    +
    BEGINSo much room for activities!END
    - +
    diff --git a/filters/strip_html.html b/filters/strip_html.html index 167e49ad7..764fb0b79 100644 --- a/filters/strip_html.html +++ b/filters/strip_html.html @@ -96,14 +96,14 @@

    Removes any HTML tags from a string.

    Input

    -
    1
    {{ "Have <em>you</em> read <strong>Ulysses</strong>?" | strip_html }}
    +
    {{ "Have <em>you</em> read <strong>Ulysses</strong>?" | strip_html }}

    Output

    -
    1
    Have you read Ulysses?
    +
    Have you read Ulysses?
    - +
    diff --git a/filters/strip_newlines.html b/filters/strip_newlines.html index f1432a11e..4de1cfa5e 100644 --- a/filters/strip_newlines.html +++ b/filters/strip_newlines.html @@ -96,14 +96,20 @@

    Removes any newline characters (line breaks) from a string.

    Input

    -
    1
    2
    3
    4
    5
    6
    {% capture string_with_newlines %}
    Hello
    there
    {% endcapture %}

    {{ string_with_newlines | strip_newlines }}
    +
    {% capture string_with_newlines %}
    +Hello
    +there
    +{% endcapture %}
    +
    +{{ string_with_newlines | strip_newlines }}

    Output

    -
    1
    2

    Hellothere
    +
    
    +Hellothere
    - +
    diff --git a/filters/sum.html b/filters/sum.html index 995849733..fac7768d4 100644 --- a/filters/sum.html +++ b/filters/sum.html @@ -97,14 +97,14 @@

    Computes the sum of all the numbers in an array.
    An optional argument specifies which property of the array’s items to sum up.

    In this example, assume the object cart.products contains an array of all products in the cart of a website.
    Assume each cart product has a qty property that gives the count of that product instance in the cart.
    Using the sum filter we can calculate the total number of products in the cart.

    Input

    -
    1
    The cart has {{ order.products | sum: "qty" }} products.
    +
    The cart has {{ order.products | sum: "qty" }} products.

    Output

    -
    1
    The cart has 7 products.
    +
    The cart has 7 products.
    - +
    diff --git a/filters/times.html b/filters/times.html index c3124b956..ab029b2e3 100644 --- a/filters/times.html +++ b/filters/times.html @@ -96,26 +96,26 @@

    Multiplies a number by another number.

    Input

    -
    1
    {{ 3 | times: 2 }}
    +
    {{ 3 | times: 2 }}

    Output

    -
    1
    6
    +
    6

    Input

    -
    1
    {{ 24 | times: 7 }}
    +
    {{ 24 | times: 7 }}

    Output

    -
    1
    168
    +
    168

    Input

    -
    1
    {{ 183.357 | times: 12 }}
    +
    {{ 183.357 | times: 12 }}

    Output

    -
    1
    2200.284
    +
    2200.284
    - +
    diff --git a/filters/to_integer.html b/filters/to_integer.html index 4b95af9fa..c185180c9 100644 --- a/filters/to_integer.html +++ b/filters/to_integer.html @@ -96,14 +96,14 @@

    Convert values to number.

    Input

    -
    1
    {{ "123" | to_integer | json }}
    +
    {{ "123" | to_integer | json }}

    Output

    -
    1
    123
    +
    123
    - +
    diff --git a/filters/truncate.html b/filters/truncate.html index 697f0e077..130f5adc3 100644 --- a/filters/truncate.html +++ b/filters/truncate.html @@ -96,29 +96,29 @@

    Shortens a string down to the number of characters passed as an argument. If the specified number of characters is less than the length of the string, an ellipsis (…) is appended to the string and is included in the character count.

    Basic Usage

    Input

    -
    1
    {{ "Ground control to Major Tom." | truncate: 20 }}
    +
    {{ "Ground control to Major Tom." | truncate: 20 }}

    Output

    -
    1
    Ground control to...
    +
    Ground control to...

    Custom ellipsis

    truncate takes an optional second argument that specifies the sequence of characters to be appended to the truncated string. By default this is an ellipsis (…), but you can specify a different sequence.

    The length of the second argument counts against the number of characters specified by the first argument. For example, if you want to truncate a string to exactly 10 characters, and use a 3-character ellipsis, use 13 for the first argument of truncate, since the ellipsis counts as 3 characters.

    Input

    -
    1
    {{ "Ground control to Major Tom." | truncate: 25, ", and so on" }}
    +
    {{ "Ground control to Major Tom." | truncate: 25, ", and so on" }}

    Output

    -
    1
    Ground control, and so on
    +
    Ground control, and so on

    No ellipsis

    You can truncate to the exact number of characters specified by the first argument and avoid showing trailing characters by passing a blank string as the second argument:

    Input

    -
    1
    {{ "Ground control to Major Tom." | truncate: 20, "" }}
    +
    {{ "Ground control to Major Tom." | truncate: 20, "" }}

    Output

    -
    1
    Ground control to Ma
    +
    Ground control to Ma
    - +
    diff --git a/filters/truncatewords.html b/filters/truncatewords.html index 3f3217482..324a0fe4f 100644 --- a/filters/truncatewords.html +++ b/filters/truncatewords.html @@ -96,28 +96,28 @@

    Shortens a string down to the number of words passed as an argument. If the specified number of words is less than the number of words in the string, an ellipsis (…) is appended to the string.

    Input

    -
    1
    {{ "Ground control to Major Tom." | truncatewords: 3 }}
    +
    {{ "Ground control to Major Tom." | truncatewords: 3 }}

    Output

    -
    1
    Ground control to...
    +
    Ground control to...

    Custom ellipsis

    truncatewords takes an optional second argument that specifies the sequence of characters to be appended to the truncated string. By default this is an ellipsis (…), but you can specify a different sequence.

    Input

    -
    1
    {{ "Ground control to Major Tom." | truncatewords: 3, "--" }}
    +
    {{ "Ground control to Major Tom." | truncatewords: 3, "--" }}

    Output

    -
    1
    Ground control to--
    +
    Ground control to--

    No ellipsis

    You can avoid showing trailing characters by passing a blank string as the second argument:

    Input

    -
    1
    {{ "Ground control to Major Tom." | truncatewords: 3, "" }}
    +
    {{ "Ground control to Major Tom." | truncatewords: 3, "" }}

    Output

    -
    1
    Ground control to
    +
    Ground control to
    - +
    diff --git a/filters/uniq.html b/filters/uniq.html index 1f1189761..b1851f290 100644 --- a/filters/uniq.html +++ b/filters/uniq.html @@ -96,14 +96,15 @@

    Removes any duplicate elements in an array.

    Input

    -
    1
    2
    {% assign my_array = "ants, bugs, bees, bugs, ants" | split: ", " %}
    {{ my_array | uniq | join: ", " }}
    +
    {% assign my_array = "ants, bugs, bees, bugs, ants" | split: ", " %}
    +{{ my_array | uniq | join: ", " }}

    Output

    -
    1
    ants, bugs, bees
    +
    ants, bugs, bees
    - +
    diff --git a/filters/unshift.html b/filters/unshift.html index c0b7930a8..b12063fc8 100644 --- a/filters/unshift.html +++ b/filters/unshift.html @@ -96,14 +96,22 @@

    Unshift an element to the front of the array. It’s NON-DESTRUCTIVE, i.e. it does not mutate the array, but rather make a copy and mutate that.

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    {% assign fruits = "oranges, peaches" | split: ", " %}

    {% assign everything = fruits | unshift: "apples" %}

    {% for item in everything %}
    - {{ item }}
    {% endfor %}
    +
    {% assign fruits = "oranges, peaches" | split: ", " %}
    +
    +{% assign everything = fruits | unshift: "apples" %}
    +
    +{% for item in everything %}
    +- {{ item }}
    +{% endfor %}

    Output

    -
    1
    2
    3
    - apples
    - oranges
    - peaches
    +
    - apples
    +- oranges
    +- peaches
    - +
    diff --git a/filters/upcase.html b/filters/upcase.html index a8d24cbaf..a92752598 100644 --- a/filters/upcase.html +++ b/filters/upcase.html @@ -96,20 +96,20 @@

    Makes each character in a string uppercase. It has no effect on strings which are already all uppercase.

    Input

    -
    1
    {{ "Parker Moore" | upcase }}
    +
    {{ "Parker Moore" | upcase }}

    Output

    -
    1
    PARKER MOORE
    +
    PARKER MOORE

    Input

    -
    1
    {{ "APPLE" | upcase }}
    +
    {{ "APPLE" | upcase }}

    Output

    -
    1
    APPLE
    +
    APPLE
    - +
    diff --git a/filters/uri_escape.html b/filters/uri_escape.html index 0c906b80e..1d5361e94 100644 --- a/filters/uri_escape.html +++ b/filters/uri_escape.html @@ -96,15 +96,15 @@

    Percent encodes any special characters in a URI. URI escape normally replaces a space with %20. Reserved characters will not be escaped.

    Input

    -
    1
    {{ "https://example.com/?q=foo, \bar?" | uri_escape }}
    +
    {{ "https://example.com/?q=foo, \bar?" | uri_escape }}

    Output

    -
    1
    https://example.com/?q=foo,%20%5Cbar?
    +
    https://example.com/?q=foo,%20%5Cbar?
    - +
    diff --git a/filters/url_decode.html b/filters/url_decode.html index 8db60bde9..46debd928 100644 --- a/filters/url_decode.html +++ b/filters/url_decode.html @@ -96,14 +96,14 @@

    Decodes a string that has been encoded as a URL.

    Input

    -
    1
    {{ "%27Stop%21%27+said+Fred" | url_decode }}
    +
    {{ "%27Stop%21%27+said+Fred" | url_decode }}

    Output

    -
    1
    'Stop!' said Fred
    +
    'Stop!' said Fred
    - +
    diff --git a/filters/url_encode.html b/filters/url_encode.html index 5d2cfeb10..d48c38f1b 100644 --- a/filters/url_encode.html +++ b/filters/url_encode.html @@ -96,20 +96,20 @@

    Converts any URL-unsafe characters in a string into percent-encoded characters.

    Input

    -
    1
    {{ "john@liquid.com" | url_encode }}
    +
    {{ "john@liquid.com" | url_encode }}

    Output

    -
    1
    john%40liquid.com
    +
    john%40liquid.com

    Input

    -
    1
    {{ "Tetsuro Takara" | url_encode }}
    +
    {{ "Tetsuro Takara" | url_encode }}

    Output

    -
    1
    Tetsuro+Takara
    +
    Tetsuro+Takara
    - +
    diff --git a/filters/where.html b/filters/where.html index 71887731f..a56e2236e 100644 --- a/filters/where.html +++ b/filters/where.html @@ -97,38 +97,83 @@

    Creates an array including only the objects with a given property value, or any truthy value by default.

    In this example, assume you have a list of products and you want to show your kitchen products separately. Using where, you can create an array containing only the products that have a "type" of "kitchen".

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    All products:
    {% for product in products %}
    - {{ product.title }}
    {% endfor %}

    {% assign kitchen_products = products | where: "type", "kitchen" %}

    Kitchen products:
    {% for product in kitchen_products %}
    - {{ product.title }}
    {% endfor %}
    +
    All products:
    +{% for product in products %}
    +- {{ product.title }}
    +{% endfor %}
    +
    +{% assign kitchen_products = products | where: "type", "kitchen" %}
    +
    +Kitchen products:
    +{% for product in kitchen_products %}
    +- {{ product.title }}
    +{% endfor %}

    Output

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    All products:
    - Vacuum
    - Spatula
    - Television
    - Garlic press

    Kitchen products:
    - Spatula
    - Garlic press
    +
    All products:
    +- Vacuum
    +- Spatula
    +- Television
    +- Garlic press
    +
    +Kitchen products:
    +- Spatula
    +- Garlic press

    Say instead you have a list of products and you only want to show those that are available to buy. You can where with a property name but no target value to include all products with a truthy "available" value.

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    All products:
    {% for product in products %}
    - {{ product.title }}
    {% endfor %}

    {% assign available_products = products | where: "available" %}

    Available products:
    {% for product in available_products %}
    - {{ product.title }}
    {% endfor %}
    +
    All products:
    +{% for product in products %}
    +- {{ product.title }}
    +{% endfor %}
    +
    +{% assign available_products = products | where: "available" %}
    +
    +Available products:
    +{% for product in available_products %}
    +- {{ product.title }}
    +{% endfor %}

    Output

    -
    1
    2
    3
    4
    5
    6
    7
    8
    All products:
    - Coffee mug
    - Limited edition sneakers
    - Boring sneakers

    Available products:
    - Coffee mug
    - Boring sneakers
    +
    All products:
    +- Coffee mug
    +- Limited edition sneakers
    +- Boring sneakers
    +
    +Available products:
    +- Coffee mug
    +- Boring sneakers

    The where filter can also be used to find a single object in an array when combined with the first filter. For example, say you want to show off the shirt in your new fall collection.

    Input

    -
    1
    2
    3
    {% assign new_shirt = products | where: "type", "shirt" | first %}

    Featured product: {{ new_shirt.title }}
    +
    {% assign new_shirt = products | where: "type", "shirt" | first %}
    +
    +Featured product: {{ new_shirt.title }}

    Output

    -
    1
    Featured product: Hawaiian print sweater vest
    +
    Featured product: Hawaiian print sweater vest

    Additionally, property can be any valid Liquid variable expression as used in output syntax, except that the scope of this expression is within each item. For the following products array:

    -
    1
    2
    3
    4
    5
    const products = [
    { meta: { details: { class: 'A' } }, order: 1 },
    { meta: { details: { class: 'B' } }, order: 2 },
    { meta: { details: { class: 'B' } }, order: 3 }
    ]
    +
    const products = [
    +    { meta: { details: { class: 'A' } }, order: 1 },
    +    { meta: { details: { class: 'B' } }, order: 2 },
    +    { meta: { details: { class: 'B' } }, order: 3 }
    +]

    Input

    -
    1
    2
    3
    4
    {% assign selected = products | where: 'meta.details["class"]', "B" %}
    {% for item in selected -%}
    - {{ item.order }}
    {% endfor %}
    +
    {% assign selected = products | where: 'meta.details["class"]', "B" %}
    +{% for item in selected -%}
    +- {{ item.order }}
    +{% endfor %}

    Output

    -
    1
    2
    - 2
    - 3
    +
    - 2
    +- 3
    - +
    diff --git a/filters/where_exp.html b/filters/where_exp.html index 1292072e5..ee688f9bb 100644 --- a/filters/where_exp.html +++ b/filters/where_exp.html @@ -96,15 +96,33 @@

    Select all the objects in an array where the expression is true. In this example, assume you have a list of products and you want to show your kitchen products separately. Using where_exp, you can create an array containing only the products that have a "type" of "kitchen".

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    All products:
    {% for product in products %}
    - {{ product.title }}
    {% endfor %}

    {% assign kitchen_products = products | where_exp: "item", "item.type == 'kitchen'" %}

    Kitchen products:
    {% for product in kitchen_products %}
    - {{ product.title }}
    {% endfor %}
    +
    All products:
    +{% for product in products %}
    +- {{ product.title }}
    +{% endfor %}
    +
    +{% assign kitchen_products = products | where_exp: "item", "item.type == 'kitchen'" %}
    +
    +Kitchen products:
    +{% for product in kitchen_products %}
    +- {{ product.title }}
    +{% endfor %}

    Output

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    All products:
    - Vacuum
    - Spatula
    - Television
    - Garlic press

    Kitchen products:
    - Spatula
    - Garlic press
    +
    All products:
    +- Vacuum
    +- Spatula
    +- Television
    +- Garlic press
    +
    +Kitchen products:
    +- Spatula
    +- Garlic press
    - +
    diff --git a/filters/xml_escape.html b/filters/xml_escape.html index 4afe1703e..cf98db074 100644 --- a/filters/xml_escape.html +++ b/filters/xml_escape.html @@ -96,14 +96,14 @@

    Escape some text for use in XML.

    Input

    -
    1
    {{ "Have you read \'James & the Giant Peach\'?" | xml_escape }}
    +
    {{ "Have you read \'James & the Giant Peach\'?" | xml_escape }}

    Output

    -
    1
    Have you read &#39;James &amp; the Giant Peach&#39;?
    +
    Have you read &#39;James &amp; the Giant Peach&#39;?
    - +
    diff --git a/sitemap.xml b/sitemap.xml index d332ba483..ad7a37bb4 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -4,1701 +4,1701 @@ https://liquidjs.com/tutorials/changelog - 2024-08-19T19:37:03.734Z + 2024-08-20T01:26:42.372Z https://liquidjs.com/zh-cn/tutorials/changelog - 2024-08-19T19:37:03.734Z + 2024-08-20T01:26:42.372Z https://liquidjs.com/zh-cn/ - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/manifest.json - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/playground - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/remove_first - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/remove_last - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/replace - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/replace_first - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/replace_last - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/reverse - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/round - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/rstrip - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/shift - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/size - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/slice - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/slugify - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/sort - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/split - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/sort_natural - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/strip - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/strip_html - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/strip_newlines - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/times - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/to_integer - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/truncate - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/truncatewords - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/uniq - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/unshift - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/upcase - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/uri_escape - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/url_decode - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/url_encode - - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/filters/where - - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/filters/where_exp - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/filters/xml_escape - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/assign - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/capture - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/case - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/comment - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/cycle - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/decrement - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/echo - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/for - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/if - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/include - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/increment - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/inline_comment - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/layout - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/liquid - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/overview - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/raw - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/render - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/tablerow - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tags/unless - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/access-scope-in-filters - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/caching - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/contribution-guidelines - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/differences - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/dos - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/drops - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/escaping - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/intro-to-liquid - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/migrate-to-9 - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/operators - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/options - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/parse-parameters - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/partials-and-layouts - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/plugins - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/register-filters-tags - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/render-file - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/render-tag-content - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/setup - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/sync-and-async - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/zh-cn/tutorials/use-in-expressjs - - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/truthy-and-falsy - 2024-08-19T19:36:26.430Z + 2024-08-20T01:26:05.768Z + + + + + https://liquidjs.com/zh-cn/tutorials/use-in-expressjs + + 2024-08-20T01:26:05.768Z https://liquidjs.com/zh-cn/tutorials/whitespace-control - 2024-08-19T19:36:26.430Z - - - - - https://liquidjs.com/ - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/manifest.json - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/playground - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/assign - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/capture - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/case - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/comment - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/cycle - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/decrement - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/echo - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/for - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/if - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/include - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/increment - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/inline_comment - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/layout - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/liquid - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/overview - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/raw - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/render - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/tablerow - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tags/unless - - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/tutorials/access-scope-in-filters - - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.768Z https://liquidjs.com/tutorials/caching - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z + + + + + https://liquidjs.com/tutorials/access-scope-in-filters + + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/contribution-guidelines - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/differences - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/dos - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/drops - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/escaping - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/intro-to-liquid - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/migrate-to-9 - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/operators - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/options - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/parse-parameters - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/partials-and-layouts - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/plugins - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/register-filters-tags - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/render-file - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/render-tag-content - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/setup - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/sync-and-async - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/truthy-and-falsy - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/use-in-expressjs - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/tutorials/whitespace-control - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/abs - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/zh-cn/filters/array_to_sentence_string - - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/append - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z + + + + + https://liquidjs.com/zh-cn/filters/array_to_sentence_string + + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/at_least - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/at_most - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/capitalize - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/ceil - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/cgi_escape - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/zh-cn/filters/compact - - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/concat - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/zh-cn/filters/date + https://liquidjs.com/zh-cn/filters/compact - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/date_to_long_string - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z + + + + + https://liquidjs.com/zh-cn/filters/date + + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/date_to_rfc822 - 2024-08-19T19:36:26.426Z - - - - - https://liquidjs.com/zh-cn/filters/date_to_xmlschema - - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/date_to_string - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z + + + + + https://liquidjs.com/zh-cn/filters/date_to_xmlschema + + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/default - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/divided_by - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/downcase - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/escape - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/escape_once - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/find - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/find_exp - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/first - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/floor - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/group_by - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/group_by_exp - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/inspect - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/join - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/json - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/jsonify - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/last - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/lstrip - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/map - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/minus - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/modulo - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/newline_to_br - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/normalize_whitespace - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/number_of_words - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/overview - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/plus - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/pop - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/prepend - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/push - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/raw - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z https://liquidjs.com/zh-cn/filters/remove - 2024-08-19T19:36:26.426Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/abs + https://liquidjs.com/zh-cn/filters/remove_first - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/append + https://liquidjs.com/zh-cn/filters/remove_last - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/array_to_sentence_string + https://liquidjs.com/zh-cn/filters/replace - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/at_least + https://liquidjs.com/zh-cn/filters/replace_first - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/at_most + https://liquidjs.com/zh-cn/filters/replace_last - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/capitalize + https://liquidjs.com/zh-cn/filters/reverse - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/ceil + https://liquidjs.com/zh-cn/filters/round - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/cgi_escape + https://liquidjs.com/zh-cn/filters/shift - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/compact + https://liquidjs.com/zh-cn/filters/rstrip - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/concat + https://liquidjs.com/zh-cn/filters/size - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/date + https://liquidjs.com/zh-cn/filters/slice - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/date_to_long_string + https://liquidjs.com/zh-cn/filters/slugify - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/date_to_rfc822 + https://liquidjs.com/zh-cn/filters/sort - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/date_to_string + https://liquidjs.com/zh-cn/filters/sort_natural - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/date_to_xmlschema + https://liquidjs.com/zh-cn/filters/split - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/default + https://liquidjs.com/zh-cn/filters/strip_html - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/divided_by + https://liquidjs.com/zh-cn/filters/strip - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/downcase + https://liquidjs.com/zh-cn/filters/strip_newlines - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/escape + https://liquidjs.com/zh-cn/filters/times - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/escape_once + https://liquidjs.com/zh-cn/filters/to_integer - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/find + https://liquidjs.com/zh-cn/filters/truncate - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/find_exp + https://liquidjs.com/zh-cn/filters/truncatewords - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/first + https://liquidjs.com/zh-cn/filters/unshift - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/floor + https://liquidjs.com/zh-cn/filters/upcase - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z - https://liquidjs.com/filters/group_by + https://liquidjs.com/zh-cn/filters/uniq - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.764Z + + + + + https://liquidjs.com/zh-cn/filters/uri_escape + + 2024-08-20T01:26:05.764Z + + + + + https://liquidjs.com/zh-cn/filters/url_decode + + 2024-08-20T01:26:05.764Z + + + + + https://liquidjs.com/zh-cn/filters/url_encode + + 2024-08-20T01:26:05.764Z + + + + + https://liquidjs.com/zh-cn/filters/where + + 2024-08-20T01:26:05.764Z + + + + + https://liquidjs.com/ + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/manifest.json + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/playground + + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/group_by_exp - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/inspect - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/join - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/json - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/jsonify - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/last - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/lstrip - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/map - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/minus - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/modulo - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/newline_to_br - 2024-08-19T19:36:26.422Z - - - - - https://liquidjs.com/filters/number_of_words - - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/normalize_whitespace - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/filters/number_of_words + + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/overview - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/plus - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/pop - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/prepend - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/push - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/raw - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/remove - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/remove_first - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/remove_last - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/replace - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/replace_first - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/replace_last - 2024-08-19T19:36:26.422Z - - - - - https://liquidjs.com/filters/reverse - - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/round - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/filters/reverse + + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/rstrip - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/shift - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/size - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/slice - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/slugify - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/sort - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/sort_natural - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/split - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/strip - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/strip_html - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/strip_newlines - 2024-08-19T19:36:26.422Z - - - - - https://liquidjs.com/filters/sum - - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/times - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/filters/sum + + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/to_integer - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/truncate - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/truncatewords - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/uniq - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/unshift - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/upcase - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/uri_escape - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/url_decode - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/url_encode - 2024-08-19T19:36:26.422Z - - - - - https://liquidjs.com/filters/where - - 2024-08-19T19:36:26.422Z - - - - - https://liquidjs.com/filters/xml_escape - - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z https://liquidjs.com/filters/where_exp - 2024-08-19T19:36:26.422Z + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/filters/where + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/filters/xml_escape + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/assign + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/capture + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/case + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/comment + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/cycle + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/decrement + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/echo + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/for + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/if + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/include + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/increment + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/inline_comment + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/layout + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/liquid + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/overview + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/raw + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/render + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/tablerow + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/tags/unless + + 2024-08-20T01:26:05.760Z + + + + + https://liquidjs.com/filters/abs + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/append + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/array_to_sentence_string + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/at_least + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/at_most + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/capitalize + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/ceil + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/cgi_escape + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/compact + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/concat + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/date + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/date_to_long_string + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/date_to_rfc822 + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/date_to_string + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/date_to_xmlschema + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/default + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/divided_by + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/downcase + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/escape + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/escape_once + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/find + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/find_exp + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/first + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/floor + + 2024-08-20T01:26:05.756Z + + + + + https://liquidjs.com/filters/group_by + + 2024-08-20T01:26:05.756Z diff --git a/tags/assign.html b/tags/assign.html index d9e6a52fb..d16b476ad 100644 --- a/tags/assign.html +++ b/tags/assign.html @@ -96,21 +96,25 @@

    Creates a new variable.

    Input

    -
    1
    2
    3
    4
    {% assign my_variable = false %}
    {% if my_variable != true %}
    This statement is valid.
    {% endif %}
    +
    {% assign my_variable = false %}
    +{% if my_variable != true %}
    +  This statement is valid.
    +{% endif %}

    Output

    -
    1
    This statement is valid.
    +
    This statement is valid.

    Wrap a variable value in quotations " to save it as a string.

    Input

    -
    1
    2
    {% assign foo = "bar" %}
    {{ foo }}
    +
    {% assign foo = "bar" %}
    +{{ foo }}

    Output

    -
    1
    bar
    +
    bar
    - +
    diff --git a/tags/capture.html b/tags/capture.html index 8d0c5b487..a8fd1154a 100644 --- a/tags/capture.html +++ b/tags/capture.html @@ -96,21 +96,29 @@

    Captures the string inside of the opening and closing tags and assigns it to a variable. Variables created through capture are strings.

    Input

    -
    1
    2
    {% capture my_variable %}I am being captured.{% endcapture %}
    {{ my_variable }}
    +
    {% capture my_variable %}I am being captured.{% endcapture %}
    +{{ my_variable }}

    Output

    -
    1
    I am being captured.
    +
    I am being captured.

    Using capture, you can create complex strings using other variables created with assign:

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    8
    {% assign favorite_food = "pizza" %}
    {% assign age = 35 %}

    {% capture about_me %}
    I am {{ age }} and my favorite food is {{ favorite_food }}.
    {% endcapture %}

    {{ about_me }}
    +
    {% assign favorite_food = "pizza" %}
    +{% assign age = 35 %}
    +
    +{% capture about_me %}
    +I am {{ age }} and my favorite food is {{ favorite_food }}.
    +{% endcapture %}
    +
    +{{ about_me }}

    Output

    -
    1
    I am 35 and my favourite food is pizza.
    +
    I am 35 and my favourite food is pizza.
    - +
    diff --git a/tags/case.html b/tags/case.html index d7c32b5da..7f1e8f686 100644 --- a/tags/case.html +++ b/tags/case.html @@ -96,14 +96,22 @@

    Creates a switch statement to compare a variable with different values. case initializes the switch statement, and when compares its values.

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    {% assign handle = "cake" %}
    {% case handle %}
    {% when "cake" %}
    This is a cake
    {% when "cookie", "biscuit" %}
    This is a cookie
    {% else %}
    This is neither a cake nor a cookie
    {% endcase %}
    +
    {% assign handle = "cake" %}
    +{% case handle %}
    +  {% when "cake" %}
    +     This is a cake
    +  {% when "cookie", "biscuit" %}
    +     This is a cookie
    +  {% else %}
    +     This is neither a cake nor a cookie
    +{% endcase %}

    Output

    -
    1
    This is a cake
    +
    This is a cake
    - +
    diff --git a/tags/comment.html b/tags/comment.html index c94ac5b7b..2a836108e 100644 --- a/tags/comment.html +++ b/tags/comment.html @@ -96,14 +96,16 @@

    Allows you to leave un-rendered code inside a Liquid template. Any text within the opening and closing comment blocks will not be printed, and any Liquid code within will not be executed.

    Input

    -
    1
    2
    Anything you put between {% comment %} and {% endcomment %} tags
    is turned into a comment.
    +
    Anything you put between {% comment %} and {% endcomment %} tags
    +is turned into a comment.

    Output

    -
    1
    2
    Anything you put between  tags
    is turned into a comment.
    +
    Anything you put between  tags
    +is turned into a comment.
    - +
    diff --git a/tags/cycle.html b/tags/cycle.html index 8c267f277..4804d2be6 100644 --- a/tags/cycle.html +++ b/tags/cycle.html @@ -96,10 +96,16 @@

    Loops through a group of strings and prints them in the order that they were passed as arguments. Each time cycle is called, the next string argument is printed.

    Basic Usage

    Input

    -
    1
    2
    3
    4
    {% cycle "one", "two", "three" %}
    {% cycle "one", "two", "three" %}
    {% cycle "one", "two", "three" %}
    {% cycle "one", "two", "three" %}
    +
    {% cycle "one", "two", "three" %}
    +{% cycle "one", "two", "three" %}
    +{% cycle "one", "two", "three" %}
    +{% cycle "one", "two", "three" %}

    Output

    -
    1
    2
    3
    4
    one
    two
    three
    one
    +
    one
    +two
    +three
    +one

    Uses for cycle include:

    Parameters

    cycle accepts a “cycle group” parameter in cases where you need multiple cycle blocks in one template. If no name is supplied for the cycle group, then it is assumed that multiple calls with the same parameters are one group.

    Input

    -
    1
    2
    3
    4
    {% cycle "first": "one", "two", "three" %}
    {% cycle "second": "one", "two", "three" %}
    {% cycle "second": "one", "two", "three" %}
    {% cycle "first": "one", "two", "three" %}
    +
    {% cycle "first": "one", "two", "three" %}
    +{% cycle "second": "one", "two", "three" %}
    +{% cycle "second": "one", "two", "three" %}
    +{% cycle "first": "one", "two", "three" %}

    Output

    -
    1
    2
    3
    4
    one
    one
    two
    two
    +
    one
    +one
    +two
    +two
    - +
    diff --git a/tags/decrement.html b/tags/decrement.html index 12cfe1f91..9f34c731b 100644 --- a/tags/decrement.html +++ b/tags/decrement.html @@ -96,16 +96,20 @@

    Creates a new number variable, and decreases its value by one every time it is called. The first value is -1.

    Input

    -
    1
    2
    3
    {% decrement variable %}
    {% decrement variable %}
    {% decrement variable %}
    +
    {% decrement variable %}
    +{% decrement variable %}
    +{% decrement variable %}

    Output

    -
    1
    2
    3
    -1
    -2
    -3
    +
    -1
    +-2
    +-3

    Like increment, variables declared inside decrement are independent from variables created through assign or capture.

    - +
    diff --git a/tags/echo.html b/tags/echo.html index 74a48fe4f..f0321e7dd 100644 --- a/tags/echo.html +++ b/tags/echo.html @@ -96,14 +96,15 @@

    Outputs an expression in the rendered HTML. This is identical to wrapping an expression in {{` and `}}, but works inside liquid tags and supports filters.

    echo

    Input

    -
    1
    2
    {% assign username = 'Bob' %}
    {% echo username | append: ", welcome to LiquidJS!" | capitalize %}
    +
    {% assign username = 'Bob' %}
    +{% echo username | append: ", welcome to LiquidJS!" | capitalize %}

    Output

    -
    1
    Bob, welcome to LiquidJS!
    +
    Bob, welcome to LiquidJS!
    - +
    diff --git a/tags/for.html b/tags/for.html index 4c822e7be..f7cc5ea6a 100644 --- a/tags/for.html +++ b/tags/for.html @@ -97,115 +97,188 @@

    Iteration tags run blocks of code repeatedly.

    Basic Usage

    for…in

    Repeatedly executes a block of code. For a full list of attributes available within a for loop, see forloop.

    Input

    -
    1
    2
    3
    {% for product in collection.products %}
    {{ product.title }}
    {% endfor %}
    +
    {% for product in collection.products %}
    +  {{ product.title }}
    +{% endfor %}

    Output

    -
    1
    hat shirt pants
    +
    hat shirt pants

    For loops can iterate over arrays, hashes, and ranges of integers.

    When iterating a hash, item[0] contains the key, and item[1] contains the value:

    Input

    -
    1
    2
    3
    {% for item in hash %}
    * {{ item[0] }}: {{ item[1] }}
    {% endfor %}
    +
    {% for item in hash %}
    +  * {{ item[0] }}: {{ item[1] }}
    +{% endfor %}

    Output

    -
    1
    2
    * key1: value1
    * key2: value2
    +
    * key1: value1
    +* key2: value2

    else

    Specifies a fallback case for a for loop which will run if the loop has zero length.

    Input

    -
    1
    2
    3
    4
    5
    {% for product in collection.products %}
    {{ product.title }}
    {% else %}
    The collection is empty.
    {% endfor %}
    +
    {% for product in collection.products %}
    +  {{ product.title }}
    +{% else %}
    +  The collection is empty.
    +{% endfor %}

    Output

    -
    1
    The collection is empty.
    +
    The collection is empty.

    break

    Causes the loop to stop iterating when it encounters the break tag.

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    {% for i in (1..5) %}
    {%- if i == 4 -%}
    {% break %}
    {%- else -%}
    {{ i }}
    {%- endif -%}
    {% endfor %}
    +
    {% for i in (1..5) %}
    +  {%- if i == 4 -%}
    +    {% break %}
    +  {%- else -%}
    +    {{ i }}
    +  {%- endif -%}
    +{% endfor %}

    Output

    -
    1
    123
    +
    123

    continue

    Causes the loop to skip the current iteration when it encounters the continue tag.

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    {% for i in (1..5) %}
    {%- if i == 4 -%}
    {%- continue -%}
    {%- else -%}
    {{ i }}
    {%- endif -%}
    {% endfor %}
    +
    {% for i in (1..5) %}
    +  {%- if i == 4 -%}
    +    {%- continue -%}
    +  {%- else -%}
    +    {{ i }}
    +  {%- endif -%}
    +{% endfor %}

    Output

    -
    1
    1235
    +
    1235

    forloop

    There’s a forloop object available inside for loops. It’s used to indicate the current state of for loop.

    The forloop.first, forloop.last and forloop.length property:

    Input

    -
    1
    2
    3
    4
    5
    6
    {% for i in (1..5) %}
    {%- if forloop.first == true -%} First
    {%- elsif forloop.last == true -%} Last
    {%- else -%} {{ forloop.length }}
    {%- endif %}
    {% endfor -%}
    +
    {% for i in (1..5) %}
    +  {%- if forloop.first == true -%} First
    +  {%- elsif forloop.last == true -%} Last
    +  {%- else -%} {{ forloop.length }}
    +  {%- endif %}
    +{% endfor -%}

    Output

    -
    1
    2
    3
    4
    5
    First
    5
    5
    5
    Last
    +
    First
    +5
    +5
    +5
    +Last

    The forloop.index, forloop.index0, forloop.rindex and forloop.rindex0 property:

    Input

    -
    1
    2
    3
    4
    index index0 rindex rindex0
    {% for i in (1..5) %}
    {{- forloop.index }} {{ forloop.index0 }} {{ forloop.rindex }} {{ forloop.rindex0 }}
    {% endfor -%}
    +
    index index0 rindex rindex0
    +{% for i in (1..5) %}
    +  {{- forloop.index }}     {{ forloop.index0 }}      {{ forloop.rindex }}      {{ forloop.rindex0 }}
    +{% endfor -%}

    Output

    -
    1
    2
    3
    4
    5
    6
    index index0 rindex rindex0
    1 0 5 4
    2 1 4 3
    3 2 3 2
    4 3 2 1
    5 4 1 0
    +
    index index0 rindex rindex0
    +1     0      5      4
    +2     1      4      3
    +3     2      3      2
    +4     3      2      1
    +5     4      1      0

    Parameters

    limit

    Limits the loop to the specified number of iterations.

    Input

    -
    1
    2
    3
    4
    <!-- for array = [1,2,3,4,5,6] -->
    {% for item in array limit:2 %}
    {{- item -}}
    {% endfor %}
    +
    <!-- for array = [1,2,3,4,5,6] -->
    +{% for item in array limit:2 %}
    +  {{- item -}}
    +{% endfor %}

    Output

    -
    1
    12
    +
    12

    offset

    Begins the loop at the specified index.

    Input

    -
    1
    2
    3
    4
    <!-- for array = [1,2,3,4,5,6] -->
    {% for item in array offset:2 %}
    {{- item -}}
    {% endfor %}
    +
    <!-- for array = [1,2,3,4,5,6] -->
    +{% for item in array offset:2 %}
    +  {{- item -}}
    +{% endfor %}

    Output

    -
    1
    3456
    +
    3456

    offset:continue

    v9.33.0

    Offset value can be continue to continue previous loop. For example:

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    <!-- for array = [1,2,3,4,5,6] -->
    {% for item in array limit:2 %}
    {{- item -}}
    {% endfor%}
    {% for item in array offset:continue %}
    {{- item -}}
    {% endfor%}
    +
    <!-- for array = [1,2,3,4,5,6] -->
    +{% for item in array limit:2 %}
    +  {{- item -}}
    +{% endfor%}
    +{% for item in array offset:continue %}
    +  {{- item -}}
    +{% endfor%}

    Output

    -
    1
    2
    12
    3456
    +
    12
    +3456

    For the same variable name ("item" in this case) and same collection ("array" in this case), there’s one position record. That means you can start a new loop with a different variable name:

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    <!-- for array = [1,2,3,4,5,6] -->
    {% for item in array limit:2 %}
    {{- item -}}
    {% endfor%}
    {% for item2 in array offset:continue %}
    {{- item2 -}}
    {% endfor%}
    +
    <!-- for array = [1,2,3,4,5,6] -->
    +{% for item in array limit:2 %}
    +  {{- item -}}
    +{% endfor%}
    +{% for item2 in array offset:continue %}
    +  {{- item2 -}}
    +{% endfor%}

    Output

    -
    1
    2
    12
    123456
    +
    12
    +123456

    range

    Defines a range of numbers to loop through. The range can be defined by both literal and variable numbers.

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    8
    {% for i in (3..5) %}
    {{- i -}}
    {% endfor-%}

    {% assign num = 4 %}
    {% for i in (1..num) %}
    {{- i -}}
    {% endfor %}
    +
    {% for i in (3..5) %}
    +  {{- i -}}
    +{% endfor-%}
    +
    +{% assign num = 4 %}
    +{% for i in (1..num) %}
    +  {{- i -}}
    +{% endfor %}

    Output

    -
    1
    2
    345
    1234
    +
    345
    +1234

    reversed

    Reverses the order of the loop. Note that this flag’s spelling is different from the filter reverse.

    Input

    -
    1
    2
    3
    4
    <!-- if array = [1,2,3,4,5,6] -->
    {% for item in array reversed %}
    {{ item }}
    {% endfor %}
    +
    <!-- if array = [1,2,3,4,5,6] -->
    +{% for item in array reversed %}
    +  {{ item }}
    +{% endfor %}

    Output

    -
    1
    6 5 4 3 2 1
    +
    6 5 4 3 2 1

    When used with additional parameters, order is important. Leading with reversed reverses the order of the loop before executing the other parameters.

    Input

    -
    1
    2
    3
    {% for i in (1..8) reversed limit: 4 %}
    {{ i }}
    {% endfor %}
    +
    {% for i in (1..8) reversed limit: 4 %}
    +  {{ i }}
    +{% endfor %}

    Output

    -
    1
    8 7 6 5
    +
    8 7 6 5

    Input

    -
    1
    2
    3
    {% for i in (1..8) limit: 4 reversed %}
    {{ i }}
    {% endfor %}
    +
    {% for i in (1..8) limit: 4 reversed %}
    +  {{ i }}
    +{% endfor %}

    Output

    -
    1
    4 3 2 1
    +
    4 3 2 1
    - +
    diff --git a/tags/if.html b/tags/if.html index 1e070f742..3863f16a8 100644 --- a/tags/if.html +++ b/tags/if.html @@ -96,21 +96,30 @@

    Executes a block of code only if a certain condition is true.

    if

    Input

    -
    1
    2
    3
    {% if product.title == "Awesome Shoes" %}
    These shoes are awesome!
    {% endif %}
    +
    {% if product.title == "Awesome Shoes" %}
    +  These shoes are awesome!
    +{% endif %}

    Output

    -
    1
    These shoes are awesome!
    +
    These shoes are awesome!

    elsif / else

    Adds more conditions within an if or unless block.

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    8
    <!-- If customer.name = "anonymous" -->
    {% if customer.name == "kevin" %}
    Hey Kevin!
    {% elsif customer.name == "anonymous" %}
    Hey Anonymous!
    {% else %}
    Hi Stranger!
    {% endif %}
    +
    <!-- If customer.name = "anonymous" -->
    +{% if customer.name == "kevin" %}
    +  Hey Kevin!
    +{% elsif customer.name == "anonymous" %}
    +  Hey Anonymous!
    +{% else %}
    +  Hi Stranger!
    +{% endif %}

    Output

    -
    1
    Hey Anonymous!
    +
    Hey Anonymous!
    - +
    diff --git a/tags/include.html b/tags/include.html index 134023c5a..ca0eaea1b 100644 --- a/tags/include.html +++ b/tags/include.html @@ -98,30 +98,32 @@

    Include a Template

    Renders a partial template from the template roots.

    -
    1
    {% include 'footer.liquid' %}
    +
    {% include 'footer.liquid' %}

    If extname option is set, the above .liquid extension becomes optional:

    -
    1
    {% include 'footer' %}
    +
    {% include 'footer' %}

    When a partial template is rendered by include, the code inside it can access its parent’s variables but its parent cannot access variables defined inside a included template.

    Passing Variables

    Variables defined in parent’s scope can be passed to a the partial template by listing them as parameters on the include tag:

    -
    1
    2
    {% assign my_variable = 'apples' %}
    {% include 'name', my_variable: my_variable, my_other_variable: 'oranges' %}
    +
    {% assign my_variable = 'apples' %}
    +{% include 'name', my_variable: my_variable, my_other_variable: 'oranges' %}

    The with Parameter

    A single object can be passed to a snippet by using the with...as syntax:

    -
    1
    2
    {% assign featured_product = all_products['product_handle'] %}
    {% include 'product' with featured_product as product %}
    +
    {% assign featured_product = all_products['product_handle'] %}
    +{% include 'product' with featured_product as product %}

    In the example above, the product variable in the partial template will hold the value of featured_product in the parent template.

    Outputs & Filters

    When filename is specified as literal string, it supports Liquid output and filter syntax. Useful when concatenating strings for a complex filename.

    -
    1
    {% include "prefix/{{name | append: \".html\"}}" %}
    +
    {% include "prefix/{{name | append: \".html\"}}" %}
    Escaping

    In LiquidJS, " within quoted string literals need to be escaped by adding a slash before the quote, e.g. \". Using Jekyll-like filenames can make this easier, see below.

    Jekyll-like filenames

    Setting dynamicPartials to false will enable Jekyll-like filenames, where file names are specified as literal string without surrounding quotes. Liquid outputs and filters are also supported within that, for example:

    -
    1
    {% include prefix/{{ page.my_variable }}/suffix %}
    +
    {% include prefix/{{ page.my_variable }}/suffix %}

    This way, you don’t need to escape " in the filename expression.

    -
    1
    {% include prefix/{{name | append: ".html"}} %}
    +
    {% include prefix/{{name | append: ".html"}} %}

    Jekyll include

    v9.33.0

    @@ -132,18 +134,24 @@
  • Parameters are under include variable instead of current scope.
  • For example, the following template:

    -
    1
    {% include article.html header="HEADER" content="CONTENT" %}
    +
    {% include article.html header="HEADER" content="CONTENT" %}

    article.html with following content:

    -
    1
    2
    3
    4
    <article>
    <header>{{include.header}}</header>
    {{include.content}}
    </article>
    +
    <article>
    +  <header>{{include.header}}</header>
    +  {{include.content}}
    +</article>

    Note that we’re referencing the first parameter by include.header instead of header. Will output following:

    -
    1
    2
    3
    4
    <article>
    <header>HEADER</header>
    CONTENT
    </article>
    +
    <article>
    +  <header>HEADER</header>
    +  CONTENT
    +</article>
    - +
    diff --git a/tags/increment.html b/tags/increment.html index ac00880fc..076c74d69 100644 --- a/tags/increment.html +++ b/tags/increment.html @@ -96,23 +96,34 @@

    Creates a new number variable, and increases its value by one every time it is called. The first value is 0.

    Input

    -
    1
    2
    3
    {% increment my_counter %}
    {% increment my_counter %}
    {% increment my_counter %}
    +
    {% increment my_counter %}
    +{% increment my_counter %}
    +{% increment my_counter %}

    Output

    -
    1
    2
    3
    0
    1
    2
    +
    0
    +1
    +2

    Variables created through the increment tag are independent from variables created through assign or capture.

    In the example below, a variable named “var” is created through assign. The increment tag is then used several times on a variable with the same name. Note that the increment tag does not affect the value of “var” that was created through assign.

    Input

    -
    1
    2
    3
    4
    5
    {% assign var = 10 %}
    {% increment var %}
    {% increment var %}
    {% increment var %}
    {{ var }}
    +
    {% assign var = 10 %}
    +{% increment var %}
    +{% increment var %}
    +{% increment var %}
    +{{ var }}

    Output

    -
    1
    2
    3
    4
    0
    1
    2
    10
    +
    0
    +1
    +2
    +10
    - +
    diff --git a/tags/inline_comment.html b/tags/inline_comment.html index f7b0c260d..b8d9e314a 100644 --- a/tags/inline_comment.html +++ b/tags/inline_comment.html @@ -96,24 +96,39 @@

    Add comments to a Liquid template using an inline tag. Text enclosed in an inline comment tag will not be printed.

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    Anything inside an inline comment tag will not be printed.
    {% # this is an inline comment %}
    But every line must start with a '#'.
    {%
    # this is a comment
    # that spans multiple lines
    %}
    +
    Anything inside an inline comment tag will not be printed.
    +{% # this is an inline comment %}
    +But every line must start with a '#'.
    +{%
    +  # this is a comment
    +  # that spans multiple lines
    +%}

    Output

    -
    1
    2
    Anything inside an inline comment tag will not be printed.
    But every line must start with a '#'.
    +
    Anything inside an inline comment tag will not be printed.
    +But every line must start with a '#'.

    Inline comments are useful inside liquid tags too.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    {% liquid
    # required args
    assign product = collection.products.first

    # optional args
    assign should_show_border = should_show_border | default: true
    assign should_highlight = should_highlight | default: false
    %}
    +
    {% liquid
    +  # required args
    +  assign product = collection.products.first
    +
    +  # optional args
    +  assign should_show_border = should_show_border | default: true
    +  assign should_highlight = should_highlight | default: false
    +%}

    But they don’t work well for commenting out blocks of Liquid code. The comment block tag is the better option when you need to temporarily stop other tags from being executed.

    Input

    -
    1
    2
    {%- # {% echo 'Welcome to LiquidJS!' %} -%}
    {% comment %}{% echo 'Welcome to LiquidJS!' %}{% endcomment %}
    +
    {%- # {% echo 'Welcome to LiquidJS!' %} -%}
    +{% comment %}{% echo 'Welcome to LiquidJS!' %}{% endcomment %}

    Output

    -
    1
    -%}
    +
    -%}
    - +
    diff --git a/tags/layout.html b/tags/layout.html index 804b92466..3e51a68a7 100644 --- a/tags/layout.html +++ b/tags/layout.html @@ -95,39 +95,68 @@

    v1.9.1

    Using a Layout

    Introduce a layout template for the current template to render in. The directory for layout files are defined by layouts or root.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // default-layout.liquid
    Header
    {% block %}{% endblock %}
    Footer

    // page.liquid
    {% layout "default-layout.liquid" %}
    {% block %}My page content{% endblock %}

    // result
    Header
    My page content
    Footer
    +
    // default-layout.liquid
    +Header
    +{% block %}{% endblock %}
    +Footer
    +
    +// page.liquid
    +{% layout "default-layout.liquid" %}
    +{% block %}My page content{% endblock %}
    +
    +// result
    +Header
    +My page content
    +Footer

    If extname option is set, the .liquid extension becomes optional:

    -
    1
    {% layout 'default-layout' %}
    +
    {% layout 'default-layout' %}
    Scoping

    When a partial template is rendered by layout, its template have access for its caller’s variables but not vice versa. Variables defined in layout will be popped out before control returning to its caller.

    Multiple Blocks

    The layout file can contain multiple blocks, each with a specified name. The following snippets yield same result as in the above example.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // default-layout.liquid
    {% block header %}{% endblock %}
    {% block content %}{% endblock %}
    {% block footer %}{% endblock %}

    // page.liquid
    {% layout "default-layout.liquid" %}
    {% block header %}Header{% endblock %}
    {% block content %}My page content{% endblock %}
    {% block footer %}Footer{% endblock %}
    +
    // default-layout.liquid
    +{% block header %}{% endblock %}
    +{% block content %}{% endblock %}
    +{% block footer %}{% endblock %}
    +
    +// page.liquid
    +{% layout "default-layout.liquid" %}
    +{% block header %}Header{% endblock %}
    +{% block content %}My page content{% endblock %}
    +{% block footer %}Footer{% endblock %}

    Default Block Contents

    In the above layout files, blocks has empty contents. But it’s not necessarily be empty, in which case, the block contents in layout files will be used as default templates. The following snippets are also equivalent to the above examples:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    // default-layout.liquid
    {% block header %}Header{% endblock %}
    {% block content %}{% endblock %}
    {% block footer %}Footer{% endblock %}

    // page.liquid
    {% layout "default-layout.liquid" %}
    {% block content %}My page content{% endblock %}
    +
    // default-layout.liquid
    +{% block header %}Header{% endblock %}
    +{% block content %}{% endblock %}
    +{% block footer %}Footer{% endblock %}
    +
    +// page.liquid
    +{% layout "default-layout.liquid" %}
    +{% block content %}My page content{% endblock %}

    Passing Variables

    Variables defined in current template can be passed to a the layout template by listing them as parameters on the layout tag:

    -
    1
    2
    {% assign my_variable = 'apples' %}
    {% layout 'name', my_variable: my_variable, my_other_variable: 'oranges' %}
    +
    {% assign my_variable = 'apples' %}
    +{% layout 'name', my_variable: my_variable, my_other_variable: 'oranges' %}

    Outputs & Filters

    When filename is specified as literal string, it supports Liquid output and filter syntax. Useful when concatenating strings for a complex filename.

    -
    1
    {% layout "prefix/{{name | append: \".html\"}}" %}
    +
    {% layout "prefix/{{name | append: \".html\"}}" %}
    Escaping

    In LiquidJS, " within quoted string literals need to be escaped. Adding a slash before the quote, e.g. \". Using Jekyll-like filenames can make this easier, see below.

    Jekyll-like Filenames

    Setting dynamicPartials to false will enable Jekyll-like filenames, file names are specified as literal string. And it also supports Liquid outputs and filters.

    -
    1
    {% layout prefix/{{ page.my_variable }}/suffix %}
    +
    {% layout prefix/{{ page.my_variable }}/suffix %}

    This way, you don’t need to escape " in the filename expression.

    -
    1
    {% layout prefix/{{name | append: ".html"}} %}
    +
    {% layout prefix/{{name | append: ".html"}} %}
    - +
    diff --git a/tags/liquid.html b/tags/liquid.html index c8871a84e..3dc519751 100644 --- a/tags/liquid.html +++ b/tags/liquid.html @@ -96,14 +96,23 @@

    Encloses multiple tags within one set of delimiters, to allow writing Liquid logic more concisely.

    liquid

    Input

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {% liquid
    assign names = 'Bob, Sally' | split: ', '

    for name in names
    echo 'Hello, ' | append: name
    unless forloop.last
    echo ', '
    endunless
    endfor
    %}
    +
    {% liquid
    +  assign names = 'Bob, Sally' | split: ', '
    +
    +  for name in names
    +    echo 'Hello, ' | append: name
    +    unless forloop.last
    +      echo ', '
    +    endunless
    +  endfor
    +%}

    Output

    -
    1
    Hello, Bob, Hello Sally
    +
    Hello, Bob, Hello Sally
    - +
    diff --git a/tags/overview.html b/tags/overview.html index 5e3ab28ed..b7be48c35 100644 --- a/tags/overview.html +++ b/tags/overview.html @@ -131,7 +131,7 @@
    - +
    diff --git a/tags/raw.html b/tags/raw.html index 114d0eeac..eebd62024 100644 --- a/tags/raw.html +++ b/tags/raw.html @@ -96,14 +96,17 @@

    Raw temporarily disables tag processing. This is useful for generating content
    (eg, Mustache, Handlebars) which uses conflicting syntax.

    Input

    -
    1
    2
    3
    4
    {% raw %}
    In Handlebars, {{ this }} will be HTML-escaped, but
    {{{ that }}} will not.
    {% endraw %}
    +
    {% raw %}
    +  In Handlebars, {{ this }} will be HTML-escaped, but
    +  {{{ that }}} will not.
    +{% endraw %}

    Output

    -
    1
    In Handlebars, {{ this }} will be HTML-escaped, but {{{ that }}} will not.
    +
    In Handlebars, {{ this }} will be HTML-escaped, but {{{ that }}} will not.
    - +
    diff --git a/tags/render.html b/tags/render.html index 21f450651..95196144f 100644 --- a/tags/render.html +++ b/tags/render.html @@ -95,36 +95,48 @@

    v9.2.0

    Render a Template

    Render a partial template from partials directory specified by partials or root.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // index.liquid
    Contents
    {% render 'footer.liquid' %}

    // footer.liquid
    Footer

    // result
    Contents
    Footer
    +
    // index.liquid
    +Contents
    +{% render 'footer.liquid' %}
    +
    +// footer.liquid
    +Footer
    +
    +// result
    +Contents
    +Footer

    If extname option is set, the above .liquid extension becomes optional:

    -
    1
    {% render 'footer' %}
    +
    {% render 'footer' %}
    Variable Scope

    When a partial template is rendered, the code inside it can’t access its parent’s variables and its variables won’t be accessible by its parent. This encapsulation makes partials easier to understand and maintain.

    Passing Variables

    Variables defined in parent’s scope can be passed to a the partial template by listing them as parameters on the render tag:

    -
    1
    2
    {% assign my_variable = 'apples' %}
    {% render 'name', my_variable: my_variable, my_other_variable: 'oranges' %}
    +
    {% assign my_variable = 'apples' %}
    +{% render 'name', my_variable: my_variable, my_other_variable: 'oranges' %}

    globals don’t need to be passed down. They are accessible from all files.

    Outputs & Filters

    When filename is specified as literal string, it supports Liquid output and filter syntax. Useful when concatenating strings for a complex filename.

    -
    1
    {% render "prefix/{{name | append: \".html\"}}" %}
    +
    {% render "prefix/{{name | append: \".html\"}}" %}
    Escaping

    In LiquidJS, " within quoted string literals need to be escaped. Adding a slash before the quote, e.g. \". Using Jekyll-like filenames can make this easier, see below.

    Jekyll-like Filenames

    Setting dynamicPartials to false will enable Jekyll-like filenames, file names are specified as literal string. And it also supports Liquid outputs and filters.

    -
    1
    {% render prefix/{{ page.my_variable }}/suffix %}
    +
    {% render prefix/{{ page.my_variable }}/suffix %}

    This way, you don’t need to escape " in the filename expression.

    -
    1
    {% render prefix/{{name | append: ".html"}} %}
    +
    {% render prefix/{{name | append: ".html"}} %}

    Parameters

    The with Parameter

    A single object can be passed to a snippet by using the with...as syntax:

    -
    1
    2
    {% assign featured_product = all_products['product_handle'] %}
    {% render 'product' with featured_product as product %}
    +
    {% assign featured_product = all_products['product_handle'] %}
    +{% render 'product' with featured_product as product %}

    In the example above, the product variable in the partial template will hold the value of featured_product in the parent template.

    The for Parameter

    A partial template can be rendered once for each value of an enumerable by using the for...as syntax:

    -
    1
    2
    {% assign variants = product.variants %}
    {% render 'variant' for variants as variant %}
    +
    {% assign variants = product.variants %}
    +{% render 'variant' for variants as variant %}

    In the example above, the partial template will be rendered once for each variant of the product, and the variant variable will hold a product’s variant object within the snippet.

    The forloop object

    When using the for parameter, the forloop object is accessible within the snippet.

    @@ -133,7 +145,7 @@
    - +
    diff --git a/tags/tablerow.html b/tags/tablerow.html index 1a66d14fb..a79b767c2 100644 --- a/tags/tablerow.html +++ b/tags/tablerow.html @@ -96,30 +96,101 @@

    Generates an HTML table. Must be wrapped in opening <table> and closing </table> HTML tags.

    Basic Usage

    Input

    -
    1
    2
    3
    4
    5
    <table>
    {% tablerow product in collection.products %}
    {{ product.title }}
    {% endtablerow %}
    </table>
    +
    <table>
    +{% tablerow product in collection.products %}
    +  {{ product.title }}
    +{% endtablerow %}
    +</table>

    Output

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <table>
    <tr class="row1">
    <td class="col1">
    Cool Shirt
    </td>
    <td class="col2">
    Alien Poster
    </td>
    <td class="col3">
    Batman Poster
    </td>
    <td class="col4">
    Bullseye Shirt
    </td>
    <td class="col5">
    Another Classic Vinyl
    </td>
    <td class="col6">
    Awesome Jeans
    </td>
    </tr>
    </table>
    +
    <table>
    +  <tr class="row1">
    +    <td class="col1">
    +      Cool Shirt
    +    </td>
    +    <td class="col2">
    +      Alien Poster
    +    </td>
    +    <td class="col3">
    +      Batman Poster
    +    </td>
    +    <td class="col4">
    +      Bullseye Shirt
    +    </td>
    +    <td class="col5">
    +      Another Classic Vinyl
    +    </td>
    +    <td class="col6">
    +      Awesome Jeans
    +    </td>
    +  </tr>
    +</table>

    Parameters

    cols

    Defines how many columns the tables should have.

    Input

    -
    1
    2
    3
    {% tablerow product in collection.products cols:2 %}
    {{ product.title }}
    {% endtablerow %}
    +
    {% tablerow product in collection.products cols:2 %}
    +  {{ product.title }}
    +{% endtablerow %}

    Output

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <table>
    <tr class="row1">
    <td class="col1">
    Cool Shirt
    </td>
    <td class="col2">
    Alien Poster
    </td>
    </tr>
    <tr class="row2">
    <td class="col1">
    Batman Poster
    </td>
    <td class="col2">
    Bullseye Shirt
    </td>
    </tr>
    <tr class="row3">
    <td class="col1">
    Another Classic Vinyl
    </td>
    <td class="col2">
    Awesome Jeans
    </td>
    </tr>
    </table>
    +
    <table>
    +  <tr class="row1">
    +    <td class="col1">
    +      Cool Shirt
    +    </td>
    +    <td class="col2">
    +      Alien Poster
    +    </td>
    +  </tr>
    +  <tr class="row2">
    +    <td class="col1">
    +      Batman Poster
    +    </td>
    +    <td class="col2">
    +      Bullseye Shirt
    +    </td>
    +  </tr>
    +  <tr class="row3">
    +    <td class="col1">
    +      Another Classic Vinyl
    +    </td>
    +    <td class="col2">
    +      Awesome Jeans
    +    </td>
    +  </tr>
    +</table>

    limit

    Exits the tablerow after a specific index.

    -
    1
    2
    3
    {% tablerow product in collection.products cols:2 limit:3 %}
    {{ product.title }}
    {% endtablerow %}
    +
    {% tablerow product in collection.products cols:2 limit:3 %}
    +  {{ product.title }}
    +{% endtablerow %}

    offset

    Starts the tablerow after a specific index.

    -
    1
    2
    3
    {% tablerow product in collection.products cols:2 offset:3 %}
    {{ product.title }}
    {% endtablerow %}
    +
    {% tablerow product in collection.products cols:2 offset:3 %}
    +  {{ product.title }}
    +{% endtablerow %}

    range

    Defines a range of numbers to loop through. The range can be defined by both literal and variable numbers.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!--variable number example-->

    {% assign num = 4 %}
    <table>
    {% tablerow i in (1..num) %}
    {{ i }}
    {% endtablerow %}
    </table>

    <!--literal number example-->

    <table>
    {% tablerow i in (3..5) %}
    {{ i }}
    {% endtablerow %}
    </table>
    +
    <!--variable number example-->
    +
    +{% assign num = 4 %}
    +<table>
    +{% tablerow i in (1..num) %}
    +  {{ i }}
    +{% endtablerow %}
    +</table>
    +
    +<!--literal number example-->
    +
    +<table>
    +{% tablerow i in (3..5) %}
    +  {{ i }}
    +{% endtablerow %}
    +</table>
    - +
    diff --git a/tags/unless.html b/tags/unless.html index 4dbb0e55d..83e2a68d6 100644 --- a/tags/unless.html +++ b/tags/unless.html @@ -96,17 +96,21 @@

    The opposite of if – executes a block of code only if a certain condition is not met.

    Input

    -
    1
    2
    3
    {% unless product.title == "Awesome Shoes" %}
    These shoes are not awesome.
    {% endunless %}
    +
    {% unless product.title == "Awesome Shoes" %}
    +  These shoes are not awesome.
    +{% endunless %}

    Output

    -
    1
    These shoes are not awesome.
    +
    These shoes are not awesome.

    This would be the equivalent of doing the following:

    -
    1
    2
    3
    {% if product.title != "Awesome Shoes" %}
    These shoes are not awesome.
    {% endif %}
    +
    {% if product.title != "Awesome Shoes" %}
    +  These shoes are not awesome.
    +{% endif %}
    - +
    diff --git a/tutorials/access-scope-in-filters.html b/tutorials/access-scope-in-filters.html index fe513bea0..ac61bbacc 100644 --- a/tutorials/access-scope-in-filters.html +++ b/tutorials/access-scope-in-filters.html @@ -93,10 +93,19 @@

    As covered in Register Filters/Tags, we can access filter arguments directly in filter function like:

    -
    1
    2
    3
    // Usage: {{ 1 | add: 2, 3 }}
    // Output: 6
    engine.registerFilter('add', (initial, arg1, arg2) => initial + arg1 + arg2)
    +
    // Usage: {{ 1 | add: 2, 3 }}
    +// Output: 6
    +engine.registerFilter('add', (initial, arg1, arg2) => initial + arg1 + arg2)

    When it comes to stateful filters, for example transform a URL path to full URL, we’ll need to access a origin in current scope:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    // Usage: {{ '/index.html' | fullURL }}
    // Scope: { origin: "https://liquidjs.com" }
    // Output: https://liquidjs.com/index.html

    engine.registerFilter('fullURL', function (path) {
    const origin = this.context.get(['origin'])
    return new URL(path, origin).toString()
    })
    +
    // Usage: {{ '/index.html' | fullURL }}
    +// Scope: { origin: "https://liquidjs.com" }
    +// Output: https://liquidjs.com/index.html
    +
    +engine.registerFilter('fullURL', function (path) {
    +    const origin = this.context.get(['origin'])
    +    return new URL(path, origin).toString() 
    +})

    See this JSFiddle: https://jsfiddle.net/ctj364up/1/

    Arrow Functions

    this in arrow functions is bound to current JavaScript context, you’ll need to use function(){} instead of ()=>{} syntax to access this.context correctly.

    @@ -105,7 +114,7 @@
    - +
    diff --git a/tutorials/caching.html b/tutorials/caching.html index 2aa91c048..5b3cbeaff 100644 --- a/tutorials/caching.html +++ b/tutorials/caching.html @@ -96,19 +96,34 @@

    LiquidJS provides multiple ways to cache the parsed templates to improve performance.

    Programmatically

    The .parse(), .parseFile(), .parseFileSync() APIs are used to parse templates from string or files. The result template can be then rendered multiple times with different context.

    Parse from string:

    -
    1
    2
    3
    4
    var tpl = engine.parse('{{name | capitalize}}');

    engine.renderSync(tpl, {name: 'alice'}) // 'Alice'
    engine.renderSync(tpl, {name: 'bob'}) // 'Bob'
    +
    var tpl = engine.parse('{{name | capitalize}}');
    +
    +engine.renderSync(tpl, {name: 'alice'}) // 'Alice'
    +engine.renderSync(tpl, {name: 'bob'}) // 'Bob'

    Parse from file:

    -
    1
    2
    3
    4
    var tpl = engine.parseFileSync('hello');    // contents of `hello.liquid`: {{name}}

    engine.renderSync(tpl, {name: 'alice'}) // 'Alice'
    engine.renderSync(tpl, {name: 'bob'}) // 'Bob'
    +
    var tpl = engine.parseFileSync('hello');    // contents of `hello.liquid`: {{name}}
    +
    +engine.renderSync(tpl, {name: 'alice'}) // 'Alice'
    +engine.renderSync(tpl, {name: 'bob'}) // 'Bob'

    The template string/file is parsed only once and rendered multiple times using different context. Templates for different files can be stored into a Map and can be retrieved directly for subsequent renders.

    The cache Option

    The cache option can be set to instruct liquidjs to use cached parsed templates each time you call renderFile or renderFileSync.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var { Liquid } = require('liquidjs');
    var engine = new Liquid({
    cache: true
    });

    // liquidjs parses the hello.liquid, then renders it with {name: 'alice'}
    engine.renderFileSync('hello', {name: 'alice'})

    // liquidjs finds the cached template, then renders it with {name: 'bob'}
    engine.renderFileSync('hello', {name: 'bob'})
    +
    var { Liquid } = require('liquidjs');
    +var engine = new Liquid({
    +    cache: true
    +});
    +
    +// liquidjs parses the hello.liquid, then renders it with {name: 'alice'}
    +engine.renderFileSync('hello', {name: 'alice'})
    +
    +// liquidjs finds the cached template, then renders it with {name: 'bob'}
    +engine.renderFileSync('hello', {name: 'bob'})
    - +
    diff --git a/tutorials/changelog.html b/tutorials/changelog.html index 91cfe9b15..059041e09 100644 --- a/tutorials/changelog.html +++ b/tutorials/changelog.html @@ -746,7 +746,7 @@
    - +
    diff --git a/tutorials/contribution-guidelines.html b/tutorials/contribution-guidelines.html index 8a8ca2a19..cb25b2142 100644 --- a/tutorials/contribution-guidelines.html +++ b/tutorials/contribution-guidelines.html @@ -102,7 +102,7 @@
    - +
    diff --git a/tutorials/differences.html b/tutorials/differences.html index 5ea24d00b..8133f8298 100644 --- a/tutorials/differences.html +++ b/tutorials/differences.html @@ -126,7 +126,7 @@
    - +
    diff --git a/tutorials/dos.html b/tutorials/dos.html index 5d4a7e85b..34d7475c2 100644 --- a/tutorials/dos.html +++ b/tutorials/dos.html @@ -94,24 +94,33 @@

    When the template or data context cannot be trusted, enabling DoS prevention options is crucial. LiquidJS provides 3 options for this purpose: parseLimit, renderLimit, and memoryLimit.

    TL;DR

    Setting these options can largely ensure that your LiquidJS instance won’t hang for extended periods or consume excessive memory. These limits are based on the available JavaScript APIs, so they are not precise hard limits but thresholds to help prevent your process from failing or hanging.

    -
    1
    2
    3
    4
    5
    const liquid = new Liquid({
    parseLimit: 1e8, // typical size of your templates in each render
    renderLimit: 1000, // limit each render to be completed in 1s
    memoryLimit: 1e9, // memory available for LiquidJS (1e9 for 1GB)
    })
    +
    const liquid = new Liquid({
    +    parseLimit: 1e8, // typical size of your templates in each render
    +    renderLimit: 1000, // limit each render to be completed in 1s
    +    memoryLimit: 1e9, // memory available for LiquidJS (1e9 for 1GB)
    +})

    When a parse() or render() cannot be completed within given resource, it throws.

    parseLimit

    parseLimit restricts the size (character length) of templates parsed in each .parse() call, including referenced partials and layouts. Since LiquidJS parses template strings in near O(n) time, limiting total template length is usually sufficient.

    A typical PC handles 1e8 (100M) characters without issues.

    renderLimit

    Restricting template size alone is insufficient because dynamic loops with large counts can occur in render time. renderLimit mitigates this by limiting the time consumed by each render() call.

    -
    1
    2
    3
    {%- for i in (1..10000000) -%}
    order: {{i}}
    {%- endfor -%}
    +
    {%- for i in (1..10000000) -%}
    +    order: {{i}}
    +{%- endfor -%}

    Render time is checked on a per-template basis (before rendering each template). In the above example, there are 2 templates in the loop: order: and {{i}}, render time will be checked 10000000x2 times.

    For time-consuming tags and filters within a single template, the process can still hang. For fully controlled rendering, consider using a process manager like paralleljs.

    memoryLimit

    Even with small number of templates and iterations, memory usage can grow exponentially. In the following example, memory doubles with each iteration:

    -
    1
    2
    3
    4
    {% assign array = "1,2,3" | split: "," %}
    {% for i in (1..32) %}
    {% assign array = array | concat: array %}
    {% endfor %}
    +
    {% assign array = "1,2,3" | split: "," %}
    +{% for i in (1..32) %}
    +    {% assign array = array | concat: array %}
    +{% endfor %}

    memoryLimit restricts memory-sensitive filters to prevent excessive memory allocation. As JavaScript uses GC to manage memory, memoryLimit limits only the total number of objects allocated by memory sensitive filters in LiquidJS thus may not reflect the actual memory footprint.

    - +
    diff --git a/tutorials/drops.html b/tutorials/drops.html index 36f63024e..3fc3d351d 100644 --- a/tutorials/drops.html +++ b/tutorials/drops.html @@ -96,7 +96,23 @@
    Drop for JavaScript

    Drop interface is implemented differently in LiquidJS compared to built-in filters and other template functionalities. Since LiquidJS runs in JavaScript, custom Drops need to be reimplemented in JavaScript anyway. There’s no compatibility between JavaScript classes and Ruby classes.

    -

    Basic Usage

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import { Liquid, Drop } from 'liquidjs'

    class SettingsDrop extends Drop {
    constructor() {
    super()
    this.foo = 'FOO'
    }
    bar() {
    return 'BAR'
    }
    }

    const engine = new Liquid()
    const template = `foo: {{settings.foo}}, bar: {{settings.bar}}`
    const context = { settings: new SettingsDrop() }
    // Outputs: "foo: FOO, bar: BAR"
    engine.parseAndRender(template, context).then(html => console.log(html))
    +

    Basic Usage

    import { Liquid, Drop } from 'liquidjs'
    +
    +class SettingsDrop extends Drop {
    +  constructor() {
    +    super()
    +    this.foo = 'FOO'
    +  }
    +  bar() {
    +    return 'BAR'
    +  }
    +}
    +
    +const engine = new Liquid()
    +const template = `foo: {{settings.foo}}, bar: {{settings.bar}}`
    +const context = { settings: new SettingsDrop() }
    +// Outputs: "foo: FOO, bar: BAR"
    +engine.parseAndRender(template, context).then(html => console.log(html))

    Runkit link

    As shown above, besides reading properties from context scopes, you can also call methods. You only need to create a custom class inherited from Drop.

    @@ -104,19 +120,93 @@

    liquidMethodMissing

    For cases when there isn’t a fixed set of properties, you can leverage liquidMethodMissing to dynamically resolve the value of a variable name.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import { Liquid, Drop } from 'liquidjs'

    class SettingsDrop extends Drop {
    liquidMethodMissing(key) {
    return key.toUpperCase()
    }
    }

    const engine = new Liquid()
    // Outputs: "COO"
    engine.parseAndRender("{{settings.coo}}", { settings: new SettingsDrop() })
    .then(html => console.log(html))
    +
    import { Liquid, Drop } from 'liquidjs'
    +
    +class SettingsDrop extends Drop {
    +  liquidMethodMissing(key) {
    +    return key.toUpperCase()
    +  }
    +}
    +
    +const engine = new Liquid()
    +// Outputs: "COO"
    +engine.parseAndRender("{{settings.coo}}", { settings: new SettingsDrop() })
    +  .then(html => console.log(html))

    liquidMethodMissing supports Promise, meaning you can make async calls within it. A more useful case can be fetching the value dynamically from the database. By using Drops, you can avoid hardcoding each property into the context. For example:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import { Liquid, Drop } from 'liquidjs'

    class DBDrop extends Drop {
    async liquidMethodMissing(key) {
    const record = await db.getRecordByKey(key)
    return record.value
    }
    }

    const engine = new Liquid()
    const context = { db: new DBDrop() }
    engine.parseAndRender("{{db.coo}}", context).then(html => console.log(html))
    +
    import { Liquid, Drop } from 'liquidjs'
    +
    +class DBDrop extends Drop {
    +  async liquidMethodMissing(key) {
    +    const record = await db.getRecordByKey(key)
    +    return record.value
    +  }
    +}
    +
    +const engine = new Liquid()
    +const context = { db: new DBDrop() }
    +engine.parseAndRender("{{db.coo}}", context).then(html => console.log(html))

    valueOf

    Drops can implement a valueOf() method, the return value of which can be used to replace itself in the output. For example:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import { Liquid, Drop } from 'liquidjs'

    class ColorDrop extends Drop {
    valueOf() {
    return 'red'
    }
    }

    const engine = new Liquid()
    const context = { color: new ColorDrop() }
    // Outputs: "red"
    engine.parseAndRender("{{color}}", context).then(html => console.log(html))
    +
    import { Liquid, Drop } from 'liquidjs'
    +
    +class ColorDrop extends Drop {
    +  valueOf() {
    +    return 'red'
    +  }
    +}
    +
    +const engine = new Liquid()
    +const context = { color: new ColorDrop() }
    +// Outputs: "red"
    +engine.parseAndRender("{{color}}", context).then(html => console.log(html))

    toLiquid

    toLiquid() is not a method of Drop, but it can be used to return a Drop. In cases where you have a fixed structure in the context that cannot change its values, you can implement toLiquid() to let LiquidJS use the returned value instead of itself to render the templates.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import { Liquid, Drop } from 'liquidjs'

    const context = {
    person: {
    firstName: "Jun",
    lastName: "Yang",
    name: "Jun Yang",
    toLiquid: () => ({
    firstName: this.firstName,
    lastName: this.lastName,
    // use a different `name`
    name: "Yang, Jun"
    })
    }
    }

    const engine = new Liquid()
    // Outputs: "Yang, Jun"
    engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))
    +
    import { Liquid, Drop } from 'liquidjs'
    +
    +const context = {
    +  person: {
    +    firstName: "Jun",
    +    lastName: "Yang",
    +    name: "Jun Yang",
    +    toLiquid: () => ({
    +      firstName: this.firstName,
    +      lastName: this.lastName,
    +      // use a different `name`
    +      name: "Yang, Jun"
    +    })
    +  }
    +}
    +
    +const engine = new Liquid()
    +// Outputs: "Yang, Jun"
    +engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))

    Of course, you can also return a PersonDrop instance in the toLiquid() method and implement this functionality within PersonDrop:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import { Liquid, Drop } from 'liquidjs'

    class PersonDrop extends Drop {
    constructor(person) {
    super()
    this.person = person
    }
    name() {
    return this.person.lastName + ", " + this.person.firstName
    }
    }

    const context = {
    person: {
    firstName: "Jun",
    lastName: "Yang",
    name: "Jun Yang",
    toLiquid: function () { return new PersonDrop(this) }
    }
    }

    const engine = new Liquid()
    // Outputs: "Yang, Jun"
    engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))
    +
    import { Liquid, Drop } from 'liquidjs'
    +
    +class PersonDrop extends Drop {
    +  constructor(person) {
    +    super()
    +    this.person = person
    +  }
    +  name() {
    +    return this.person.lastName + ", " + this.person.firstName
    +  }
    +}
    +
    +const context = {
    +  person: {
    +    firstName: "Jun",
    +    lastName: "Yang",
    +    name: "Jun Yang",
    +    toLiquid: function () { return new PersonDrop(this) }
    +  }
    +}
    +
    +const engine = new Liquid()
    +// Outputs: "Yang, Jun"
    +engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))
    toLiquid() vs. valueOf() Difference
    • valueOf() is typically used to define how the current variable should be rendered, while toLiquid() is often used to convert an object into a Drop or another scope provided to the template.
    • @@ -126,22 +216,28 @@

      Special Drops

      LiquidJS itself implements several built-in drops to facilitate template writing. This part is compatible with Shopify Liquid, as we need templates to be portable.

      blank

      Useful to check whether a string variable is false, null, undefined, an empty string, or a string containing only blank characters.

      -
      1
      2
      3
      {% unless author == blank %}
      {{author}}
      {% endif %}
      +
      {% unless author == blank %}
      +    {{author}}
      +{% endif %}

      empty

      Useful to check if an array, string, or object is empty.

      -
      1
      2
      3
      {% if authors == empty %}
      Author list is empty
      {% endif %}
      +
      {% if authors == empty %}
      +    Author list is empty
      +{% endif %}
      empty implementation

      For arrays and strings, LiquidJS checks their .length property. For objects, LiquidJS calls Object.keys() to check whether they have keys.

      nil

      nil Drop is used to check whether a variable is not defined or defined as null or undefined, essentially equivalent to JavaScript == null check.

      -
      1
      2
      3
      {% if nonexistent == nil %}
      null variable
      {% endif %}
      +
      {% if nonexistent == nil %}
      +    null variable
      +{% endif %}

      Other Drops

      There are still several Drops for specific tags, like forloop, tablerowloop, block, which are covered by respective tag documents.

      - +
      diff --git a/tutorials/escaping.html b/tutorials/escaping.html index a2f158129..39b18019a 100644 --- a/tutorials/escaping.html +++ b/tutorials/escaping.html @@ -99,37 +99,43 @@

      HTML Escape

      By default output is not escaped. While you can use escape filter for this:

      Input

      -
      1
      {{ "1 < 2" | escape }}
      +
      {{ "1 < 2" | escape }}

      Output

      -
      1
      1 &lt; 2
      +
      1 &lt; 2

      There’s also escape_once, newline_to_br, strip_html filters for you to fine tune your output.

      In cases where variables are mostly not trusted, outputEscape can be set to "escape" to apply escape by default. In this case, when you need some output not to be escaped, raw filter can be used:

      Input

      -
      1
      2
      {{ "1 < 2" }}
      {{ "<button>OK</button>" | raw }}
      +
      {{ "1 < 2" }}
      +{{ "<button>OK</button>" | raw }}

      Output

      -
      1
      2
      1 &lt; 2
      <button>OK</button>
      +
      1 &lt; 2
      +<button>OK</button>

      Liquid Escape

      To disable Liquid language and output strings like {{ and {%, the raw tag can be used.

      Input

      -
      1
      2
      3
      4
      {% raw %}
      In LiquidJS, {{ this | escape }} will be HTML-escaped, but
      {{{ that }}} will not.
      {% endraw %}
      +
      {% raw %}
      +  In LiquidJS, {{ this | escape }} will be HTML-escaped, but
      +  {{{ that }}} will not.
      +{% endraw %}

      Output

      -
      1
      2
      In LiquidJS, {{ this | escape }} will be HTML-escaped, but
      {{{ that }}} will not.
      +
      In LiquidJS, {{ this | escape }} will be HTML-escaped, but
      +{{{ that }}} will not.

      Within strings literals in LiquidJS template, \ can be used to escape special characters in string syntax. For example:

      Input

      -
      1
      {{ "\"" }}
      +
      {{ "\"" }}

      Output

      -
      1
      "
      +
      "
      - +
      diff --git a/tutorials/intro-to-liquid.html b/tutorials/intro-to-liquid.html index c63d9743e..0f8e8ab99 100644 --- a/tutorials/intro-to-liquid.html +++ b/tutorials/intro-to-liquid.html @@ -102,26 +102,30 @@

    Outputs

    Outputs are used to output variables, which can be transformed by filters, into HTML. The following template will insert the value of username into the input’s value:

    -
    1
    <input type="text" name="user" value="{{username}}">
    +
    <input type="text" name="user" value="{{username}}">

    Values in output can be transformed by filters before output. To append a string after the variable:

    -
    1
    {{ username | append: ", welcome to LiquidJS!" }}
    +
    {{ username | append: ", welcome to LiquidJS!" }}

    Filters can be chained:

    -
    1
    {{ username | append: ", welcome to LiquidJS!" | capitalize }}
    +
    {{ username | append: ", welcome to LiquidJS!" | capitalize }}

    A complete list of filters supported by LiquidJS can be found here.

    Tags

    Tags are used to control the template rendering process, manipulating template variables, inter-op with other templates, etc. For example assign can be used to define a variable which can be later used in the template:

    -
    1
    {% assign foo = "FOO" %}
    +
    {% assign foo = "FOO" %}

    Typically tags appear in pairs with a start tag and a corresponding end tag. For example:

    -
    1
    2
    3
    4
    5
    {% if foo == "FOO" %}
    Variable `foo` equals "FOO"
    {% else %}
    Variable `foo` not equals "FOO"
    {% endif %}
    +
    {% if foo == "FOO" %}
    +    Variable `foo` equals "FOO"
    +{% else %}
    +    Variable `foo` not equals "FOO"
    +{% endif %}

    A complete list of tags supported by LiquidJS can be found here.

    - +
    diff --git a/tutorials/migrate-to-9.html b/tutorials/migrate-to-9.html index 5819c12ab..9228877ae 100644 --- a/tutorials/migrate-to-9.html +++ b/tutorials/migrate-to-9.html @@ -116,7 +116,7 @@
    - +
    diff --git a/tutorials/operators.html b/tutorials/operators.html index 081586611..e5d89fca0 100644 --- a/tutorials/operators.html +++ b/tutorials/operators.html @@ -106,7 +106,7 @@
    - +
    diff --git a/tutorials/options.html b/tutorials/options.html index 832be5e4b..2ffcf2365 100644 --- a/tutorials/options.html +++ b/tutorials/options.html @@ -93,7 +93,10 @@

    The Liquid constructor accepts a plain object as options to define the behavior of LiquidJS. All of these options are optional thus we can specify any of them, for example the cache option:

    -
    1
    2
    3
    4
    const { Liquid } = require('liquidjs')
    const engine = new Liquid({
    cache: true
    })
    +
    const { Liquid } = require('liquidjs')
    +const engine = new Liquid({
    +    cache: true
    +})
    API Document

    Following is an overview for all the options, for exact types and signatures please refer to LiquidOptions | API.

    @@ -109,10 +112,10 @@

    Note: for historical reasons, it’s named dynamicPartials but it also works for layouts.

    dynamicPartials indicates whether or not to treat filename arguments in include, render, layout tags as a variable. Defaults to true. For example, render the following snippet with scope { file: 'foo.html' } will include the foo.html:

    -
    1
    {% include file %}
    +
    {% include file %}

    Setting dynamicPartials: false, LiquidJS will try to include the file named file, which is weird but allows simpler syntax if your template relations are static:

    -
    1
    {% liquid foo.html %}
    +
    {% liquid foo.html %}
    Common Pitfall

    LiquidJS defaults this option to true to be compatible with shopify/liquid, but if you’re from eleventy it’s set to false by default (see Quoted Include Paths) which I believe is trying to be compatible with Jekyll.

    @@ -126,10 +129,18 @@
  • Parameters are under include variable instead of current scope.
  • For example in the following template, name.html is not quoted, header and "HEADER" are separated by =, and the header parameter is referenced by include.header. More details please check out include.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    // entry template
    {% include article.html header="HEADER" content="CONTENT" %}

    // article.html
    <article>
    <header>{{include.header}}</header>
    {{include.content}}
    </article>
    +
    // entry template
    +{% include article.html header="HEADER" content="CONTENT" %}
    +
    +// article.html
    +<article>
    +  <header>{{include.header}}</header>
    +  {{include.content}}
    +</article>

    extname

    extname defines the default extension name to be appended into filenames if the filename has no extension name. Defaults to '' which means it’s disabled by default. By setting it to .liquid:

    -
    1
    2
    {% render "foo" %}  there's no extname, adds `.liquid` and loads foo.liquid
    {% render "foo.html" %} there is an extname already, loads foo.html directly
    +
    {% render "foo" %}  there's no extname, adds `.liquid` and loads foo.liquid
    +{% render "foo.html" %}  there is an extname already, loads foo.html directly
    Legacy Versions

    Before 2.0.1, extname is set to .liquid by default. To change that you need to set extname: '' explicitly. See #41 for details.

    @@ -149,7 +160,7 @@

    dateFormat is used to specify a default format to output dates. %A, %B %-e, %Y at %-l:%M %P %z will be used if not specified. For example, set dateFormat: %Y-%m-%dT%H:%M:%S:%LZ to output all dates in [JavaScript Date.toJson()][https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON] format.

    Trimming

    greedy, trimOutputLeft, trimOutputRight, trimTagLeft, trimTagRight options are used to eliminate extra newlines and indents in templates around Liquid Constructs. See Whitespace Control for details.

    Delimiter

    outputDelimiterLeft, outputDelimiterRight, tagDelimiterLeft, tagDelimiterRight are used to customize the delimiters for LiquidJS Tags and Filters. For example with outputDelimiterLeft: <%=, outputDelimiterRight: %> we are able to avoid conflicts with other languages:

    -
    1
    <%= username | append: ", welcome to LiquidJS!" %>
    +
    <%= username | append: ", welcome to LiquidJS!" %>

    Strict

    strictFilters is used to assert filter existence. If set to false, undefined filters will be skipped. Otherwise, undefined filters will cause a parse exception. Defaults to false.

    strictVariables is used to assert variable existence. If set to false, undefined variables will be rendered as empty string. Otherwise, undefined variables will cause a render exception. Defaults to false.

    @@ -162,7 +173,7 @@
    - +
    diff --git a/tutorials/parse-parameters.html b/tutorials/parse-parameters.html index b246c9862..3fb739b33 100644 --- a/tutorials/parse-parameters.html +++ b/tutorials/parse-parameters.html @@ -93,31 +93,75 @@

    Access Raw Parameters

    As covered in Register Filters/Tags, tag parameters is available on tagToken.args as a raw string. For example:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Usage: {% random foo bar coo %}
    // Output: "foo", "bar" or "coo"
    engine.registerTag('random', {
    parse(tagToken) {
    // tagToken.args === "foo bar coo"
    this.items = tagToken.args.split(' ')
    },
    render(context, emitter) {
    // get a random index
    const index = Math.floor(this.items.length * Math.random())
    // output that item
    emitter.write(this.items[index])
    }
    })
    +
    // Usage: {% random foo bar coo %}
    +// Output: "foo", "bar" or "coo"
    +engine.registerTag('random', {
    +  parse(tagToken) {
    +    // tagToken.args === "foo bar coo"
    +    this.items = tagToken.args.split(' ')
    +  },
    +  render(context, emitter) {
    +    // get a random index
    +    const index = Math.floor(this.items.length * Math.random())
    +    // output that item
    +    emitter.write(this.items[index])
    +  }
    +})

    Here’s a JSFiddle version: https://jsfiddle.net/ctj364up/2/

    Parse Parameters as Values

    Sometimes we need more dynamic tags and want to pass values to the custom tag instead of static strings. Variables in LiquidJS can be literal (string, number, etc.) or a variable from current context scope.

    The following modified template also contains 3 values to random from, but they’re values instead of static strings. The first one is string literal, second one is an identifier, third one is a property access sequence containing two identifiers.

    -
    1
    {% random "foo" bar obj.coo %}
    +
    {% random "foo" bar obj.coo %}

    It can be tricky to parse all these cases manually, but there’s a Tokenizer class in LiquidJS you can make use of.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    const { Liquid, Tokenizer, evalToken } = require('liquidjs')
    engine.registerTag('random', {
    parse(tagToken) {
    const tokenizer = new Tokenizer(tagToken.args)
    this.items = []
    while (!tokenizer.end()) {
    // here readValue() returns a LiteralToken or PropertyAccessToken
    this.items.push(tokenizer.readValue())
    }
    },
    * render(context, emitter) {
    const index = Math.floor(this.items.length * Math.random())
    const token = this.items[index]
    // in LiquidJS, we use yield to wait for async call
    const value = yield evalToken(token, context)
    emitter.write(value)
    }
    })
    +
    const { Liquid, Tokenizer, evalToken } = require('liquidjs')
    +engine.registerTag('random', {
    +  parse(tagToken) {
    +    const tokenizer = new Tokenizer(tagToken.args)
    +    this.items = []
    +    while (!tokenizer.end()) {
    +      // here readValue() returns a LiteralToken or PropertyAccessToken
    +      this.items.push(tokenizer.readValue())
    +    }
    +  },
    +  * render(context, emitter) {
    +    const index = Math.floor(this.items.length * Math.random())
    +    const token = this.items[index]
    +    // in LiquidJS, we use yield to wait for async call
    +    const value = yield evalToken(token, context)
    +    emitter.write(value)
    +  }
    +})

    Calling this tag in scope { bar: "bar", obj: { coo: "coo" } } yields exactly the same result as the first example. See this JSFiddle: https://jsfiddle.net/ctj364up/3/

    Async and Promises

    Async calls in LiquidJS are implemented by generators directly, for we can call generators in synchronous manner so this tag implementation is also valid for renderSync(), parseAndRenderSync(), renderFileSync(). If you need to await a promise in tag implementation, simply replace await somePromise with yield somePromise and keep * render() instead of async render() will do the trick. See Sync and Async for more details.

    Parse Key-Value Pairs as Named Parameters

    Named parameters become very handy when there’re optional parameters or lots of parameters, in which case the order of parameters is not important. This is exactly what Hash class is invented for.

    -
    1
    {% random from:2, to:max %}
    +
    {% random from:2, to:max %}

    In the above example, we’re trying to generate a random number in the range [2, max]. We’ll use Hash to parse from and to parameters.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const { Liquid, Hash } = require('liquidjs')

    engine.registerTag('random', {
    parse(tagToken) {
    // parse the parameters structure into `this.args`
    this.args = new Hash(tagToken.args)
    },
    * render(context, emitter) {
    // evaluate the parameters in `context`
    const {from, to} = yield this.args.render(context)
    const length = to - from + 1
    const value = from + Math.floor(length * Math.random())
    emitter.write(value)
    }
    })
    +
    const { Liquid, Hash } = require('liquidjs')
    +
    +engine.registerTag('random', {
    +  parse(tagToken) {
    +    // parse the parameters structure into `this.args`
    +    this.args = new Hash(tagToken.args)
    +  },
    +  * render(context, emitter) {
    +    // evaluate the parameters in `context`
    +    const {from, to} = yield this.args.render(context)
    +    const length = to - from + 1
    +    const value = from + Math.floor(length * Math.random())
    +    emitter.write(value)
    +  }
    +})

    Rendering {% random from:2, to:max %} in scope { max: 10 } will generate a random number in the range [2, 10]. See this JSFiddle: https://jsfiddle.net/ctj364up/4/

    - +
    diff --git a/tutorials/partials-and-layouts.html b/tutorials/partials-and-layouts.html index ce7b877dd..ad6165397 100644 --- a/tutorials/partials-and-layouts.html +++ b/tutorials/partials-and-layouts.html @@ -93,26 +93,44 @@

    Render Partials

    For the following template files:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    // file: color.liquid
    color: '{{ color }}' shape: '{{ shape }}'

    // file: theme.liquid
    {% assign shape = 'circle' %}
    {% render 'color.liquid' %}
    {% render 'color.liquid' with 'red' %}
    {% render 'color.liquid', color: 'yellow', shape: 'square' %}
    +
    // file: color.liquid
    +color: '{{ color }}' shape: '{{ shape }}'
    +
    +// file: theme.liquid
    +{% assign shape = 'circle' %}
    +{% render 'color.liquid' %}
    +{% render 'color.liquid' with 'red' %}
    +{% render 'color.liquid', color: 'yellow', shape: 'square' %}

    The output will be:

    -
    1
    2
    3
    color: '' shape: 'circle'
    color: 'red' shape: 'circle'
    color: 'yellow' shape: 'square'
    +
    color: '' shape: 'circle'
    +color: 'red' shape: 'circle'
    +color: 'yellow' shape: 'square'

    More details please refer to the render tag.

    The ".liquid" Extension

    The “.liquid” extension in layout, render and include can be omitted if Liquid instance is created using extname: ".liquid" option. See the extname option for details.

    Layout Templates (Extends)

    For the following template files:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    // file: default-layout.liquid
    Header
    {% block content %}My default content{% endblock %}
    Footer

    // file: page.liquid
    {% layout "default-layout.liquid" %}
    {% block content %}My page content{% endblock %}
    +
    // file: default-layout.liquid
    +Header
    +{% block content %}My default content{% endblock %}
    +Footer
    +
    +// file: page.liquid
    +{% layout "default-layout.liquid" %}
    +{% block content %}My page content{% endblock %}

    The output of page.liquid:

    -
    1
    2
    3
    Header
    My page content
    Footer
    +
    Header
    +My page content
    +Footer

    More details please refer to the layout tag.

    - +
    diff --git a/tutorials/plugins.html b/tutorials/plugins.html index c8b0fdbe2..bb9cc2dfb 100644 --- a/tutorials/plugins.html +++ b/tutorials/plugins.html @@ -95,10 +95,22 @@

    A number of tags and filters can be encapsulated into a plugin, which will be typically installed via npm. This article provides information about how to create and use a plugin.

    Write a Plugin

    A liquidjs plugin is simple function which takes the Liquid class as the first parameter and the Liquid instance for this. We can call liquidjs APIs on this to make certain changes, especially register filters and tags.

    Now we’ll make a plugin to upper case every letter of the input, save the following snippet to upup.js:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * Inside the plugin function, `this` refers to the Liquid instance.
    *
    * @param Liquid: provides facilities to implement tags and filters.
    */
    module.exports = function (Liquid) {
    this.registerFilter('upup', x => x.toUpperCase());
    }
    +
    /**
    + * Inside the plugin function, `this` refers to the Liquid instance.
    + *
    + * @param Liquid: provides facilities to implement tags and filters.
    + */
    +module.exports = function (Liquid) {
    +    this.registerFilter('upup', x => x.toUpperCase());
    +}

    Use a Plugin

    Simply pass the plugin function into the .plugin() method:

    -
    1
    2
    3
    4
    5
    6
    const engine = new Liquid()

    engine.plugin(require('./upup.js'));
    engine
    .parseAndRender('{{ "foo" | upup }}')
    .then(console.log) // outputs "FOO"
    +
    const engine = new Liquid()
    +
    +engine.plugin(require('./upup.js'));
    +engine
    +    .parseAndRender('{{ "foo" | upup }}')
    +    .then(console.log)  // outputs "FOO"

    Plugin List

    Since this library excludes certain features that are available on the Shopify platform but not on the Shopify/liquid repo, see Differences with Shopify/liquid.

    Here’s a list of plugins that backfill those features. Feel free to add yours, this file is publicly editable.

    @@ -109,7 +121,7 @@
    - +
    diff --git a/tutorials/register-filters-tags.html b/tutorials/register-filters-tags.html index 447c794d5..6e53cffcb 100644 --- a/tutorials/register-filters-tags.html +++ b/tutorials/register-filters-tags.html @@ -92,27 +92,67 @@
    -

    Register Tags

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Usage: {% upper name %}
    import { Value, TagToken, Context, Emitter, TopLevelToken } from 'liquidjs'

    engine.registerTag('upper', {
    parse: function(tagToken: TagToken, remainTokens: TopLevelToken[]) {
    this.value = new Value(token.args, liquid)
    },
    render: function*(ctx: Context) {
    const str = yield this.value.value(ctx); // 'alice'
    return str.toUpperCase() // 'ALICE'
    }
    });
    +

    Register Tags

    // Usage: {% upper name %}
    +import { Value, TagToken, Context, Emitter, TopLevelToken } from 'liquidjs'
    +
    +engine.registerTag('upper', {
    +    parse: function(tagToken: TagToken, remainTokens: TopLevelToken[]) {
    +        this.value = new Value(token.args, liquid)
    +    },
    +    render: function*(ctx: Context) {
    +        const str = yield this.value.value(ctx); // 'alice'
    +        return str.toUpperCase() // 'ALICE'
    +    }
    +});
    • parse: Read tokens from remainTokens until your end token.
    • render: Combine scope data with your parsed tokens into HTML string.

    For complex tag implementation, you can also provide a tag class:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Usage: {% upper name:"alice" %}
    import { Hash, Tag, TagToken, Context, Emitter, TopLevelToken, Liquid } from 'liquidjs'

    engine.registerTag('upper', class UpperTag extends Tag {
    private hash: Hash
    constructor(tagToken: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    super(tagToken, remainTokens, liquid)
    this.hash = new Hash(tagToken.args)
    }
    * render(ctx: Context) {
    const hash = yield this.hash.render();
    return hash.name.toUpperCase() // 'ALICE'
    }
    });
    +
    // Usage: {% upper name:"alice" %}
    +import { Hash, Tag, TagToken, Context, Emitter, TopLevelToken, Liquid } from 'liquidjs'
    +
    +engine.registerTag('upper', class UpperTag extends Tag {
    +    private hash: Hash
    +    constructor(tagToken: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    +        super(tagToken, remainTokens, liquid)
    +        this.hash = new Hash(tagToken.args)
    +    }
    +    * render(ctx: Context) {
    +        const hash = yield this.hash.render();
    +        return hash.name.toUpperCase() // 'ALICE'
    +    }
    +});

    See existing tag implementations here: https://github.com/harttle/liquidjs/tree/master/src/tags
    See demo example here: https://github.com/harttle/liquidjs/blob/master/demo/typescript/index.ts

    -

    Register Filters

    1
    2
    // Usage: {{ name | upper }}
    engine.registerFilter('upper', v => v.toUpperCase())
    +

    Register Filters

    // Usage: {{ name | upper }}
    +engine.registerFilter('upper', v => v.toUpperCase())

    Filter arguments will be passed to the registered filter function, for example:

    -
    1
    2
    // Usage: {{ 1 | add: 2, 3 }}
    engine.registerFilter('add', (initial, arg1, arg2) => initial + arg1 + arg2)
    +
    // Usage: {{ 1 | add: 2, 3 }}
    +engine.registerFilter('add', (initial, arg1, arg2) => initial + arg1 + arg2)

    See existing filter implementations here: https://github.com/harttle/liquidjs/tree/master/src/filters

    Unregister Tags/Filters

    In some cases it’s desirable to disable some tags/filters (see #324), you’ll need to register a dummy tag/filter in which an corresponding Error throws.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // disable a tag
    const disabledTag = {
    parse: function(token) {
    throw new Error(`tag "${token.name}" disabled`);
    }
    }
    engine.registerTag('include', disabledTag);

    // disable a filter
    function disabledFilter(name) {
    return function () {
    throw new Error(`filter "${name}" disabled`);
    }
    }
    engine.registerFilter('plus', disabledFilter('plus'));
    +
    // disable a tag
    +const disabledTag = {
    +    parse: function(token) {
    +        throw new Error(`tag "${token.name}" disabled`);
    +    }
    +}
    +engine.registerTag('include', disabledTag);
    +
    +// disable a filter
    +function disabledFilter(name) {
    +    return function () {
    +        throw new Error(`filter "${name}" disabled`);
    +    }
    +}
    +engine.registerFilter('plus', disabledFilter('plus'));
    - +
    diff --git a/tutorials/render-file.html b/tutorials/render-file.html index d0c935352..d6a26b0e6 100644 --- a/tutorials/render-file.html +++ b/tutorials/render-file.html @@ -94,17 +94,31 @@

    For a typical project there could be a directory of template files, you’ll need to set the template root and call renderFile or renderFileSync to render a specific file.

    Render a File

    For example you have a directory of templates like this:

    -
    1
    2
    3
    4
    5
    .
    ├── index.js
    └── views/
    ├── hello.liquid
    └── world.liquid
    +
    .
    +├── index.js
    +└── views/
    +  ├── hello.liquid
    +  └── world.liquid

    hello.liquid contains a single line `name: {{name}}`.
    Now save the following contents into index.js:

    -
    1
    2
    3
    4
    5
    6
    7
    var engine = new Liquid({
    root: path.resolve(__dirname, 'views/'), // root for layouts/includes lookup
    extname: '.liquid' // used for layouts/includes, defaults ""
    });
    engine
    .renderFile("hello", {name: 'alice'}) // will read and render `views/hello.liquid`
    .then(console.log) // outputs "Alice"
    +
    var engine = new Liquid({
    +    root: path.resolve(__dirname, 'views/'),  // root for layouts/includes lookup
    +    extname: '.liquid'          // used for layouts/includes, defaults ""
    +});
    +engine
    +    .renderFile("hello", {name: 'alice'})   // will read and render `views/hello.liquid`
    +    .then(console.log)  // outputs "Alice"

    Run node index.js and you’ll get output like this:

    -
    1
    2
    > node index.js
    name: alice
    +
    > node index.js
    +name: alice

    Template Lookup

    Template files names passed to renderFile, parseFile, renderFileSync, parseFileSync APIs,
    and include, layout tags are resolved against the root option.

    It can be a string-typed path (see above example), or a list of root directories, in which case templates will be looked up in that order. e.g.

    -
    1
    2
    3
    4
    var engine = new Liquid({
    root: ['views/', 'views/partials/'],
    extname: '.liquid'
    });
    +
    var engine = new Liquid({
    +    root: ['views/', 'views/partials/'],
    +    extname: '.liquid'
    +});
    Relative Paths

    Relative paths in root will be resolved against cwd().

    @@ -122,19 +136,48 @@

    If fetch fails, a 404/500 error or network failures for example, an ENOENT error will be thrown.
    Here’s a demo for browsers: demo/browser.

    Abstract File System

    LiquidJS defines an abstract file system interface and the default implementation is src/fs/fs-impl.ts for Node.js and src/build/fs-impl-browser.ts for the browser bundle.

    The Liquid constructor provides a fs option to specify the file system implementation. It’s supposed to be used to define customized template fetching logic, i.e. fetch template from a database table, like:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    var engine = new Liquid({
    fs: {
    readFileSync (file) {
    return db.model('Template').findByIdSync(file).text
    },
    async readFile (file) {
    const template = await db.model('Template').findById(file)
    return template.text
    },
    existsSync () {
    return true
    },
    async exists () {
    return true
    },
    contains () {
    return true
    },
    resolve(root, file, ext) {
    return file
    }
    }
    });
    +
    var engine = new Liquid({
    +    fs: {
    +        readFileSync (file) {
    +            return db.model('Template').findByIdSync(file).text
    +        },
    +        async readFile (file) {
    +            const template = await db.model('Template').findById(file)
    +            return template.text
    +        },
    +        existsSync () {
    +            return true
    +        },
    +        async exists () {
    +            return true
    +        },
    +        contains () {
    +            return true
    +        },
    +        resolve(root, file, ext) {
    +            return file
    +        }
    +    }
    +});
    Path Traversal Vulnerability

    The default value of contains() always returns true. That means when specifying an abstract file system, you’ll need to provide a proper contains() to avoid expose such vulnerabilities.

    In-memory Template

    To facilitate rendering w/o files, there’s a templates option to specify a mapping of filenames and their content. LiquidJS will read templates from the mapping.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    const engine = new Liquid({
    templates: {
    'views/entry': 'header {% include "../partials/footer" %}',
    'partials/footer': 'footer'
    }
    })
    engine.renderFileSync('views/entry'))
    // Result: 'header footer'
    +
    const engine = new Liquid({
    +  templates: {
    +    'views/entry': 'header {% include "../partials/footer" %}',
    +    'partials/footer': 'footer'
    +  }
    +})
    +engine.renderFileSync('views/entry'))
    +// Result: 'header footer'

    Note that file system options like root, layouts, partials, relativeReference will be ignored when templates is specified.

    - +
    diff --git a/tutorials/render-tag-content.html b/tutorials/render-tag-content.html index 8f61ced69..1901ce872 100644 --- a/tutorials/render-tag-content.html +++ b/tutorials/render-tag-content.html @@ -94,10 +94,14 @@

    Custom tags can have content template and can be nested. This article describes how to implement custom tags that consists of a begin tag, an end tag, and template content between them.

    Render Tag Content

    We’ll start with a simple tag wrap which wraps its content into a <div class="wrapper"></div> element:

    -
    1
    2
    3
    {% wrap %}
    {{ "hello world!" | capitalize }}
    {% endwrap %}
    +
    {% wrap %}
    +  {{ "hello world!" | capitalize }}
    +{% endwrap %}

    Expected output:

    -
    1
    2
    3
    <div class='wrapper'>
    Hello world!
    </div>
    +
    <div class='wrapper'>
    +  Hello world!
    +</div>

    Firstly, register a tag with name wrap and parse the content into this.tpls. Here in parse(tagToken, remainTokens),

      @@ -105,25 +109,77 @@
    • remainTokens is an array of all tokens following {% wrap %} until the end of this template file.

    Basically, what we need to do is take/.shift() enough tags from remainTokens until we got a endwrap token (the name can be arbitrary, but in convention, we need it to be endwrap). And if there’s no endwrap until the end of template file, we need to throw an tag-not-closed Error.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    engine.registerTag('wrap', {
    parse(tagToken, remainTokens) {
    this.tpls = []
    let closed = false
    while(remainTokens.length) {
    let token = remainTokens.shift()
    // we got the end tag! stop taking tokens
    if (token.name === 'endwrap') {
    closed = true
    break
    }
    // parse token into template
    // parseToken() may consume more than 1 tokens
    // e.g. {% if %}...{% endif %}
    let tpl = this.liquid.parser.parseToken(token, remainTokens)
    this.tpls.push(tpl)
    }
    if (!closed) throw new Error(`tag ${tagToken.getText()} not closed`)
    },
    * render(context, emitter) {
    emitter.write("<div class='wrapper'>")
    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    emitter.write("</div>")
    }
    })
    +
    engine.registerTag('wrap', {
    +  parse(tagToken, remainTokens) {
    +    this.tpls = []
    +    let closed = false
    +    while(remainTokens.length) {
    +      let token = remainTokens.shift()
    +      // we got the end tag! stop taking tokens
    +      if (token.name === 'endwrap') {
    +        closed = true
    +        break
    +      }
    +      // parse token into template
    +      // parseToken() may consume more than 1 tokens
    +      // e.g. {% if %}...{% endif %}
    +      let tpl = this.liquid.parser.parseToken(token, remainTokens)
    +      this.tpls.push(tpl)
    +    }
    +    if (!closed) throw new Error(`tag ${tagToken.getText()} not closed`)
    +  },
    +  * render(context, emitter) {
    +    emitter.write("<div class='wrapper'>")
    +    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    +    emitter.write("</div>")
    +  }
    +})

    .renderTemplates() can be async, we need yield to wait it complete. More details on async in LiquidJS, please refer to Sync and Async. Other parts of render() method is quite straightforward. Here’s a JSFiddle version: https://jsfiddle.net/por0zcn1/3/

    Using ParseStream

    When it comes to complex tags like for and if, the parse() can be very complicated. There’s a ParseStream utility to organize the parse() in event-based style. Following is a re-written parse() using ParseStream and does exactly the same as above example.

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    parse(tagToken, remainTokens) {
    this.tpls = []
    this.liquid.parser.parseStream(remainTokens)
    .on('template', tpl => this.tpls.push(tpl))
    // note that we cannot use arrow function because we need `this`
    .on('tag:endwrap', function () { this.stop() })
    .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`) })
    .start()
    }
    +
    parse(tagToken, remainTokens) {
    +  this.tpls = []
    +  this.liquid.parser.parseStream(remainTokens)
    +    .on('template', tpl => this.tpls.push(tpl))
    +    // note that we cannot use arrow function because we need `this`
    +    .on('tag:endwrap', function () { this.stop() })
    +    .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`) })
    +    .start()
    +}

    Here’s a JSFiddle version: https://jsfiddle.net/por0zcn1/4/. For simplicity, the following examples are implemented using ParseStream.

    Manipulate the Context

    The wrap tag above doesn’t seem to be very useful, without using that tag we can render the content anyway. Now we’re going to implement a repeat tag to render the content 2 times (we can also add a parameter to render arbitrary times).

    -
    1
    2
    3
    {% repeat %}
    {{ repeat.i }}. {{ "hello world!" | capitalize }}
    {% endrepeat %}`
    +
    {% repeat %}
    +  {{ repeat.i }}. {{ "hello world!" | capitalize }}
    +{% endrepeat %}`

    Expected outputs:

    -
    1
    2
    1. Hello world!
    2. Hello world!
    +
    1. Hello world!
    +2. Hello world!

    As you’ve noticed, there’s an additional repeat.i in the context of repeat. That is implemented by manipulating the Context.

    Context

    Context defines the value of each variable in Liquid template. In LiquidJS, a Context consists of a stack of Scopes. A Scope is a plain object like the one specified in engine.render(tpl, scope).

    Each time we enter a new Context, we need to push a new Scope. And when we finish rendering and exit the Context, we pop the Scope from the Context. As you can see in the following implementation:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    engine.registerTag('repeat', {
    parse(tagToken, remainTokens) {
    this.tpls = []
    this.liquid.parser.parseStream(remainTokens)
    .on('template', tpl => this.tpls.push(tpl))
    .on('tag:endrepeat', function () { this.stop() })
    .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`) })
    .start()
    },
    * render(context, emitter) {
    const repeat = { i: 1 }
    context.push({ repeat })
    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    repeat.i++
    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    context.pop()
    }
    })
    +
    engine.registerTag('repeat', {
    +  parse(tagToken, remainTokens) {
    +    this.tpls = []
    +    this.liquid.parser.parseStream(remainTokens)
    +      .on('template', tpl => this.tpls.push(tpl))
    +      .on('tag:endrepeat', function () { this.stop() })
    +      .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`) })
    +      .start()
    +  },
    +  * render(context, emitter) {
    +    const repeat = { i: 1 }
    +    context.push({ repeat })
    +    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    +    repeat.i++
    +    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    +    context.pop()
    +  }
    +})

    The parse() is exactly the same as wrap tag, we repeat the content simply by calling .renderTemplates(this.tpls) twice during render(). Here’s the JSFiddle: https://jsfiddle.net/por0zcn1/2/

    Use Push & Pop in Pairs

    context.push() and context.pop() have to be used in pairs. Failing to pop() the Scope you pushed will leak the Scope to latter templates and may corrupt the Context stack.

    @@ -132,7 +188,7 @@
    - +
    diff --git a/tutorials/setup.html b/tutorials/setup.html index 65f117001..bf27b2b6f 100644 --- a/tutorials/setup.html +++ b/tutorials/setup.html @@ -94,21 +94,34 @@

    In case you’re not familiar with Liquid Template Language, see Introduction to Liquid Template Language.

    LiquidJS in Node.js

    Install via npm:

    -
    1
    npm install --save liquidjs
    +
    npm install --save liquidjs
    -
    1
    2
    3
    4
    5
    6
    var { Liquid } = require('liquidjs');
    var engine = new Liquid();

    engine
    .parseAndRender('{{name | capitalize}}', {name: 'alice'})
    .then(console.log); // outputs 'Alice'
    +
    var { Liquid } = require('liquidjs');
    +var engine = new Liquid();
    +
    +engine
    +    .parseAndRender('{{name | capitalize}}', {name: 'alice'})
    +    .then(console.log);     // outputs 'Alice'
    Working Demo

    Here’s a working demo for LiquidJS usage in Node.js: liquidjs/demo/nodejs/.

    Type definitions for LiquidJS are also exported and published, which makes it more enjoyable for TypeScript projects:

    -
    1
    2
    3
    4
    5
    6
    import { Liquid } from 'liquidjs';
    const engine = new Liquid();

    engine
    .parseAndRender('{{name | capitalize}}', {name: 'alice'})
    .then(console.log); // outputs 'Alice'
    +
    import { Liquid } from 'liquidjs';
    +const engine = new Liquid();
    +
    +engine
    +    .parseAndRender('{{name | capitalize}}', {name: 'alice'})
    +    .then(console.log);     // outputs 'Alice'
    Working Demo

    Here’s a working demo for LiquidJS usage in TypeScript: liquidjs/demo/typescript/.

    LiquidJS in Browsers

    Pre-built UMD bundles are also available:

    -
    1
    2
    3
    4
    <!--for production-->
    <script src="https://cdn.jsdelivr.net/npm/liquidjs/dist/liquid.browser.min.js"></script>
    <!--for development-->
    <script src="https://cdn.jsdelivr.net/npm/liquidjs/dist/liquid.browser.umd.js"></script>
    +
    <!--for production-->
    +<script src="https://cdn.jsdelivr.net/npm/liquidjs/dist/liquid.browser.min.js"></script>
    +<!--for development-->
    +<script src="https://cdn.jsdelivr.net/npm/liquidjs/dist/liquid.browser.umd.js"></script>
    Working Demo

    Here’s a living demo on jsFiddle: jsfiddle.net/pd4jhzLs/1/, and the source code is also available in liquidjs/demo/browser/.

    @@ -117,30 +130,32 @@

    LiquidJS in CLI

    LiquidJS can also be used to render a template directly from CLI using npx:

    -
    1
    npx liquidjs --template '{{"hello" | capitalize}}'
    +
    npx liquidjs --template '{{"hello" | capitalize}}'

    You can either pass the template inline (as shown above) or you can read it from a file by using the @ character followed by a path, like so:

    -
    1
    npx liquidjs --template @./some-template.liquid
    +
    npx liquidjs --template @./some-template.liquid

    You can also use the @- syntax to read the template from stdin:

    -
    1
    echo '{{"hello" | capitalize}}' | npx liquidjs --template @-
    +
    echo '{{"hello" | capitalize}}' | npx liquidjs --template @-

    A context can be passed in the same ways (i.e. inline, from a path or piped through stdin). The following three are equivalent:

    -
    1
    2
    3
    npx liquidjs --template 'Hello, {{ name }}!' --context '{"name": "Snake"}'
    npx liquidjs --template 'Hello, {{ name }}!' --context @./some-context.json
    echo '{"name": "Snake"}' | npx liquidjs --template 'Hello, {{ name }}!' --context @-
    +
    npx liquidjs --template 'Hello, {{ name }}!' --context '{"name": "Snake"}'
    +npx liquidjs --template 'Hello, {{ name }}!' --context @./some-context.json
    +echo '{"name": "Snake"}' | npx liquidjs --template 'Hello, {{ name }}!' --context @-

    Note that you can only use the stdin specifier @- for a single argument. If you try to use it for both --template and --context you will get an error.

    The rendered output is written to stdout by default, but you can also specify an output file (if the file exists, it will be overwritten):

    -
    1
    npx liquidjs --template '{{"hello" | capitalize}}' --output ./hello.txt
    +
    npx liquidjs --template '{{"hello" | capitalize}}' --output ./hello.txt

    You can also pass a number of options to customize template rendering behavior. For example, the --js-truthy option can be used to enable JavaScript truthiness:

    -
    1
    npx liquidjs --template @./some-template.liquid --js-truthy
    +
    npx liquidjs --template @./some-template.liquid --js-truthy

    Most of the options available through the JavaScript API are also available from the CLI. For help on available options, use npx liquidjs --help.

    Miscellaneous

    A ReactJS demo is also added by @stevenanthonyrevo, see liquidjs/demo/reactjs/.

    - +
    diff --git a/tutorials/sync-and-async.html b/tutorials/sync-and-async.html index 1f6b92d08..4e1ef0b6a 100644 --- a/tutorials/sync-and-async.html +++ b/tutorials/sync-and-async.html @@ -110,7 +110,21 @@
  • evalValueSync()
  • Implement Sync-Compatible Tags

    LiquidJS uses a generator-based async implementation to support both async and sync in one piece of tag implementation. For example, below UpperTag can be used in both engine.renderSync() and engine.render().

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid } from 'liquidjs'

    // Usage: {% upper "alice" %}
    // Output: ALICE
    engine.registerTag('upper', class UpperTag extends Tag {
    private value: Value
    constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    super(token, remainTokens, liquid)
    this.value = new Value(token.args, liquid)
    }
    * render (ctx: Context, emitter: Emitter) {
    const title = yield this.value.value(ctx)
    emitter.write(title.toUpperCase())
    }
    })
    +
    import { TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid } from 'liquidjs'
    +
    +// Usage: {% upper "alice" %}
    +// Output: ALICE
    +engine.registerTag('upper', class UpperTag extends Tag {
    +  private value: Value
    +  constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    +    super(token, remainTokens, liquid)
    +    this.value = new Value(token.args, liquid)
    +  }
    +  * render (ctx: Context, emitter: Emitter) {
    +    const title = yield this.value.value(ctx)
    +    emitter.write(title.toUpperCase())
    +  }
    +})

    All builtin tags are implemented this way and safe to use in both sync and async (I’ll call it sync-compatible). To make your custom tag sync-compatible, you’ll need to:

    Call APIs that return a Promise

    But LiquidJS is Promise-friendly, right? You can still call Promise-based functions and wait for that Promise within tag implementations. Just replace await with yield. e.g. we’re calling fs.readFile() which returns a Promise:

    -
    1
    2
    3
    4
    5
    * render (ctx: Context, emitter: Emitter) {
    const file = yield this.value.value(ctx)
    const title = yield fs.readFile(file, 'utf8')
    emitter.write(title.toUpperCase())
    }
    +
    * render (ctx: Context, emitter: Emitter) {
    +  const file = yield this.value.value(ctx)
    +  const title = yield fs.readFile(file, 'utf8')
    +  emitter.write(title.toUpperCase())
    +}

    Now that this * render() calls an API that returns a Promise, so it’s no longer sync-compatible.

    Non Sync-Compatible Tags

    Non sync-compatible tags are also valid tags, will work just fine for asynchronous API calls. When called synchronously, tags that return a Promise will be rendered as [object Promise].

    Convert LiquidJS async Generator to Promise

    You can convert a Generator to Promise by toPromise, for example:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid, toPromise } from 'liquidjs'

    // Usage: {% upper "alice" %}
    // Output: ALICE
    engine.registerTag('upper', class UpperTag extends Tag {
    private value: Value
    constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    super(token, remainTokens, liquid)
    this.value = new Value(token.args, liquid)
    }
    async render (ctx: Context, emitter: Emitter) {
    const title = await toPromise(this.value.value(ctx))
    emitter.write(title.toUpperCase())
    }
    })
    +
    import { TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid, toPromise } from 'liquidjs'
    +
    +// Usage: {% upper "alice" %}
    +// Output: ALICE
    +engine.registerTag('upper', class UpperTag extends Tag {
    +  private value: Value
    +  constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    +    super(token, remainTokens, liquid)
    +    this.value = new Value(token.args, liquid)
    +  }
    +  async render (ctx: Context, emitter: Emitter) {
    +    const title = await toPromise(this.value.value(ctx))
    +    emitter.write(title.toUpperCase())
    +  }
    +})

    Async only Tags

    If your tag is intend to be used only asynchronously, it can be declared as async render() so you can use await in its implementation directly:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { toPromise, TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid } from 'liquidjs'

    // Usage: {% upper "alice" %}
    // Output: ALICE
    engine.registerTag('upper', class UpperTag extends Tag {
    private value: Value
    constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    super(token, remainTokens, liquid)
    this.value = new Value(token.args, liquid)
    }
    async render (ctx: Context, emitter: Emitter) {
    const title = await toPromise(this.value.value(ctx))
    emitter.write(`<h1>${title}</h1>`)
    }
    })
    +
    import { toPromise, TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid } from 'liquidjs'
    +
    +// Usage: {% upper "alice" %}
    +// Output: ALICE
    +engine.registerTag('upper', class UpperTag extends Tag {
    +  private value: Value
    +  constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    +    super(token, remainTokens, liquid)
    +    this.value = new Value(token.args, liquid)
    +  }
    +  async render (ctx: Context, emitter: Emitter) {
    +    const title = await toPromise(this.value.value(ctx))
    +    emitter.write(`<h1>${title}</h1>`)
    +  }
    +})
    - +
    diff --git a/tutorials/truthy-and-falsy.html b/tutorials/truthy-and-falsy.html index 6fdbe47a8..2da561120 100644 --- a/tutorials/truthy-and-falsy.html +++ b/tutorials/truthy-and-falsy.html @@ -226,7 +226,7 @@
    - +
    diff --git a/tutorials/use-in-expressjs.html b/tutorials/use-in-expressjs.html index f649447f3..d12d560b7 100644 --- a/tutorials/use-in-expressjs.html +++ b/tutorials/use-in-expressjs.html @@ -93,27 +93,48 @@

    LiquidJS is compatible to the express template engines. You can set liquidjs instance to the view engine option:

    -
    1
    2
    3
    4
    5
    6
    7
    var { Liquid } = require('liquidjs');
    var engine = new Liquid();

    // register liquid engine
    app.engine('liquid', engine.express());
    app.set('views', './views'); // specify the views directory
    app.set('view engine', 'liquid'); // set liquid to default
    +
    var { Liquid } = require('liquidjs');
    +var engine = new Liquid();
    +
    +// register liquid engine
    +app.engine('liquid', engine.express()); 
    +app.set('views', './views');            // specify the views directory
    +app.set('view engine', 'liquid');       // set liquid to default
    Working Demo

    Here’s a working demo for LiquidJS usage in Express.js: liquidjs/demo/express/.

    Template Lookup

    The root option will continue to work as templates root, as you can see in Render A Template File. Additionally, the views option in express.js (as shown above) will also be respected. Say you have a template directory like:

    -
    1
    2
    3
    4
    5
    .
    ├── views1/
    │ └── hello.liquid
    └── views2/
    └── world.liquid
    +
    .
    +├── views1/
    +│ └── hello.liquid
    +└── views2/
    +  └── world.liquid

    And you’re setting template root for liquidjs to views1 and expressjs to views2:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    var { Liquid } = require('liquidjs');
    var engine = new Liquid({
    root: './views1/'
    });

    app.engine('liquid', engine.express());
    app.set('views', './views2'); // specify the views directory
    app.set('view engine', 'liquid'); // set liquid to default
    +
    var { Liquid } = require('liquidjs');
    +var engine = new Liquid({
    +    root: './views1/'
    +});
    +
    +app.engine('liquid', engine.express()); 
    +app.set('views', './views2');            // specify the views directory
    +app.set('view engine', 'liquid');       // set liquid to default

    Both of hello.liquid and world.liquid can be resolved and rendered:

    -
    1
    2
    res.render('hello')
    res.render('world')
    +
    res.render('hello')
    +res.render('world')

    Caching

    Simply setting the cache option to true will enable template caching, as explained in Caching. It’s recommended to enable cache in production environment, which can be done by:

    -
    1
    2
    3
    4
    var { Liquid } = require('liquidjs');
    var engine = new Liquid({
    cache: process.env.NODE_ENV === 'production'
    });
    +
    var { Liquid } = require('liquidjs');
    +var engine = new Liquid({
    +    cache: process.env.NODE_ENV === 'production'
    +});
    - +
    diff --git a/tutorials/whitespace-control.html b/tutorials/whitespace-control.html index 3373346db..40dbf4100 100644 --- a/tutorials/whitespace-control.html +++ b/tutorials/whitespace-control.html @@ -94,16 +94,19 @@

    To keep source code neat and indented, we’re adding spaces to our templates. LiquidJS offers whitespace control capabilities to eliminate these unwanted whitespaces in output HTML.

    via Markups

    By default, all tags and output markups lines will generate a NL (\n), and whitespaces if there’s any indentation. For example:

    -
    1
    2
    {%  author = "harttle" %}
    {{ author }}
    +
    {%  author = "harttle" %}
    +{{ author }}

    Outputs (note the blank link):

    -
    1
    2

    harttle
    +
    
    +harttle

    We can include hyphens in your tag syntax ({{-, -}}, {%-, -%}) to strip whitespace from left or right. For example:

    -
    1
    2
    {% assign author = "harttle" -%}
    {{ author }}
    +
    {% assign author = "harttle" -%}
    +{{ author }}

    Outputs:

    -
    1
    harttle
    +
    harttle

    In this case, the -%} strips the whitespace from the right side of the assign tag.

    via Options

    Alternatively, LiquidJS provides these per engine options to enable whitespace control without sweeping changes of your templates:

    @@ -118,7 +121,7 @@
    - +
    diff --git a/zh-cn/filters/abs.html b/zh-cn/filters/abs.html index 0232b67df..643339ada 100644 --- a/zh-cn/filters/abs.html +++ b/zh-cn/filters/abs.html @@ -96,27 +96,27 @@

    返回数字的绝对值。

    输入

    -
    1
    {{ -17 | abs }}
    +
    {{ -17 | abs }}

    输出

    -
    1
    17
    +
    17

    输入

    -
    1
    {{ 4 | abs }}
    +
    {{ 4 | abs }}

    输出

    -
    1
    4
    +
    4

    对于只包含数字的字符串也好使:

    输入

    -
    1
    {{ "-19.86" | abs }}
    +
    {{ "-19.86" | abs }}

    输出

    -
    1
    19.86
    +
    19.86
    diff --git a/zh-cn/filters/append.html b/zh-cn/filters/append.html index 9f8061c47..11b6adf5a 100644 --- a/zh-cn/filters/append.html +++ b/zh-cn/filters/append.html @@ -96,21 +96,23 @@

    连接两个字符串并返回结果。

    输入

    -
    1
    {{ "/my/fancy/url" | append: ".html" }}
    +
    {{ "/my/fancy/url" | append: ".html" }}

    输出

    -
    1
    /my/fancy/url.html
    +
    /my/fancy/url.html

    也可以用于变量。

    输入

    -
    1
    2
    {% assign filename = "/index.html" %}
    {{ "website.com" | append: filename }}
    +
    {% assign filename = "/index.html" %}
    +{{ "website.com" | append: filename }}

    输出

    -
    1
    2

    website.com/index.html
    +
    
    +website.com/index.html
    diff --git a/zh-cn/filters/array_to_sentence_string.html b/zh-cn/filters/array_to_sentence_string.html index 70eaddf9f..55d1652bd 100644 --- a/zh-cn/filters/array_to_sentence_string.html +++ b/zh-cn/filters/array_to_sentence_string.html @@ -96,20 +96,20 @@

    把数组转化为句子,用于做标签列表。有一个可选的连接词参数。

    输入

    -
    1
    {{ "foo,bar,baz" | split: "," | array_to_sentence_string }}
    +
    {{ "foo,bar,baz" | split: "," | array_to_sentence_string }}

    输出

    -
    1
    foo, bar, and baz
    +
    foo, bar, and baz

    输入

    -
    1
    {{ "foo,bar,baz" | split: "," | array_to_sentence_string: "or" }}
    +
    {{ "foo,bar,baz" | split: "," | array_to_sentence_string: "or" }}

    输出

    -
    1
    foo, bar, or baz
    +
    foo, bar, or baz
    diff --git a/zh-cn/filters/at_least.html b/zh-cn/filters/at_least.html index adfd08387..cd61d6f87 100644 --- a/zh-cn/filters/at_least.html +++ b/zh-cn/filters/at_least.html @@ -96,20 +96,20 @@

    限制数字到某个最小值。

    输入

    -
    1
    {{ 4 | at_least: 5 }}
    +
    {{ 4 | at_least: 5 }}

    输出

    -
    1
    5
    +
    5

    输入

    -
    1
    {{ 4 | at_least: 3 }}
    +
    {{ 4 | at_least: 3 }}

    输出

    -
    1
    4
    +
    4
    diff --git a/zh-cn/filters/at_most.html b/zh-cn/filters/at_most.html index 6831f5cb4..e4d0c92f3 100644 --- a/zh-cn/filters/at_most.html +++ b/zh-cn/filters/at_most.html @@ -96,20 +96,20 @@

    限制数字到某个最大值。

    输入

    -
    1
    {{ 4 | at_most: 5 }}
    +
    {{ 4 | at_most: 5 }}

    输出

    -
    1
    4
    +
    4

    输入

    -
    1
    {{ 4 | at_most: 3 }}
    +
    {{ 4 | at_most: 3 }}

    输出

    -
    1
    3
    +
    3
    diff --git a/zh-cn/filters/capitalize.html b/zh-cn/filters/capitalize.html index 985806c77..5bdaef5ee 100644 --- a/zh-cn/filters/capitalize.html +++ b/zh-cn/filters/capitalize.html @@ -96,21 +96,21 @@

    把字符串首字母改为大写。

    输入

    -
    1
    {{ "title" | capitalize }}
    +
    {{ "title" | capitalize }}

    输出

    -
    1
    Title
    +
    Title

    capitalize 只会大写首字母,因此后续单词的不会受影响:

    Input

    -
    1
    {{ "my great title" | capitalize }}
    +
    {{ "my great title" | capitalize }}

    输出

    -
    1
    My great title
    +
    My great title
    diff --git a/zh-cn/filters/ceil.html b/zh-cn/filters/ceil.html index 1b52d0476..35bd13f66 100644 --- a/zh-cn/filters/ceil.html +++ b/zh-cn/filters/ceil.html @@ -96,33 +96,33 @@

    向上取整,取整前 LiquidJS 会首先把输入转换为数字。

    输入

    -
    1
    {{ 1.2 | ceil }}
    +
    {{ 1.2 | ceil }}

    输出

    -
    1
    2
    +
    2

    输入

    -
    1
    {{ 2.0 | ceil }}
    +
    {{ 2.0 | ceil }}

    输出

    -
    1
    2
    +
    2

    输入

    -
    1
    {{ 183.357 | ceil }}
    +
    {{ 183.357 | ceil }}

    输出

    -
    1
    184
    +
    184

    下面的例子中输入是字符串:

    输入

    -
    1
    {{ "3.5" | ceil }}
    +
    {{ "3.5" | ceil }}

    输出

    -
    1
    4
    +
    4
    diff --git a/zh-cn/filters/cgi_escape.html b/zh-cn/filters/cgi_escape.html index dcc0b87a9..b3f405c31 100644 --- a/zh-cn/filters/cgi_escape.html +++ b/zh-cn/filters/cgi_escape.html @@ -96,14 +96,14 @@

    把字符串 CGI 转义,用于 URL。用对应的 %XX 替换特殊字符,空格会被转义为 + 号。

    输入

    -
    1
    {{ "foo, bar; baz?" | cgi_escape }}
    +
    {{ "foo, bar; baz?" | cgi_escape }}

    输出

    -
    1
    foo%2C+bar%3B+baz%3F
    +
    foo%2C+bar%3B+baz%3F
    diff --git a/zh-cn/filters/compact.html b/zh-cn/filters/compact.html index dfbf436bb..0f4055ba6 100644 --- a/zh-cn/filters/compact.html +++ b/zh-cn/filters/compact.html @@ -97,21 +97,39 @@

    从数组里移除任何 nullundefined 值。

    假设 site.pages 是网页列表,有些网页包含 category 属性用来标明类别。如果把它们 map 到数组里,那么对于没有 category 属性的元素就会是 undefined

    输入

    -
    1
    2
    3
    4
    5
    {% assign site_categories = site.pages | map: "category" %}

    {% for category in site_categories %}
    - {{ category }}
    {% endfor %}
    +
    {% assign site_categories = site.pages | map: "category" %}
    +
    +{% for category in site_categories %}
    +- {{ category }}
    +{% endfor %}

    输出

    -
    1
    2
    3
    4
    5
    6
    7
    - business
    - celebrities
    -
    - lifestyle
    - sports
    -
    - technology
    +
    - business
    +- celebrities
    +-
    +- lifestyle
    +- sports
    +-
    +- technology

    使用 compact 创建 site_categories 数组,可以移除所有 nullundefined 值。

    输入

    -
    1
    2
    3
    4
    5
    {% assign site_categories = site.pages | map: "category" | compact %}

    {% for category in site_categories %}
    - {{ category }}
    {% endfor %}
    +
    {% assign site_categories = site.pages | map: "category" | compact %}
    +
    +{% for category in site_categories %}
    +- {{ category }}
    +{% endfor %}

    输出

    -
    1
    2
    3
    4
    5
    - business
    - celebrities
    - lifestyle
    - sports
    - technology
    +
    - business
    +- celebrities
    +- lifestyle
    +- sports
    +- technology
    diff --git a/zh-cn/filters/concat.html b/zh-cn/filters/concat.html index ecca9fe20..b879eda52 100644 --- a/zh-cn/filters/concat.html +++ b/zh-cn/filters/concat.html @@ -96,21 +96,47 @@

    连接多个数组,返回的数组包含所有传入数组的元素。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    8
    {% assign fruits = "apples, oranges, peaches" | split: ", " %}
    {% assign vegetables = "carrots, turnips, potatoes" | split: ", " %}

    {% assign everything = fruits | concat: vegetables %}

    {% for item in everything %}
    - {{ item }}
    {% endfor %}
    +
    {% assign fruits = "apples, oranges, peaches" | split: ", " %}
    +{% assign vegetables = "carrots, turnips, potatoes" | split: ", " %}
    +
    +{% assign everything = fruits | concat: vegetables %}
    +
    +{% for item in everything %}
    +- {{ item }}
    +{% endfor %}

    输出

    -
    1
    2
    3
    4
    5
    6
    - apples
    - oranges
    - peaches
    - carrots
    - turnips
    - potatoes
    +
    - apples
    +- oranges
    +- peaches
    +- carrots
    +- turnips
    +- potatoes

    可以链式地使用 concat 过滤器来连接多个数组:

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    {% assign furniture = "chairs, tables, shelves" | split: ", " %}

    {% assign everything = fruits | concat: vegetables | concat: furniture %}

    {% for item in everything %}
    - {{ item }}
    {% endfor %}
    +
    {% assign furniture = "chairs, tables, shelves" | split: ", " %}
    +
    +{% assign everything = fruits | concat: vegetables | concat: furniture %}
    +
    +{% for item in everything %}
    +- {{ item }}
    +{% endfor %}

    输出

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    - apples
    - oranges
    - peaches
    - carrots
    - turnips
    - potatoes
    - chairs
    - tables
    - shelves
    +
    - apples
    +- oranges
    +- peaches
    +- carrots
    +- turnips
    +- potatoes
    +- chairs
    +- tables
    +- shelves
    diff --git a/zh-cn/filters/date.html b/zh-cn/filters/date.html index cdb8d0b2d..9a42207d1 100644 --- a/zh-cn/filters/date.html +++ b/zh-cn/filters/date.html @@ -107,44 +107,46 @@

    输入

    -
    1
    {{ article.published_at | date: "%a, %b %d, %y" }}
    +
    {{ article.published_at | date: "%a, %b %d, %y" }}

    输出

    -
    1
    Fri, Jul 17, 15
    +
    Fri, Jul 17, 15
    时区

    日期在输出时会转换为当地时区,设置 timezoneOffset LiquidJS 参数可以指定一个不同的时区。或者设置 preserveTimezonestrue 来保持字面量时间戳的时区,数据中的日期对象不受此参数的影响。

    你也可以在使用 date 时再设置时区:

    输入

    -
    1
    2
    {{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", 360}} // 等价于设置 `options.timezoneOffset` to `360`.
    {{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", "Asia/Colombo" }}
    +
    {{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", 360}} // 等价于设置 `options.timezoneOffset` to `360`.
    +{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", "Asia/Colombo" }}

    输出

    -
    1
    2
    1990-12-31T17:00:00
    1991-01-01T04:30:00
    +
    1990-12-31T17:00:00
    +1991-01-01T04:30:00

    输入

    -
    1
    {{ article.published_at | date: "%Y" }}
    +
    {{ article.published_at | date: "%Y" }}

    输出

    -
    1
    2015
    +
    2015

    输入也可以是符合 JavaScript Date 格式的字符串::

    输入

    -
    1
    {{ "March 14, 2016" | date: "%b %d, %y" }}
    +
    {{ "March 14, 2016" | date: "%b %d, %y" }}

    输出

    -
    1
    Mar 14, 16
    +
    Mar 14, 16
    时间戳字符串

    LiquidJS 使用 JavaScript [Date][newDate] 来解析输入字符串,意味着支持 IETF-compliant RFC 2822 时间戳特定版本的 ISO8601

    可以用特殊值 "now"(或"today")来获取当前时间:

    输入

    -
    1
    This page was last updated at {{ "now" | date: "%Y-%m-%d %H:%M" }}.
    +
    This page was last updated at {{ "now" | date: "%Y-%m-%d %H:%M" }}.

    输出

    -
    1
    This page was last updated at 2020-03-25 15:57.
    +
    This page was last updated at 2020-03-25 15:57.
    当前时间

    注意得到的当前时间是模板渲染时的时间,如果你在用静态站点生成器或者模板有被缓存这一时间可能与用户看到的时间不同。

    @@ -152,7 +154,7 @@ diff --git a/zh-cn/filters/date_to_long_string.html b/zh-cn/filters/date_to_long_string.html index 9e58fee80..aed439fbd 100644 --- a/zh-cn/filters/date_to_long_string.html +++ b/zh-cn/filters/date_to_long_string.html @@ -96,23 +96,23 @@

    把日期转换为长格式(只支持 US/UK 两种),与 Jekyll 的 date_to_long_string 过滤器一样。

    输入

    -
    1
    {{ site.time | date_to_long_string }}
    +
    {{ site.time | date_to_long_string }}

    输出

    -
    1
    07 November 2008
    +
    07 November 2008

    输入

    -
    1
    {{ site.time | date_to_long_string: "ordinal" }}
    +
    {{ site.time | date_to_long_string: "ordinal" }}

    输出

    -
    1
    7th November 2008
    +
    7th November 2008

    注意 JavaScript Date 没有时区信息,详情请参考 date 过滤器。

    diff --git a/zh-cn/filters/date_to_rfc822.html b/zh-cn/filters/date_to_rfc822.html index 8e2189884..fcd0d8cf4 100644 --- a/zh-cn/filters/date_to_rfc822.html +++ b/zh-cn/filters/date_to_rfc822.html @@ -96,16 +96,16 @@

    把日期转换为 RFC-822 格式用于 RSS feed,与 Jekyll 的 date_to_rfc822 过滤器一样。

    输入

    -
    1
    {{ site.time | date_to_rfc822 }}
    +
    {{ site.time | date_to_rfc822 }}

    输入

    -
    1
    Mon, 07 Nov 2008 13:07:54 -0800
    +
    Mon, 07 Nov 2008 13:07:54 -0800

    注意 JavaScript Date 没有时区信息,详情请参考 date 过滤器。

    diff --git a/zh-cn/filters/date_to_string.html b/zh-cn/filters/date_to_string.html index 631104f63..f5e3746ce 100644 --- a/zh-cn/filters/date_to_string.html +++ b/zh-cn/filters/date_to_string.html @@ -96,22 +96,22 @@

    把日期转换为短格式(只支持 US/UK 两种),与 Jekyll 的 date_to_string 过滤器一样。

    输入

    -
    1
    {{ site.time | date_to_string }}
    +
    {{ site.time | date_to_string }}

    输出

    -
    1
    07 Nov 2008
    +
    07 Nov 2008

    输入

    -
    1
    {{ site.time | date_to_string: "ordinal", "US" }}
    +
    {{ site.time | date_to_string: "ordinal", "US" }}

    输出

    -
    1
    Nov 7th, 2008
    +
    Nov 7th, 2008

    注意 JavaScript Date 没有时区信息,详情请参考 date 过滤器。

    diff --git a/zh-cn/filters/date_to_xmlschema.html b/zh-cn/filters/date_to_xmlschema.html index b4b2056ba..a04f735dc 100644 --- a/zh-cn/filters/date_to_xmlschema.html +++ b/zh-cn/filters/date_to_xmlschema.html @@ -96,16 +96,16 @@

    把日期转换为 XML Schema (ISO 8601) 格式,与 Jekyll 的 date_to_xmlschema 过滤器一样。

    输入

    -
    1
    {{ site.time | date_to_xmlschema }}
    +
    {{ site.time | date_to_xmlschema }}

    输出

    -
    1
    2008-11-07T13:07:54-08:00
    +
    2008-11-07T13:07:54-08:00

    注意 JavaScript Date 没有时区信息,详情请参考 date 过滤器。

    diff --git a/zh-cn/filters/default.html b/zh-cn/filters/default.html index 69850d0c9..c7ea0f0c8 100644 --- a/zh-cn/filters/default.html +++ b/zh-cn/filters/default.html @@ -96,38 +96,41 @@

    在值不存在时给一个默认值,如果左侧是 falsy 或空(stringArray)就会使用这个默认值。下面的例子中 product_price 没有定义,因此使用了默认值。

    输入

    -
    1
    {{ product_price | default: 2.99 }}
    +
    {{ product_price | default: 2.99 }}

    输出

    -
    1
    2.99
    +
    2.99

    下面的例子中定义了 product_price 所以没有使用默认值。

    输入

    -
    1
    2
    {% assign product_price = 4.99 %}
    {{ product_price | default: 2.99 }}
    +
    {% assign product_price = 4.99 %}
    +{{ product_price | default: 2.99 }}

    输出

    -
    1
    4.99
    +
    4.99

    下面例子中 product_price 为空,所以使用了默认值。

    输入

    -
    1
    2
    {% assign product_price = "" %}
    {{ product_price | default: 2.99 }}
    +
    {% assign product_price = "" %}
    +{{ product_price | default: 2.99 }}

    输出

    -
    1
    2.99
    +
    2.99

    允许 false

    v9.32.0

    为了允许让 false 直接输出而不是用默认值,可以用 allow_false 参数。

    输入

    -
    1
    2
    {% assign display_price = false %}
    {{ display_price | default: true, allow_false: true }}
    +
    {% assign display_price = false %}
    +{{ display_price | default: true, allow_false: true }}

    输出

    -
    1
    false
    +
    false
    diff --git a/zh-cn/filters/divided_by.html b/zh-cn/filters/divided_by.html index 896877476..1c448aa70 100644 --- a/zh-cn/filters/divided_by.html +++ b/zh-cn/filters/divided_by.html @@ -96,31 +96,32 @@

    两数相除返回商,返回结果数字在 JavaScript 中 .toString() 得到的字符串。

    输入

    -
    1
    {{ 16 | divided_by: 4 }}
    +
    {{ 16 | divided_by: 4 }}

    输出

    -
    1
    4
    +
    4

    输入

    -
    1
    {{ 5 | divided_by: 3 }}
    +
    {{ 5 | divided_by: 3 }}

    输出

    -
    1
    1.6666666666666667
    +
    1.6666666666666667

    在 JavaScript 里数字没有浮点和整数的区分,它们的类型都是 number

    -
    1
    2
    // always true
    5.0 === 5
    +
    // always true
    +5.0 === 5

    因此如果需要做整数运算,需要传入额外的 integerArithmetic 参数:

    Input

    -
    1
    {{ 5 | divided_by: 3, true }}
    +
    {{ 5 | divided_by: 3, true }}

    Output

    -
    1
    1
    +
    1
    diff --git a/zh-cn/filters/downcase.html b/zh-cn/filters/downcase.html index 89a30f700..d410ad938 100644 --- a/zh-cn/filters/downcase.html +++ b/zh-cn/filters/downcase.html @@ -96,20 +96,20 @@

    字符串中每个字符都转为小写,对已经是小写的字符没有影响。

    输入

    -
    1
    {{ "Parker Moore" | downcase }}
    +
    {{ "Parker Moore" | downcase }}

    输出

    -
    1
    parker moore
    +
    parker moore

    输入

    -
    1
    {{ "apple" | downcase }}
    +
    {{ "apple" | downcase }}

    输出

    -
    1
    apple
    +
    apple
    diff --git a/zh-cn/filters/escape.html b/zh-cn/filters/escape.html index f764ad4f8..36133b748 100644 --- a/zh-cn/filters/escape.html +++ b/zh-cn/filters/escape.html @@ -96,7 +96,7 @@

    把字符串中的 HTML 特殊字符转义,对不需要转义的字符串不会产生影响。

    输入

    -
    1
    {{ "Have you read 'James & the Giant Peach'?" | escape }}
    +
    {{ "Have you read 'James & the Giant Peach'?" | escape }}

    输出

    @@ -104,14 +104,14 @@ Have you read &#39;James &amp; the Giant Peach&#39;?
     

    输入

    -
    1
    {{ "Tetsuro Takara" | escape }}
    +
    {{ "Tetsuro Takara" | escape }}

    输出

    -
    1
    Tetsuro Takara
    +
    Tetsuro Takara
    diff --git a/zh-cn/filters/escape_once.html b/zh-cn/filters/escape_once.html index 47cc507e1..a4f3b3d79 100644 --- a/zh-cn/filters/escape_once.html +++ b/zh-cn/filters/escape_once.html @@ -96,7 +96,7 @@

    把字符串中的特殊字符转义得到可用在 URL 里的字符串,对已经转义过的字符串和不需要转义的字符串不会产生影响。

    输入

    -
    1
    {{ "1 < 2 & 3" | escape_once }}
    +
    {{ "1 < 2 & 3" | escape_once }}

    输出

    @@ -115,7 +115,7 @@
     
                   
                   
                 
    diff --git a/zh-cn/filters/find.html b/zh-cn/filters/find.html
    index 010b1b6c3..e1df4eea3 100644
    --- a/zh-cn/filters/find.html
    +++ b/zh-cn/filters/find.html
    @@ -95,17 +95,21 @@
                     

    v10.11.0

    在数组中找到给定的属性为给定的值的第一个元素并返回;如果没有这样的元素则返回 nil。对于 members 数组:

    -
    1
    2
    3
    4
    5
    const members = [
    { graduation_year: 2013, name: 'Jay' },
    { graduation_year: 2014, name: 'John' },
    { graduation_year: 2014, name: 'Jack' }
    ]
    +
    const members = [
    +  { graduation_year: 2013, name: 'Jay' },
    +  { graduation_year: 2014, name: 'John' },
    +  { graduation_year: 2014, name: 'Jack' }
    +]

    输入

    -
    1
    {{ members | find: "graduation_year", 2014 | json }}
    +
    {{ members | find: "graduation_year", 2014 | json }}

    输出

    -
    1
    {"graduation_year":2014,"name":"John"}
    +
    {"graduation_year":2014,"name":"John"}
    diff --git a/zh-cn/filters/find_exp.html b/zh-cn/filters/find_exp.html index 1e63bbf7c..a38e454ca 100644 --- a/zh-cn/filters/find_exp.html +++ b/zh-cn/filters/find_exp.html @@ -95,17 +95,21 @@

    v10.11.0

    找到数组中给定的表达式值为 true 的第一个元素,如果没有这样的元素则返回 nil。对于下面的 members 数组:

    -
    1
    2
    3
    4
    5
    const members = [
    { graduation_year: 2013, name: 'Jay' },
    { graduation_year: 2014, name: 'John' },
    { graduation_year: 2014, name: 'Jack' }
    ]
    +
    const members = [
    +  { graduation_year: 2013, name: 'Jay' },
    +  { graduation_year: 2014, name: 'John' },
    +  { graduation_year: 2014, name: 'Jack' }
    +]

    输入

    -
    1
    {{ members | find_exp: "item", "item.graduation_year == 2014" | json }}
    +
    {{ members | find_exp: "item", "item.graduation_year == 2014" | json }}

    输出

    -
    1
    {"graduation_year":2014,"name":"John"}
    +
    {"graduation_year":2014,"name":"John"}
    diff --git a/zh-cn/filters/first.html b/zh-cn/filters/first.html index f7f8f899e..2503b41b7 100644 --- a/zh-cn/filters/first.html +++ b/zh-cn/filters/first.html @@ -96,23 +96,27 @@

    返回数组的第一个元素。

    输入

    -
    1
    {{ "Ground control to Major Tom." | split: " " | first }}
    +
    {{ "Ground control to Major Tom." | split: " " | first }}

    输出

    -
    1
    Ground
    +
    Ground

    输入

    -
    1
    2
    {% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
    {{ my_array.first }}
    +
    {% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
    +{{ my_array.first }}

    输出

    -
    1
    2

    zebra
    +
    
    +zebra

    需要在标签中使用的时候,可以用点来计算 first

    -
    1
    2
    3
    {% if my_array.first == "zebra" %}
    Here comes a zebra!
    {% endif %}
    +
    {% if my_array.first == "zebra" %}
    +  Here comes a zebra!
    +{% endif %}
    diff --git a/zh-cn/filters/floor.html b/zh-cn/filters/floor.html index 14c08d3cd..76f4cda2c 100644 --- a/zh-cn/filters/floor.html +++ b/zh-cn/filters/floor.html @@ -96,33 +96,33 @@

    数字下取整,LiquidJS 会尝试把输入转换为数字再做下取整操作。

    输入

    -
    1
    {{ 1.2 | floor }}
    +
    {{ 1.2 | floor }}

    输出

    -
    1
    1
    +
    1

    输入

    -
    1
    {{ 2.0 | floor }}
    +
    {{ 2.0 | floor }}

    输出

    -
    1
    2
    +
    2

    输入

    -
    1
    {{ 183.357 | floor }}
    +
    {{ 183.357 | floor }}

    输出

    -
    1
    183
    +
    183

    下面的例子中输入是个数字:

    输入

    -
    1
    {{ "3.5" | floor }}
    +
    {{ "3.5" | floor }}

    输出

    -
    1
    3
    +
    3
    diff --git a/zh-cn/filters/group_by.html b/zh-cn/filters/group_by.html index f560cac03..6704ddb8e 100644 --- a/zh-cn/filters/group_by.html +++ b/zh-cn/filters/group_by.html @@ -95,17 +95,44 @@

    v10.11.0

    把数组元素按照给定的属性的值分组。对于 members 数组:

    -
    1
    2
    3
    4
    5
    const members = [
    { graduation_year: 2003, name: 'Jay' },
    { graduation_year: 2003, name: 'John' },
    { graduation_year: 2004, name: 'Jack' }
    ]
    +
    const members = [
    +  { graduation_year: 2003, name: 'Jay' },
    +  { graduation_year: 2003, name: 'John' },
    +  { graduation_year: 2004, name: 'Jack' }
    +]

    输入

    -
    1
    {{ members | group_by: "graduation_year" | json: 2 }}
    +
    {{ members | group_by: "graduation_year" | json: 2 }}

    输出

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    [
    {
    "name": 2003,
    "items": [
    {
    "graduation_year": 2003,
    "name": "Jay"
    },
    {
    "graduation_year": 2003,
    "name": "John"
    }
    ]
    },
    {
    "name": 2004,
    "items": [
    {
    "graduation_year": 2004,
    "name": "Jack"
    }
    ]
    }
    ]
    +
    [
    +  {
    +    "name": 2003,
    +    "items": [
    +      {
    +        "graduation_year": 2003,
    +        "name": "Jay"
    +      },
    +      {
    +        "graduation_year": 2003,
    +        "name": "John"
    +      }
    +    ]
    +  },
    +  {
    +    "name": 2004,
    +    "items": [
    +      {
    +        "graduation_year": 2004,
    +        "name": "Jack"
    +      }
    +    ]
    +  }
    +]
    diff --git a/zh-cn/filters/group_by_exp.html b/zh-cn/filters/group_by_exp.html index 8be2a5a3b..a39e45255 100644 --- a/zh-cn/filters/group_by_exp.html +++ b/zh-cn/filters/group_by_exp.html @@ -95,17 +95,44 @@

    v10.11.0

    把数组元素按照给定的 Liquid 表达式的值分组。对于 members 数组:

    -
    1
    2
    3
    4
    5
    const members = [
    { graduation_year: 2013, name: 'Jay' },
    { graduation_year: 2014, name: 'John' },
    { graduation_year: 2009, name: 'Jack' }
    ]
    +
    const members = [
    +  { graduation_year: 2013, name: 'Jay' },
    +  { graduation_year: 2014, name: 'John' },
    +  { graduation_year: 2009, name: 'Jack' }
    +]

    输入

    -
    1
    {{ members | group_by_exp: "item", "item.graduation_year | truncate: 3, ''" | json: 2 }}
    +
    {{ members | group_by_exp: "item", "item.graduation_year | truncate: 3, ''" | json: 2 }}

    输出

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    [
    {
    "name": "201",
    "items": [
    {
    "graduation_year": 2013,
    "name": "Jay"
    },
    {
    "graduation_year": 2014,
    "name": "John"
    }
    ]
    },
    {
    "name": "200",
    "items": [
    {
    "graduation_year": 2009,
    "name": "Jack"
    }
    ]
    }
    ]
    +
    [
    +  {
    +    "name": "201",
    +    "items": [
    +      {
    +        "graduation_year": 2013,
    +        "name": "Jay"
    +      },
    +      {
    +        "graduation_year": 2014,
    +        "name": "John"
    +      }
    +    ]
    +  },
    +  {
    +    "name": "200",
    +    "items": [
    +      {
    +        "graduation_year": 2009,
    +        "name": "Jack"
    +      }
    +    ]
    +  }
    +]
    diff --git a/zh-cn/filters/inspect.html b/zh-cn/filters/inspect.html index 14c4472a6..c40109225 100644 --- a/zh-cn/filters/inspect.html +++ b/zh-cn/filters/inspect.html @@ -95,24 +95,31 @@

    v10.13.0

    类似于 json,但可以处理循环引用的情况。例如对于上下文:

    -
    1
    2
    3
    4
    5
    const foo = {
    bar: 'BAR'
    }
    foo.foo = foo
    const scope = { foo }
    +
    const foo = {
    +    bar: 'BAR'
    +}
    +foo.foo = foo
    +const scope = { foo }

    输入

    -
    1
    {% foo | inspect %}
    +
    {% foo | inspect %}

    输出

    -
    1
    {"bar":"BAR","foo":"[Circular]"}
    +
    {"bar":"BAR","foo":"[Circular]"}

    格式化

    可以指定一个 space 参数来缩进长度。

    输入

    -
    1
    {{ foo | inspect: 4 }}
    +
    {{ foo | inspect: 4 }}

    输出

    -
    1
    2
    3
    4
    {
    "bar": "BAR",
    "foo": "[Circular]"
    }
    +
    {
    +    "bar": "BAR",
    +    "foo": "[Circular]"
    +}
    diff --git a/zh-cn/filters/join.html b/zh-cn/filters/join.html index 5160a884a..9e18dbd56 100644 --- a/zh-cn/filters/join.html +++ b/zh-cn/filters/join.html @@ -96,14 +96,16 @@

    把数组中的元素连接成为一个字符串,以传入的参数作为分隔符。

    输入

    -
    1
    2
    {% assign beatles = "John, Paul, George, Ringo" | split: ", " %}
    {{ beatles | join: " and " }}
    +
    {% assign beatles = "John, Paul, George, Ringo" | split: ", " %}
    +{{ beatles | join: " and " }}

    输出

    -
    1
    2

    John and Paul and George and Ringo
    +
    
    +John and Paul and George and Ringo
    diff --git a/zh-cn/filters/json.html b/zh-cn/filters/json.html index 72723c090..90db50374 100644 --- a/zh-cn/filters/json.html +++ b/zh-cn/filters/json.html @@ -96,23 +96,29 @@

    通过 JSON.stringify() 把值转换为字符串,多用于调试用途。

    输入

    -
    1
    2
    {% assign arr = "foo bar coo" | split: " " %}
    {{ arr | json }}
    +
    {% assign arr = "foo bar coo" | split: " " %}
    +{{ arr | json }}

    输出

    -
    1
    ["foo","bar","coo"]
    +
    ["foo","bar","coo"]

    格式化

    v10.11.0

    可以指定一个 space 参数来格式化 JSON。

    输入

    -
    1
    2
    {% assign arr = "foo bar coo" | split: " " %}
    {{ arr | json: 4 }}
    +
    {% assign arr = "foo bar coo" | split: " " %}
    +{{ arr | json: 4 }}

    输出

    -
    1
    2
    3
    4
    5
    [
    "foo",
    "bar",
    "coo"
    ]
    +
    [
    +    "foo",
    +    "bar",
    +    "coo"
    +]
    diff --git a/zh-cn/filters/jsonify.html b/zh-cn/filters/jsonify.html index 8acee387a..fdce2072d 100644 --- a/zh-cn/filters/jsonify.html +++ b/zh-cn/filters/jsonify.html @@ -98,7 +98,7 @@ diff --git a/zh-cn/filters/last.html b/zh-cn/filters/last.html index 617f8c9b1..9f90fe3f6 100644 --- a/zh-cn/filters/last.html +++ b/zh-cn/filters/last.html @@ -96,23 +96,27 @@

    返回数组的最后一个元素。

    输入

    -
    1
    {{ "Ground control to Major Tom." | split: " " | last }}
    +
    {{ "Ground control to Major Tom." | split: " " | last }}

    输出

    -
    1
    Tom.
    +
    Tom.

    输入

    -
    1
    2
    {% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
    {{ my_array.last }}
    +
    {% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
    +{{ my_array.last }}

    输出

    -
    1
    2

    tiger
    +
    
    +tiger

    需要在标签中使用的时候,可以用点来计算 last

    -
    1
    2
    3
    {% if my_array.last == "tiger" %}
    There goes a tiger!
    {% endif %}
    +
    {% if my_array.last == "tiger" %}
    +  There goes a tiger!
    +{% endif %}
    diff --git a/zh-cn/filters/lstrip.html b/zh-cn/filters/lstrip.html index aaf64deb6..9ff570491 100644 --- a/zh-cn/filters/lstrip.html +++ b/zh-cn/filters/lstrip.html @@ -96,14 +96,14 @@

    移除字符串左侧的空白字符(制表符、空格、换行),不影响词之间的空格。

    输入

    -
    1
    BEGIN{{ "          So much room for activities!          " | lstrip }}END
    +
    BEGIN{{ "          So much room for activities!          " | lstrip }}END

    输出

    -
    1
    BEGINSo much room for activities!          END
    +
    BEGINSo much room for activities!          END
    diff --git a/zh-cn/filters/map.html b/zh-cn/filters/map.html index 87ca9cee6..017e97d21 100644 --- a/zh-cn/filters/map.html +++ b/zh-cn/filters/map.html @@ -97,14 +97,22 @@

    按照属性名提取对象的属性形成另一个数组并返回。

    下面的例子中假设 site.pages 包含了站点的所有网页元信息。使用 assignmap 过滤器创建了一个 site.pages 中所有对象的 category 属性的值构成的数组。

    输入

    -
    1
    2
    3
    4
    5
    {% assign all_categories = site.pages | map: "category" %}

    {% for item in all_categories %}
    - {{ item }}
    {% endfor %}
    +
    {% assign all_categories = site.pages | map: "category" %}
    +
    +{% for item in all_categories %}
    +- {{ item }}
    +{% endfor %}

    输出

    -
    1
    2
    3
    4
    5
    - business
    - celebrities
    - lifestyle
    - sports
    - technology
    +
    - business
    +- celebrities
    +- lifestyle
    +- sports
    +- technology
    diff --git a/zh-cn/filters/minus.html b/zh-cn/filters/minus.html index 787666cee..7a3f1baba 100644 --- a/zh-cn/filters/minus.html +++ b/zh-cn/filters/minus.html @@ -96,26 +96,26 @@

    两数相减。

    输入

    -
    1
    {{ 4 | minus: 2 }}
    +
    {{ 4 | minus: 2 }}

    输出

    -
    1
    2
    +
    2

    输入

    -
    1
    {{ 16 | minus: 4 }}
    +
    {{ 16 | minus: 4 }}

    输出

    -
    1
    12
    +
    12

    输入

    -
    1
    {{ 183.357 | minus: 12 }}
    +
    {{ 183.357 | minus: 12 }}

    输出

    -
    1
    171.357
    +
    171.357
    diff --git a/zh-cn/filters/modulo.html b/zh-cn/filters/modulo.html index c5e315b6a..f2d8107cc 100644 --- a/zh-cn/filters/modulo.html +++ b/zh-cn/filters/modulo.html @@ -96,26 +96,26 @@

    返回两数相除的余数。

    输入

    -
    1
    {{ 3 | modulo: 2 }}
    +
    {{ 3 | modulo: 2 }}

    输出

    -
    1
    1
    +
    1

    输入

    -
    1
    {{ 24 | modulo: 7 }}
    +
    {{ 24 | modulo: 7 }}

    输出

    -
    1
    3
    +
    3

    输入

    -
    1
    {{ 183.357 | modulo: 12 }}
    +
    {{ 183.357 | modulo: 12 }}

    输出

    -
    1
    3.3569999999999993
    +
    3.3569999999999993
    diff --git a/zh-cn/filters/newline_to_br.html b/zh-cn/filters/newline_to_br.html index 6c3db893e..c41e0adfd 100644 --- a/zh-cn/filters/newline_to_br.html +++ b/zh-cn/filters/newline_to_br.html @@ -96,14 +96,20 @@

    把字符串里的所有换行符(\n)替换为 HTML 换行(<br />)。

    输入

    -
    1
    2
    3
    4
    5
    6
    {% capture string_with_newlines %}
    Hello
    there
    {% endcapture %}

    {{ string_with_newlines | newline_to_br }}
    +
    {% capture string_with_newlines %}
    +Hello
    +there
    +{% endcapture %}
    +
    +{{ string_with_newlines | newline_to_br }}

    输出

    -
    1
    2

    <br/>Hello<br/>there<br/>
    +
    
    +<br/>Hello<br/>there<br/>
    diff --git a/zh-cn/filters/normalize_whitespace.html b/zh-cn/filters/normalize_whitespace.html index 563ca8ff0..e02006bbb 100644 --- a/zh-cn/filters/normalize_whitespace.html +++ b/zh-cn/filters/normalize_whitespace.html @@ -96,14 +96,14 @@

    把连续的空白字符替换为单个空格。

    输入

    -
    1
    {{ "a \n b" | normalize_whitespace }}
    +
    {{ "a \n b" | normalize_whitespace }}

    输出

    -
    1
    a b
    +
    a b
    diff --git a/zh-cn/filters/number_of_words.html b/zh-cn/filters/number_of_words.html index f77176e04..8412cd46d 100644 --- a/zh-cn/filters/number_of_words.html +++ b/zh-cn/filters/number_of_words.html @@ -100,32 +100,32 @@
  • 'auto':与 'cjk' 类似,但如果过滤器用于可能包含或不包含 CJK 字符的字符串,则性能更好。
  • 输入

    -
    1
    {{ "Hello world!" | number_of_words }}
    +
    {{ "Hello world!" | number_of_words }}

    输出

    -
    1
    2
    +
    2

    输入

    -
    1
    {{ "你好hello世界world" | number_of_words }}
    +
    {{ "你好hello世界world" | number_of_words }}

    输出

    -
    1
    1
    +
    1

    输入

    -
    1
    {{ "你好hello世界world" | number_of_words: "cjk" }}
    +
    {{ "你好hello世界world" | number_of_words: "cjk" }}

    输出

    -
    1
    6
    +
    6

    输入

    -
    1
    {{ "你好hello世界world" | number_of_words: "auto" }}
    +
    {{ "你好hello世界world" | number_of_words: "auto" }}

    输出

    -
    1
    6
    +
    6
    diff --git a/zh-cn/filters/overview.html b/zh-cn/filters/overview.html index e48d54af8..9eb6697fb 100644 --- a/zh-cn/filters/overview.html +++ b/zh-cn/filters/overview.html @@ -129,7 +129,7 @@
    - +
    diff --git a/zh-cn/filters/plus.html b/zh-cn/filters/plus.html index 440143ed4..0acf3b7c0 100644 --- a/zh-cn/filters/plus.html +++ b/zh-cn/filters/plus.html @@ -96,26 +96,26 @@

    两数相加。

    输入

    -
    1
    {{ 4 | plus: 2 }}
    +
    {{ 4 | plus: 2 }}

    输出

    -
    1
    6
    +
    6

    输入

    -
    1
    {{ 16 | plus: 4 }}
    +
    {{ 16 | plus: 4 }}

    输出

    -
    1
    20
    +
    20

    输入

    -
    1
    {{ 183.357 | plus: 12 }}
    +
    {{ 183.357 | plus: 12 }}

    输出

    -
    1
    195.357
    +
    195.357
    diff --git a/zh-cn/filters/pop.html b/zh-cn/filters/pop.html index 935b40abb..fb7aed20c 100644 --- a/zh-cn/filters/pop.html +++ b/zh-cn/filters/pop.html @@ -96,14 +96,21 @@

    从数组末尾弹出一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    {% assign fruits = "apples, oranges, peaches" | split: ", " %}

    {% assign everything = fruits | pop %}

    {% for item in everything %}
    - {{ item }}
    {% endfor %}
    +
    {% assign fruits = "apples, oranges, peaches" | split: ", " %}
    +
    +{% assign everything = fruits | pop %}
    +
    +{% for item in everything %}
    +- {{ item }}
    +{% endfor %}

    输出

    -
    1
    2
    - apples
    - oranges
    +
    - apples
    +- oranges
    diff --git a/zh-cn/filters/prepend.html b/zh-cn/filters/prepend.html index 82aac5b87..93f6491cd 100644 --- a/zh-cn/filters/prepend.html +++ b/zh-cn/filters/prepend.html @@ -96,21 +96,23 @@

    在字符串开头添加另一个字符串。

    输入

    -
    1
    {{ "apples, oranges, and bananas" | prepend: "Some fruit: " }}
    +
    {{ "apples, oranges, and bananas" | prepend: "Some fruit: " }}

    输出

    -
    1
    Some fruit: apples, oranges, and bananas
    +
    Some fruit: apples, oranges, and bananas

    prepend 也可以用于变量。

    输入

    -
    1
    2
    {% assign url = "example.com" %}
    {{ "/index.html" | prepend: url }}
    +
    {% assign url = "example.com" %}
    +{{ "/index.html" | prepend: url }}

    输出

    -
    1
    2

    example.com/index.html
    +
    
    +example.com/index.html
    diff --git a/zh-cn/filters/push.html b/zh-cn/filters/push.html index 8a5b44cb2..86c343c57 100644 --- a/zh-cn/filters/push.html +++ b/zh-cn/filters/push.html @@ -96,14 +96,22 @@

    在数组中添加一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    {% assign fruits = "apples, oranges" | split: ", " %}

    {% assign everything = fruits | push: "peaches" %}

    {% for item in everything %}
    - {{ item }}
    {% endfor %}
    +
    {% assign fruits = "apples, oranges" | split: ", " %}
    +
    +{% assign everything = fruits | push: "peaches" %}
    +
    +{% for item in everything %}
    +- {{ item }}
    +{% endfor %}

    输出

    -
    1
    2
    3
    - apples
    - oranges
    - peaches
    +
    - apples
    +- oranges
    +- peaches
    diff --git a/zh-cn/filters/raw.html b/zh-cn/filters/raw.html index c27d0f955..f7377abb1 100644 --- a/zh-cn/filters/raw.html +++ b/zh-cn/filters/raw.html @@ -99,33 +99,33 @@

    输入(未设置 outputEscape

    -
    1
    {{ "<" }}
    +
    {{ "<" }}

    输出

    -
    1
    <
    +
    <

    输入(outputEscape="escape"

    -
    1
    {{ "<" }}
    +
    {{ "<" }}

    输出

    -
    1
    &lt;
    +
    &lt;

    输入(outputEscape="json"

    -
    1
    {{ "<" }}
    +
    {{ "<" }}

    输出

    -
    1
    "<"
    +
    "<"

    输入(outputEscape="escape"

    -
    1
    {{ "<" | raw }}
    +
    {{ "<" | raw }}

    输出

    -
    1
    <
    +
    <
    diff --git a/zh-cn/filters/remove.html b/zh-cn/filters/remove.html index 7047d2e09..71aa83adf 100644 --- a/zh-cn/filters/remove.html +++ b/zh-cn/filters/remove.html @@ -96,14 +96,14 @@

    移除字符串中出现的所有指定子字符串。

    输入

    -
    1
    {{ "I strained to see the train through the rain" | remove: "rain" }}
    +
    {{ "I strained to see the train through the rain" | remove: "rain" }}

    输出

    -
    1
    I strained to see the t through the 
    +
    I strained to see the t through the 
    diff --git a/zh-cn/filters/remove_first.html b/zh-cn/filters/remove_first.html index c00eee432..1c2ebae7a 100644 --- a/zh-cn/filters/remove_first.html +++ b/zh-cn/filters/remove_first.html @@ -96,14 +96,14 @@

    移除字符串中出现的第一个指定子字符串。

    输入

    -
    1
    {{ "I strained to see the train through the rain" | remove_first: "rain" }}
    +
    {{ "I strained to see the train through the rain" | remove_first: "rain" }}

    输出

    -
    1
    I strained to see the t through the rain
    +
    I strained to see the t through the rain
    diff --git a/zh-cn/filters/remove_last.html b/zh-cn/filters/remove_last.html index 17077a65a..8090aa0a2 100644 --- a/zh-cn/filters/remove_last.html +++ b/zh-cn/filters/remove_last.html @@ -96,14 +96,14 @@

    移除字符串中出现的最后一个指定子字符串。

    输入

    -
    1
    {{ "I strained to see the train through the rain" | remove_last: "rain" }}
    +
    {{ "I strained to see the train through the rain" | remove_last: "rain" }}

    输出

    -
    1
    I strained to see the train through the
    +
    I strained to see the train through the
    diff --git a/zh-cn/filters/replace.html b/zh-cn/filters/replace.html index 8c456a9bc..0f6ffb62f 100644 --- a/zh-cn/filters/replace.html +++ b/zh-cn/filters/replace.html @@ -96,14 +96,14 @@

    把字符串中出现的每一个指定子字符串替换为另一个字符串。

    输入

    -
    1
    {{ "Take my protein pills and put my helmet on" | replace: "my", "your" }}
    +
    {{ "Take my protein pills and put my helmet on" | replace: "my", "your" }}

    输出

    -
    1
    Take your protein pills and put your helmet on
    +
    Take your protein pills and put your helmet on
    diff --git a/zh-cn/filters/replace_first.html b/zh-cn/filters/replace_first.html index 6a88af662..aa7ae5eaa 100644 --- a/zh-cn/filters/replace_first.html +++ b/zh-cn/filters/replace_first.html @@ -96,14 +96,14 @@

    把字符串中出现的第一个指定子字符串替换为另一个字符串。

    输入

    -
    1
    {{ "Take my protein pills and put my helmet on" | replace_first: "my", "your" }}
    +
    {{ "Take my protein pills and put my helmet on" | replace_first: "my", "your" }}

    输出

    -
    1
    Take your protein pills and put my helmet on
    +
    Take your protein pills and put my helmet on
    diff --git a/zh-cn/filters/replace_last.html b/zh-cn/filters/replace_last.html index cbe675295..c1e468823 100644 --- a/zh-cn/filters/replace_last.html +++ b/zh-cn/filters/replace_last.html @@ -96,14 +96,14 @@

    把字符串中出现的最后一个指定子字符串替换为另一个字符串。

    输入

    -
    1
    {{ "Take my protein pills and put my helmet on" | replace_last: "my", "your" }}
    +
    {{ "Take my protein pills and put my helmet on" | replace_last: "my", "your" }}

    输出

    -
    1
    Take my protein pills and put your helmet on
    +
    Take my protein pills and put your helmet on
    diff --git a/zh-cn/filters/reverse.html b/zh-cn/filters/reverse.html index 2ea834831..083137434 100644 --- a/zh-cn/filters/reverse.html +++ b/zh-cn/filters/reverse.html @@ -96,21 +96,25 @@

    反转数组的所有元素,不可用于字符串。

    输入

    -
    1
    2
    3
    {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}

    {{ my_array | reverse | join: ", " }}
    +
    {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}
    +
    +{{ my_array | reverse | join: ", " }}

    输出

    -
    1
    2
    3


    plums, peaches, oranges, apples
    +
    
    +
    +plums, peaches, oranges, apples

    尽管 reverse 不能直接用于字符串,可以把字符串分割成数组,反转后再连接成字符串:

    输入

    -
    1
    {{ "Ground control to Major Tom." | split: "" | reverse | join: "" }}
    +
    {{ "Ground control to Major Tom." | split: "" | reverse | join: "" }}

    输出

    -
    1
    .moT rojaM ot lortnoc dnuorG
    +
    .moT rojaM ot lortnoc dnuorG
    diff --git a/zh-cn/filters/round.html b/zh-cn/filters/round.html index c27ef84fa..10cf7958f 100644 --- a/zh-cn/filters/round.html +++ b/zh-cn/filters/round.html @@ -96,26 +96,26 @@

    数字四舍五入取整,如果传入小数位数作为参数。

    输入

    -
    1
    {{ 1.2 | round }}
    +
    {{ 1.2 | round }}

    输出

    -
    1
    1
    +
    1

    输入

    -
    1
    {{ 2.7 | round }}
    +
    {{ 2.7 | round }}

    输出

    -
    1
    3
    +
    3

    输入

    -
    1
    {{ 183.357 | round: 2 }}
    +
    {{ 183.357 | round: 2 }}

    输出

    -
    1
    183.36
    +
    183.36
    diff --git a/zh-cn/filters/rstrip.html b/zh-cn/filters/rstrip.html index 42c0ca1cc..375294a8b 100644 --- a/zh-cn/filters/rstrip.html +++ b/zh-cn/filters/rstrip.html @@ -96,14 +96,14 @@

    移除字符串右侧的空白字符(制表符、空格、换行),不影响词之间的空格。

    输入

    -
    1
    BEGIN{{ "          So much room for activities!          " | rstrip }}END
    +
    BEGIN{{ "          So much room for activities!          " | rstrip }}END

    输出

    -
    1
    BEGIN          So much room for activities!END
    +
    BEGIN          So much room for activities!END
    diff --git a/zh-cn/filters/shift.html b/zh-cn/filters/shift.html index ff2f2a866..3b528fd0a 100644 --- a/zh-cn/filters/shift.html +++ b/zh-cn/filters/shift.html @@ -96,14 +96,21 @@

    从数组头部弹出一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    {% assign fruits = "apples, oranges, peaches" | split: ", " %}

    {% assign everything = fruits | shift %}

    {% for item in everything %}
    - {{ item }}
    {% endfor %}
    +
    {% assign fruits = "apples, oranges, peaches" | split: ", " %}
    +
    +{% assign everything = fruits | shift %}
    +
    +{% for item in everything %}
    +- {{ item }}
    +{% endfor %}

    输出

    -
    1
    2
    - oranges
    - peaches
    +
    - oranges
    +- peaches
    diff --git a/zh-cn/filters/size.html b/zh-cn/filters/size.html index 16927cbdf..f86bf9bc2 100644 --- a/zh-cn/filters/size.html +++ b/zh-cn/filters/size.html @@ -96,23 +96,29 @@

    返回字符串的字符个数或者数组的元素个数。

    输入

    -
    1
    {{ "Ground control to Major Tom." | size }}
    +
    {{ "Ground control to Major Tom." | size }}

    输出

    -
    1
    28
    +
    28

    输入

    -
    1
    2
    3
    {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}

    {{ my_array.size }}
    +
    {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}
    +
    +{{ my_array.size }}

    输出

    -
    1
    2
    3


    4
    +
    
    +
    +4

    在标签里可以用点来计算 size

    -
    1
    2
    3
    {% if site.pages.size > 10 %}
    This is a big website!
    {% endif %}
    +
    {% if site.pages.size > 10 %}
    +  This is a big website!
    +{% endif %}
    diff --git a/zh-cn/filters/slice.html b/zh-cn/filters/slice.html index e99ebb78a..daef9f19f 100644 --- a/zh-cn/filters/slice.html +++ b/zh-cn/filters/slice.html @@ -96,33 +96,33 @@

    返回第一个参数为下标位置的一个字符,如果指定了第二个参数会被解释为子字符串的长度。字符串下标从零开始。

    输入

    -
    1
    {{ "Liquid" | slice: 0 }}
    +
    {{ "Liquid" | slice: 0 }}

    输出

    -
    1
    L
    +
    L

    输入

    -
    1
    {{ "Liquid" | slice: 2 }}
    +
    {{ "Liquid" | slice: 2 }}

    输出

    -
    1
    q
    +
    q

    输入

    -
    1
    {{ "Liquid" | slice: 2, 5 }}
    +
    {{ "Liquid" | slice: 2, 5 }}

    输出

    -
    1
    quid
    +
    quid

    If the first argument is a negative number, the indices are counted from the end of the string:

    输入

    -
    1
    {{ "Liquid" | slice: -3, 2 }}
    +
    {{ "Liquid" | slice: -3, 2 }}

    输出

    -
    1
    ui
    +
    ui
    diff --git a/zh-cn/filters/slugify.html b/zh-cn/filters/slugify.html index 76b96588c..9f5d9be69 100644 --- a/zh-cn/filters/slugify.html +++ b/zh-cn/filters/slugify.html @@ -106,33 +106,33 @@
  • case: boolean。默认为 false。如果为 true,则保留 slug 原本的大小写。
  • 输入

    -
    1
    {{ "The _config.yml file" | slugify }}
    +
    {{ "The _config.yml file" | slugify }}

    输出

    -
    1
    the-config-yml-file
    +
    the-config-yml-file

    输入

    -
    1
    {{ "The _config.yml file" | slugify: "pretty" }}
    +
    {{ "The _config.yml file" | slugify: "pretty" }}

    输出

    -
    1
    the-_config.yml-file
    +
    the-_config.yml-file

    输入

    -
    1
    {{ "The _cönfig.yml file" | slugify: "ascii" }}
    +
    {{ "The _cönfig.yml file" | slugify: "ascii" }}

    输出

    -
    1
    the-c-nfig-yml-file
    +
    the-c-nfig-yml-file

    输入

    -
    1
    {{ "The cönfig.yml file" | slugify: "latin" }}
    +
    {{ "The cönfig.yml file" | slugify: "latin" }}

    输出

    -
    1
    the-config-yml-file
    +
    the-config-yml-file

    输入

    -
    1
    {{ "The cönfig.yml file" | slugify: "latin", true }}
    +
    {{ "The cönfig.yml file" | slugify: "latin", true }}

    输出

    -
    1
    The-config-yml-file
    +
    The-config-yml-file
    diff --git a/zh-cn/filters/sort.html b/zh-cn/filters/sort.html index c4d088c6f..05cc05357 100644 --- a/zh-cn/filters/sort.html +++ b/zh-cn/filters/sort.html @@ -96,17 +96,24 @@

    对数组中的元素排序,排序方式为 JavaScript Array.prototype.sort()

    输入

    -
    1
    2
    3
    {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}

    {{ my_array | sort | join: ", " }}
    +
    {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}
    +
    +{{ my_array | sort | join: ", " }}

    输出

    -
    1
    2
    3


    Sally Snake, giraffe, octopus, zebra
    +
    
    +
    +Sally Snake, giraffe, octopus, zebra

    有一个参数来指定用元素的哪个属性排序。

    -
    1
    2
    3
    4
    {% assign products_by_price = collection.products | sort: "price" %}
    {% for product in products_by_price %}
    <h4>{{ product.title }}</h4>
    {% endfor %}
    +
    {% assign products_by_price = collection.products | sort: "price" %}
    +{% for product in products_by_price %}
    +  <h4>{{ product.title }}</h4>
    +{% endfor %}
    diff --git a/zh-cn/filters/sort_natural.html b/zh-cn/filters/sort_natural.html index 8e7ec1bfd..2f113eefd 100644 --- a/zh-cn/filters/sort_natural.html +++ b/zh-cn/filters/sort_natural.html @@ -96,17 +96,24 @@

    大小写不敏感地对数组元素排序。

    输入

    -
    1
    2
    3
    {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}

    {{ my_array | sort_natural | join: ", " }}
    +
    {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}
    +
    +{{ my_array | sort_natural | join: ", " }}

    输出

    -
    1
    2
    3


    giraffe, octopus, Sally Snake, zebra
    +
    
    +
    +giraffe, octopus, Sally Snake, zebra

    有一个参数来指定用元素的哪个属性排序。

    -
    1
    2
    3
    4
    {% assign products_by_company = collection.products | sort_natural: "company" %}
    {% for product in products_by_company %}
    <h4>{{ product.title }}</h4>
    {% endfor %}
    +
    {% assign products_by_company = collection.products | sort_natural: "company" %}
    +{% for product in products_by_company %}
    +  <h4>{{ product.title }}</h4>
    +{% endfor %}
    diff --git a/zh-cn/filters/split.html b/zh-cn/filters/split.html index 6da40f182..db74624b6 100644 --- a/zh-cn/filters/split.html +++ b/zh-cn/filters/split.html @@ -96,14 +96,28 @@

    把字符串按照指定的分隔符进行分割,split 通常用于把逗号分隔的字符串转换为数组。

    输入

    -
    1
    2
    3
    4
    5
    {% assign beatles = "John, Paul, George, Ringo" | split: ", " %}

    {% for member in beatles %}
    {{ member }}
    {% endfor %}
    +
    {% assign beatles = "John, Paul, George, Ringo" | split: ", " %}
    +
    +{% for member in beatles %}
    +  {{ member }}
    +{% endfor %}

    输出

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11




    John

    Paul

    George

    Ringo
    +
    
    +
    +
    +
    +John
    +
    +Paul
    +
    +George
    +
    +Ringo
    diff --git a/zh-cn/filters/strip.html b/zh-cn/filters/strip.html index c64ed28a7..46e71db8d 100644 --- a/zh-cn/filters/strip.html +++ b/zh-cn/filters/strip.html @@ -96,14 +96,14 @@

    移除字符串两侧的空白字符(制表符、空格、换行),不影响词之间的空格。

    输入

    -
    1
    BEGIN{{ "          So much room for activities!          " | strip }}END
    +
    BEGIN{{ "          So much room for activities!          " | strip }}END

    输出

    -
    1
    BEGINSo much room for activities!END
    +
    BEGINSo much room for activities!END
    diff --git a/zh-cn/filters/strip_html.html b/zh-cn/filters/strip_html.html index 131e73e12..8e92d2dbf 100644 --- a/zh-cn/filters/strip_html.html +++ b/zh-cn/filters/strip_html.html @@ -96,14 +96,14 @@

    移除字符串中的 HTML 标签。

    输入

    -
    1
    {{ "Have <em>you</em> read <strong>Ulysses</strong>?" | strip_html }}
    +
    {{ "Have <em>you</em> read <strong>Ulysses</strong>?" | strip_html }}

    输出

    -
    1
    Have you read Ulysses?
    +
    Have you read Ulysses?
    diff --git a/zh-cn/filters/strip_newlines.html b/zh-cn/filters/strip_newlines.html index 9b91d3d1f..0e1aba274 100644 --- a/zh-cn/filters/strip_newlines.html +++ b/zh-cn/filters/strip_newlines.html @@ -96,14 +96,20 @@

    移除字符串中的换行符。

    输入

    -
    1
    2
    3
    4
    5
    6
    {% capture string_with_newlines %}
    Hello
    there
    {% endcapture %}

    {{ string_with_newlines | strip_newlines }}
    +
    {% capture string_with_newlines %}
    +Hello
    +there
    +{% endcapture %}
    +
    +{{ string_with_newlines | strip_newlines }}

    输出

    -
    1
    2

    Hellothere
    +
    
    +Hellothere
    diff --git a/zh-cn/filters/times.html b/zh-cn/filters/times.html index 3eea8f721..cc660f8e0 100644 --- a/zh-cn/filters/times.html +++ b/zh-cn/filters/times.html @@ -96,26 +96,26 @@

    两数相乘。

    输入

    -
    1
    {{ 3 | times: 2 }}
    +
    {{ 3 | times: 2 }}

    输出

    -
    1
    6
    +
    6

    输入

    -
    1
    {{ 24 | times: 7 }}
    +
    {{ 24 | times: 7 }}

    输出

    -
    1
    168
    +
    168

    输入

    -
    1
    {{ 183.357 | times: 12 }}
    +
    {{ 183.357 | times: 12 }}

    输出

    -
    1
    2200.284
    +
    2200.284
    diff --git a/zh-cn/filters/to_integer.html b/zh-cn/filters/to_integer.html index c422eac95..32697671c 100644 --- a/zh-cn/filters/to_integer.html +++ b/zh-cn/filters/to_integer.html @@ -96,14 +96,14 @@

    转换为数字类型。

    输入

    -
    1
    {{ "123" | to_integer | json }}
    +
    {{ "123" | to_integer | json }}

    输出

    -
    1
    123
    +
    123
    diff --git a/zh-cn/filters/truncate.html b/zh-cn/filters/truncate.html index ca16c7b6c..0c2d7af47 100644 --- a/zh-cn/filters/truncate.html +++ b/zh-cn/filters/truncate.html @@ -96,28 +96,28 @@

    把字符串截断为指定长度,可以指定一个数字表示截断到多少长度。最后会添加一个省略号(…)且记在长度里。

    基本使用

    输入

    -
    1
    {{ "Ground control to Major Tom." | truncate: 20 }}
    +
    {{ "Ground control to Major Tom." | truncate: 20 }}

    输出

    -
    1
    Ground control to...
    +
    Ground control to...

    自定义省略号

    truncate 的第二个可选参数用来指定后面追加的字符串,默认为省略号(…)。这个参数的长度会计算在第一个参数的长度里。例如,如果要把字符串截断到 10 个字符,并使用了一个 3 字符长度的省略号,那么第一个参数的值要设置到 13

    输入

    -
    1
    {{ "Ground control to Major Tom." | truncate: 25, ", and so on" }}
    +
    {{ "Ground control to Major Tom." | truncate: 25, ", and so on" }}

    输出

    -
    1
    Ground control, and so on
    +
    Ground control, and so on

    不要省略号

    如果需要把字符串截断到特定长度且不要添加省略号,则把第二个参数设置为空字符串:

    输入

    -
    1
    {{ "Ground control to Major Tom." | truncate: 20, "" }}
    +
    {{ "Ground control to Major Tom." | truncate: 20, "" }}

    输出

    -
    1
    Ground control to Ma
    +
    Ground control to Ma
    diff --git a/zh-cn/filters/truncatewords.html b/zh-cn/filters/truncatewords.html index 17959d690..1a3b1e821 100644 --- a/zh-cn/filters/truncatewords.html +++ b/zh-cn/filters/truncatewords.html @@ -96,28 +96,28 @@

    把字符串截断为指定个数的单词,可以指定一个数字表示截断到多少个单词。最后会添加一个省略号(…)。

    基本使用

    输入

    -
    1
    {{ "Ground control to Major Tom." | truncatewords: 3 }}
    +
    {{ "Ground control to Major Tom." | truncatewords: 3 }}

    输出

    -
    1
    Ground control to...
    +
    Ground control to...

    自定义省略号

    truncate 的第二个可选参数用来指定后面追加的字符串,默认为省略号(…)。

    输入

    -
    1
    {{ "Ground control to Major Tom." | truncatewords: 3, "--" }}
    +
    {{ "Ground control to Major Tom." | truncatewords: 3, "--" }}

    输出

    -
    1
    Ground control to--
    +
    Ground control to--

    不要省略号

    如果不希望添加省略号,把第二个参数设置为空字符串即可:

    输入

    -
    1
    {{ "Ground control to Major Tom." | truncatewords: 3, "" }}
    +
    {{ "Ground control to Major Tom." | truncatewords: 3, "" }}

    输出

    -
    1
    Ground control to
    +
    Ground control to
    diff --git a/zh-cn/filters/uniq.html b/zh-cn/filters/uniq.html index 5f0c5bc55..1326181e4 100644 --- a/zh-cn/filters/uniq.html +++ b/zh-cn/filters/uniq.html @@ -96,7 +96,8 @@

    移除数组中的重复元素。

    输入

    -
    1
    2
    {% assign my_array = "ants, bugs, bees, bugs, ants" | split: ", " %}
    {{ my_array | uniq | join: ", " }}
    +
    {% assign my_array = "ants, bugs, bees, bugs, ants" | split: ", " %}
    +{{ my_array | uniq | join: ", " }}

    输出

    
    @@ -104,7 +105,7 @@ ants, bugs, bees```
    diff --git a/zh-cn/filters/unshift.html b/zh-cn/filters/unshift.html index d2950a966..76db27b5e 100644 --- a/zh-cn/filters/unshift.html +++ b/zh-cn/filters/unshift.html @@ -96,14 +96,22 @@

    往数组头部添加一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    {% assign fruits = "oranges, peaches" | split: ", " %}

    {% assign everything = fruits | unshift: "apples" %}

    {% for item in everything %}
    - {{ item }}
    {% endfor %}
    +
    {% assign fruits = "oranges, peaches" | split: ", " %}
    +
    +{% assign everything = fruits | unshift: "apples" %}
    +
    +{% for item in everything %}
    +- {{ item }}
    +{% endfor %}

    输出

    -
    1
    2
    3
    - apples
    - oranges
    - peaches
    +
    - apples
    +- oranges
    +- peaches
    diff --git a/zh-cn/filters/upcase.html b/zh-cn/filters/upcase.html index ca01a5ee0..8c231c6bd 100644 --- a/zh-cn/filters/upcase.html +++ b/zh-cn/filters/upcase.html @@ -96,20 +96,20 @@

    字符串中每个字符都转为大写,对已经是大写的字符没有影响。

    输入

    -
    1
    {{ "Parker Moore" | upcase }}
    +
    {{ "Parker Moore" | upcase }}

    输出

    -
    1
    PARKER MOORE
    +
    PARKER MOORE

    输入

    -
    1
    {{ "APPLE" | upcase }}
    +
    {{ "APPLE" | upcase }}

    输出

    -
    1
    APPLE
    +
    APPLE
    diff --git a/zh-cn/filters/uri_escape.html b/zh-cn/filters/uri_escape.html index bc1210c71..dcab0a44a 100644 --- a/zh-cn/filters/uri_escape.html +++ b/zh-cn/filters/uri_escape.html @@ -96,15 +96,15 @@

    把 URI 中的特殊字符做百分号编码,空格会变成 %20保留字 不会被转义。

    输入

    -
    1
    {{ "https://example.com/?q=foo, \bar?" | uri_escape }}
    +
    {{ "https://example.com/?q=foo, \bar?" | uri_escape }}

    输出

    -
    1
    https://example.com/?q=foo,%20%5Cbar?
    +
    https://example.com/?q=foo,%20%5Cbar?
    diff --git a/zh-cn/filters/url_decode.html b/zh-cn/filters/url_decode.html index e774e9b37..b602a6045 100644 --- a/zh-cn/filters/url_decode.html +++ b/zh-cn/filters/url_decode.html @@ -96,14 +96,14 @@

    把 URL 编码的字符串解码。

    输入

    -
    1
    {{ "%27Stop%21%27+said+Fred" | url_decode }}
    +
    {{ "%27Stop%21%27+said+Fred" | url_decode }}

    输出

    -
    1
    'Stop!' said Fred
    +
    'Stop!' said Fred
    diff --git a/zh-cn/filters/url_encode.html b/zh-cn/filters/url_encode.html index c3004e068..27fff0891 100644 --- a/zh-cn/filters/url_encode.html +++ b/zh-cn/filters/url_encode.html @@ -96,20 +96,20 @@

    把字符串中 URL 不安全的字符转义为百分号编码。

    输入

    -
    1
    {{ "john@liquid.com" | url_encode }}
    +
    {{ "john@liquid.com" | url_encode }}

    输出

    -
    1
    john%40liquid.com
    +
    john%40liquid.com

    输入

    -
    1
    {{ "Tetsuro Takara" | url_encode }}
    +
    {{ "Tetsuro Takara" | url_encode }}

    输出

    -
    1
    Tetsuro+Takara
    +
    Tetsuro+Takara
    diff --git a/zh-cn/filters/where.html b/zh-cn/filters/where.html index ce717f5d4..373da64b8 100644 --- a/zh-cn/filters/where.html +++ b/zh-cn/filters/where.html @@ -97,39 +97,84 @@

    按照数组中对象的属性值来过滤得到新数组,如果未指定第二个参数(属性值)则过滤得到所有属性值为 truthy 的对象。

    下面的例子中,假设你有一个 products 列表并且希望展示其中的厨房产品。使用 where 过滤器可以得到一个只包含 "type" 属性值为 "kitchen" 的元素的数组。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    All products:
    {% for product in products %}
    - {{ product.title }}
    {% endfor %}

    {% assign kitchen_products = products | where: "type", "kitchen" %}

    Kitchen products:
    {% for product in kitchen_products %}
    - {{ product.title }}
    {% endfor %}
    +
    All products:
    +{% for product in products %}
    +- {{ product.title }}
    +{% endfor %}
    +
    +{% assign kitchen_products = products | where: "type", "kitchen" %}
    +
    +Kitchen products:
    +{% for product in kitchen_products %}
    +- {{ product.title }}
    +{% endfor %}

    输出

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    All products:
    - Vacuum
    - Spatula
    - Television
    - Garlic press

    Kitchen products:
    - Spatula
    - Garlic press
    +
    All products:
    +- Vacuum
    +- Spatula
    +- Television
    +- Garlic press
    +
    +Kitchen products:
    +- Spatula
    +- Garlic press

    如果你有一个产品列表且希望只显示可用的产品,可以用 where 过滤器但不指定目标值,LiquidJS 会过滤得到 "available" 值为 truthy 的产品列表。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    All products:
    {% for product in products %}
    - {{ product.title }}
    {% endfor %}

    {% assign available_products = products | where: "available" %}

    Available products:
    {% for product in available_products %}
    - {{ product.title }}
    {% endfor %}
    +
    All products:
    +{% for product in products %}
    +- {{ product.title }}
    +{% endfor %}
    +
    +{% assign available_products = products | where: "available" %}
    +
    +Available products:
    +{% for product in available_products %}
    +- {{ product.title }}
    +{% endfor %}

    输出

    -
    1
    2
    3
    4
    5
    6
    7
    8
    All products:
    - Coffee mug
    - Limited edition sneakers
    - Boring sneakers

    Available products:
    - Coffee mug
    - Boring sneakers
    +
    All products:
    +- Coffee mug
    +- Limited edition sneakers
    +- Boring sneakers
    +
    +Available products:
    +- Coffee mug
    +- Boring sneakers

    where 后面再加一个 first 可以用来得到单个元素。例如,你要展示秋季系列里的单个 T-shirt。

    输入

    -
    1
    2
    3
    {% assign new_shirt = products | where: "type", "shirt" | first %}

    Featured product: {{ new_shirt.title }}
    +
    {% assign new_shirt = products | where: "type", "shirt" | first %}
    +
    +Featured product: {{ new_shirt.title }}

    输出

    -
    1
    Featured product: Hawaiian print sweater vest
    +
    Featured product: Hawaiian print sweater vest

    此外 property 可以是任意合法的变量表达式,就像在输出结构中一样,只是它的上下文是数组的每一个元素。对于下面的 products 数组:

    -
    1
    2
    3
    4
    5
    const products = [
    { meta: { details: { class: 'A' } }, order: 1 },
    { meta: { details: { class: 'B' } }, order: 2 },
    { meta: { details: { class: 'B' } }, order: 3 }
    ]
    +
    const products = [
    +    { meta: { details: { class: 'A' } }, order: 1 },
    +    { meta: { details: { class: 'B' } }, order: 2 },
    +    { meta: { details: { class: 'B' } }, order: 3 }
    +]

    输入

    -
    1
    2
    3
    4
    {% assign selected = products | where: 'meta.details["class"]', "B" %}
    {% for item in selected -%}
    - {{ item.order }}
    {% endfor %}
    +
    {% assign selected = products | where: 'meta.details["class"]', "B" %}
    +{% for item in selected -%}
    +- {{ item.order }}
    +{% endfor %}

    输出

    -
    1
    2
    - 2
    - 3
    +
    - 2
    +- 3
    diff --git a/zh-cn/filters/where_exp.html b/zh-cn/filters/where_exp.html index 2c83241e6..dbb08e544 100644 --- a/zh-cn/filters/where_exp.html +++ b/zh-cn/filters/where_exp.html @@ -96,15 +96,33 @@

    从数组中选择所有表达式值为真的对象。下面的例子中,假设你要从产品列表中筛选出来厨房用品。利用 where_exp 可以创建一个只包含 "type""kitchen" 的列表。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    All products:
    {% for product in products %}
    - {{ product.title }}
    {% endfor %}

    {% assign kitchen_products = products | where_exp: "item", "item.type == 'kitchen'" %}

    Kitchen products:
    {% for product in kitchen_products %}
    - {{ product.title }}
    {% endfor %}
    +
    All products:
    +{% for product in products %}
    +- {{ product.title }}
    +{% endfor %}
    +
    +{% assign kitchen_products = products | where_exp: "item", "item.type == 'kitchen'" %}
    +
    +Kitchen products:
    +{% for product in kitchen_products %}
    +- {{ product.title }}
    +{% endfor %}

    输出

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    All products:
    - Vacuum
    - Spatula
    - Television
    - Garlic press

    Kitchen products:
    - Spatula
    - Garlic press
    +
    All products:
    +- Vacuum
    +- Spatula
    +- Television
    +- Garlic press
    +
    +Kitchen products:
    +- Spatula
    +- Garlic press
    diff --git a/zh-cn/filters/xml_escape.html b/zh-cn/filters/xml_escape.html index d6a6d36c1..fc4b53285 100644 --- a/zh-cn/filters/xml_escape.html +++ b/zh-cn/filters/xml_escape.html @@ -96,14 +96,14 @@

    把文本做 XML 转义。

    输入

    -
    1
    {{ "Have you read \'James & the Giant Peach\'?" | xml_escape }}
    +
    {{ "Have you read \'James & the Giant Peach\'?" | xml_escape }}

    输出

    -
    1
    Have you read &#39;James &amp; the Giant Peach&#39;?
    +
    Have you read &#39;James &amp; the Giant Peach&#39;?
    - +
    diff --git a/zh-cn/tags/assign.html b/zh-cn/tags/assign.html index c9937c6df..81425467e 100644 --- a/zh-cn/tags/assign.html +++ b/zh-cn/tags/assign.html @@ -96,21 +96,25 @@

    创建一个新变量。

    输入

    -
    1
    2
    3
    4
    {% assign my_variable = false %}
    {% if my_variable != true %}
    This statement is valid.
    {% endif %}
    +
    {% assign my_variable = false %}
    +{% if my_variable != true %}
    +  This statement is valid.
    +{% endif %}

    输出

    -
    1
    This statement is valid.
    +
    This statement is valid.

    用引号(")包起来表示一个字符串。

    输入

    -
    1
    2
    {% assign foo = "bar" %}
    {{ foo }}
    +
    {% assign foo = "bar" %}
    +{{ foo }}

    输出

    -
    1
    bar
    +
    bar
    diff --git a/zh-cn/tags/capture.html b/zh-cn/tags/capture.html index b954b3f1b..079c77ec8 100644 --- a/zh-cn/tags/capture.html +++ b/zh-cn/tags/capture.html @@ -96,21 +96,29 @@

    capture 开闭标签之间的内容渲染后赋值给一个变量,这个变量的类型总是字符串。

    输入

    -
    1
    2
    {% capture my_variable %}I am being captured.{% endcapture %}
    {{ my_variable }}
    +
    {% capture my_variable %}I am being captured.{% endcapture %}
    +{{ my_variable }}

    输出

    -
    1
    I am being captured.
    +
    I am being captured.

    capture 里可以使用 assign 创建的其他变量来构建复杂字符串:

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    8
    {% assign favorite_food = "pizza" %}
    {% assign age = 35 %}

    {% capture about_me %}
    I am {{ age }} and my favorite food is {{ favorite_food }}.
    {% endcapture %}

    {{ about_me }}
    +
    {% assign favorite_food = "pizza" %}
    +{% assign age = 35 %}
    +
    +{% capture about_me %}
    +I am {{ age }} and my favorite food is {{ favorite_food }}.
    +{% endcapture %}
    +
    +{{ about_me }}

    输出

    -
    1
    I am 35 and my favourite food is pizza.
    +
    I am 35 and my favourite food is pizza.
    diff --git a/zh-cn/tags/case.html b/zh-cn/tags/case.html index 99cf74a51..ba17794d0 100644 --- a/zh-cn/tags/case.html +++ b/zh-cn/tags/case.html @@ -96,14 +96,22 @@

    创建一个 switch 语句,把变量跟不同的值比较。case 创建 switch 语句,when 比较它的值。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    {% assign handle = "cake" %}
    {% case handle %}
    {% when "cake" %}
    This is a cake
    {% when "cookie", "biscuit" %}
    This is a cookie
    {% else %}
    This is neither a cake nor a cookie
    {% endcase %}
    +
    {% assign handle = "cake" %}
    +{% case handle %}
    +  {% when "cake" %}
    +     This is a cake
    +  {% when "cookie", "biscuit" %}
    +     This is a cookie
    +  {% else %}
    +     This is neither a cake nor a cookie
    +{% endcase %}

    输出

    -
    1
    This is a cake
    +
    This is a cake
    diff --git a/zh-cn/tags/comment.html b/zh-cn/tags/comment.html index 7f201cf57..433a5fe6f 100644 --- a/zh-cn/tags/comment.html +++ b/zh-cn/tags/comment.html @@ -96,14 +96,16 @@

    让 Liquid 模板里一段代码不渲染。处于 comment 开闭标签之间的文本都不会输出,Liquid 代码都不会执行。

    输入

    -
    1
    2
    Anything you put between {% comment %} and {% endcomment %} tags
    is turned into a comment.
    +
    Anything you put between {% comment %} and {% endcomment %} tags
    +is turned into a comment.

    输出

    -
    1
    2
    Anything you put between  tags
    is turned into a comment.
    +
    Anything you put between  tags
    +is turned into a comment.
    diff --git a/zh-cn/tags/cycle.html b/zh-cn/tags/cycle.html index b364948c3..f807d4b52 100644 --- a/zh-cn/tags/cycle.html +++ b/zh-cn/tags/cycle.html @@ -96,10 +96,16 @@

    循环一组字符串按照它们传入的顺序打印出来。每次调用 cycle 打印下一个参数。

    基本使用

    输入

    -
    1
    2
    3
    4
    {% cycle "one", "two", "three" %}
    {% cycle "one", "two", "three" %}
    {% cycle "one", "two", "three" %}
    {% cycle "one", "two", "three" %}
    +
    {% cycle "one", "two", "three" %}
    +{% cycle "one", "two", "three" %}
    +{% cycle "one", "two", "three" %}
    +{% cycle "one", "two", "three" %}

    输出

    -
    1
    2
    3
    4
    one
    two
    three
    one
    +
    one
    +two
    +three
    +one

    cycle 可以用于:

      @@ -108,14 +114,20 @@

    参数

    一个模板中需要多个 cycle 时可以使用 “cycle 组” 参数。如果没有提供组名,使用同样参数调用的 cycle 会被认为处于同一组。

    输入

    -
    1
    2
    3
    4
    {% cycle "first": "one", "two", "three" %}
    {% cycle "second": "one", "two", "three" %}
    {% cycle "second": "one", "two", "three" %}
    {% cycle "first": "one", "two", "three" %}
    +
    {% cycle "first": "one", "two", "three" %}
    +{% cycle "second": "one", "two", "three" %}
    +{% cycle "second": "one", "two", "three" %}
    +{% cycle "first": "one", "two", "three" %}

    输出

    -
    1
    2
    3
    4
    one
    one
    two
    two
    +
    one
    +one
    +two
    +two
    diff --git a/zh-cn/tags/decrement.html b/zh-cn/tags/decrement.html index cfe96347a..8f6775e03 100644 --- a/zh-cn/tags/decrement.html +++ b/zh-cn/tags/decrement.html @@ -96,16 +96,20 @@

    创建一个新的数字类型的变量,每次调用都把它的值减一。第一次是 -1

    输入

    -
    1
    2
    3
    {% decrement variable %}
    {% decrement variable %}
    {% decrement variable %}
    +
    {% decrement variable %}
    +{% decrement variable %}
    +{% decrement variable %}

    输出

    -
    1
    2
    3
    -1
    -2
    -3
    +
    -1
    +-2
    +-3

    increment 一样,在 decrement 里声明的变量独立于 assigncapture 创建的变量。

    diff --git a/zh-cn/tags/echo.html b/zh-cn/tags/echo.html index 508ff5a6b..425cc1fa1 100644 --- a/zh-cn/tags/echo.html +++ b/zh-cn/tags/echo.html @@ -96,14 +96,15 @@

    根据表达式输出渲染 HTML。和使用 {{` expression `}} 包裹模板效果一样,不同的是 echo 可以在 liquid 标签中使用,同时也支持过滤器。

    echo

    输入

    -
    1
    2
    {% assign username = 'Bob' %}
    {% echo username | append: ", welcome to LiquidJS!" | capitalize %}
    +
    {% assign username = 'Bob' %}
    +{% echo username | append: ", welcome to LiquidJS!" | capitalize %}

    输出

    -
    1
    Bob, welcome to LiquidJS!
    +
    Bob, welcome to LiquidJS!
    diff --git a/zh-cn/tags/for.html b/zh-cn/tags/for.html index 8087d1617..bc584f169 100644 --- a/zh-cn/tags/for.html +++ b/zh-cn/tags/for.html @@ -97,94 +97,160 @@

    重复执行代码块的迭代标签。

    基本使用

    for…in

    重复执行一段代码。

    输入

    -
    1
    2
    3
    {% for product in collection.products %}
    {{ product.title }}
    {% endfor %}
    +
    {% for product in collection.products %}
    +  {{ product.title }}
    +{% endfor %}

    输出

    -
    1
    hat shirt pants
    +
    hat shirt pants

    else

    指定 for 循环的集合长度为零时执行的代码块。

    输入

    -
    1
    2
    3
    4
    5
    {% for product in collection.products %}
    {{ product.title }}
    {% else %}
    The collection is empty.
    {% endfor %}
    +
    {% for product in collection.products %}
    +  {{ product.title }}
    +{% else %}
    +  The collection is empty.
    +{% endfor %}

    输出

    -
    1
    The collection is empty.
    +
    The collection is empty.

    break

    遇到 break 标签时 for 循环停止执行。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    {% for i in (1..5) %}
    {%- if i == 4 -%}
    {% break %}
    {%- else -%}
    {{ i }}
    {%- endif -%}
    {% endfor %}
    +
    {% for i in (1..5) %}
    +  {%- if i == 4 -%}
    +    {% break %}
    +  {%- else -%}
    +    {{ i }}
    +  {%- endif -%}
    +{% endfor %}

    输出

    -
    1
    123
    +
    123

    continue

    遇到 continue 标签时跳过当前这次迭代。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    {% for i in (1..5) %}
    {%- if i == 4 -%}
    {%- continue -%}
    {%- else -%}
    {{ i }}
    {%- endif -%}
    {% endfor %}
    +
    {% for i in (1..5) %}
    +  {%- if i == 4 -%}
    +    {%- continue -%}
    +  {%- else -%}
    +    {{ i }}
    +  {%- endif -%}
    +{% endfor %}

    输出

    -
    1
    1235
    +
    1235

    forloop

    for 循环里有一个 forloop 变量可用,用来表示迭代的当前状态。

    forloop.first, forloop.lastforloop.length 属性:

    输入

    -
    1
    2
    3
    4
    5
    6
    {% for i in (1..5) %}
    {%- if forloop.first == true -%} First
    {%- elsif forloop.last == true -%} Last
    {%- else -%} {{ forloop.length }}
    {%- endif %}
    {% endfor -%}
    +
    {% for i in (1..5) %}
    +  {%- if forloop.first == true -%} First
    +  {%- elsif forloop.last == true -%} Last
    +  {%- else -%} {{ forloop.length }}
    +  {%- endif %}
    +{% endfor -%}

    输出

    -
    1
    2
    3
    4
    5
    First
    5
    5
    5
    Last
    +
    First
    +5
    +5
    +5
    +Last

    forloop.index, forloop.index0, forloop.rindexforloop.rindex0 属性:

    输入

    -
    1
    2
    3
    4
    index index0 rindex rindex0
    {% for i in (1..5) %}
    {{- forloop.index }} {{ forloop.index0 }} {{ forloop.rindex }} {{ forloop.rindex0 }}
    {% endfor -%}
    +
    index index0 rindex rindex0
    +{% for i in (1..5) %}
    +  {{- forloop.index }}     {{ forloop.index0 }}      {{ forloop.rindex }}      {{ forloop.rindex0 }}
    +{% endfor -%}

    输出

    -
    1
    2
    3
    4
    5
    6
    index index0 rindex rindex0
    1 0 5 4
    2 1 4 3
    3 2 3 2
    4 3 2 1
    5 4 1 0
    +
    index index0 rindex rindex0
    +1     0      5      4
    +2     1      4      3
    +3     2      3      2
    +4     3      2      1
    +5     4      1      0

    参数

    limit

    限制循环执行的次数。

    输入

    -
    1
    2
    3
    4
    <!-- if array = [1,2,3,4,5,6] -->
    {% for item in array limit:2 %}
    {{- item -}}
    {% endfor %}
    +
    <!-- if array = [1,2,3,4,5,6] -->
    +{% for item in array limit:2 %}
    +  {{- item -}}
    +{% endfor %}

    输出

    -
    1
    12
    +
    12

    offset

    从指定的下标处开始循环。

    输入

    -
    1
    2
    3
    4
    <!-- for array = [1,2,3,4,5,6] -->
    {% for item in array offset:2 %}
    {{- item -}}
    {% endfor %}
    +
    <!-- for array = [1,2,3,4,5,6] -->
    +{% for item in array offset:2 %}
    +  {{- item -}}
    +{% endfor %}

    输出

    -
    1
    3456
    +
    3456

    offset:continue

    v9.33.0

    offset 的值可以是 continue,用来继续上一次循环。例如:

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    <!-- for array = [1,2,3,4,5,6] -->
    {% for item in array limit:2 %}
    {{- item -}}
    {% endfor%}
    {% for item in array offset:continue %}
    {{- item -}}
    {% endfor%}
    +
    <!-- for array = [1,2,3,4,5,6] -->
    +{% for item in array limit:2 %}
    +  {{- item -}}
    +{% endfor%}
    +{% for item in array offset:continue %}
    +  {{- item -}}
    +{% endfor%}

    输出

    -
    1
    2
    12
    3456
    +
    12
    +3456

    对同样的变量名和集合名(这个例子中是 "item-array"),存在唯一的位置记录。也就是说用新的变量名就可以开启一个新的循环:

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    <!-- for array = [1,2,3,4,5,6] -->
    {% for item in array limit:2 %}
    {{- item -}}
    {% endfor%}
    {% for item2 in array offset:continue %}
    {{- item2 -}}
    {% endfor%}
    +
    <!-- for array = [1,2,3,4,5,6] -->
    +{% for item in array limit:2 %}
    +  {{- item -}}
    +{% endfor%}
    +{% for item2 in array offset:continue %}
    +  {{- item2 -}}
    +{% endfor%}

    输出

    -
    1
    2
    12
    123456
    +
    12
    +123456

    range

    定义一个用于循环的数字范围。可以用字面量定义范围,也可以用变量定义范围。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    8
    {% for i in (3..5) %}
    {{ i }}
    {% endfor %}

    {% assign num = 4 %}
    {% for i in (1..num) %}
    {{ i }}
    {% endfor %}
    +
    {% for i in (3..5) %}
    +  {{ i }}
    +{% endfor %}
    +
    +{% assign num = 4 %}
    +{% for i in (1..num) %}
    +  {{ i }}
    +{% endfor %}

    输出

    -
    1
    2
    3 4 5
    1 2 3 4
    +
    3 4 5
    +1 2 3 4

    reversed

    反转循环的顺序。注意这个参数的拼写和过滤器 reverse 不同。

    输入

    -
    1
    2
    3
    4
    <!-- if array = [1,2,3,4,5,6] -->
    {% for item in array reversed %}
    {{ item }}
    {% endfor %}
    +
    <!-- if array = [1,2,3,4,5,6] -->
    +{% for item in array reversed %}
    +  {{ item }}
    +{% endfor %}

    输出

    -
    1
    6 5 4 3 2 1
    +
    6 5 4 3 2 1
    diff --git a/zh-cn/tags/if.html b/zh-cn/tags/if.html index d920c5e72..d69a8d7b7 100644 --- a/zh-cn/tags/if.html +++ b/zh-cn/tags/if.html @@ -96,22 +96,31 @@

    条件为 true 时执行某个代码块。

    if

    输入

    -
    1
    2
    3
    {% if product.title == "Awesome Shoes" %}
    These shoes are awesome!
    {% endif %}
    +
    {% if product.title == "Awesome Shoes" %}
    +  These shoes are awesome!
    +{% endif %}

    输出

    -
    1
    These shoes are awesome!
    +
    These shoes are awesome!

    elsif / else

    ifunless 块中添加更多的条件。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    8
    <!-- If customer.name = "anonymous" -->
    {% if customer.name == "kevin" %}
    Hey Kevin!
    {% elsif customer.name == "anonymous" %}
    Hey Anonymous!
    {% else %}
    Hi Stranger!
    {% endif %}
    +
    <!-- If customer.name = "anonymous" -->
    +{% if customer.name == "kevin" %}
    +  Hey Kevin!
    +{% elsif customer.name == "anonymous" %}
    +  Hey Anonymous!
    +{% else %}
    +  Hi Stranger!
    +{% endif %}

    输出

    -
    1
    Hey Anonymous!
    +
    Hey Anonymous!
    diff --git a/zh-cn/tags/include.html b/zh-cn/tags/include.html index 6aa081e1a..86226c7a6 100644 --- a/zh-cn/tags/include.html +++ b/zh-cn/tags/include.html @@ -98,30 +98,32 @@

    引入一个模板

    从模板 根路径 引入一个模板:

    -
    1
    {% include 'footer.liquid' %}
    +
    {% include 'footer.liquid' %}

    设置 extname 选项为 ".liquid" 后上面的 .liquid 后缀就可以省略了,等价于:

    -
    1
    {% include 'footer' %}
    +
    {% include 'footer' %}

    通过 include 渲染一个子模板时,它内部的代码可以访问父模板的变量,但父模板中不能访问它里面定义的变量。

    变量传递

    父模板里定义的变量可以通过 include 标签的参数列表传递给子模板:

    -
    1
    2
    {% assign my_variable = 'apples' %}
    {% include 'name', my_variable: my_variable, my_other_variable: 'oranges' %}
    +
    {% assign my_variable = 'apples' %}
    +{% include 'name', my_variable: my_variable, my_other_variable: 'oranges' %}

    with 参数

    使用 with...as 语法可以给子模板传递一个变量:

    -
    1
    2
    {% assign featured_product = all_products['product_handle'] %}
    {% include 'product' with featured_product as product %}
    +
    {% assign featured_product = all_products['product_handle'] %}
    +{% include 'product' with featured_product as product %}

    上面的例子中,子模板中 product 会保有父模板中的 featured_product 变量的值。

    输出和过滤器

    文件名为字符串字面量时,支持 Liquid 输出和过滤器。在拼接文件名时很方便:

    -
    1
    {% include "prefix/{{name | append: \".html\"}}" %}
    +
    {% include "prefix/{{name | append: \".html\"}}" %}
    转义

    字符串字面量里的 " 需要转义为 \",使用静态文件名可以避免这个问题,见下面的 Jekyll-like 文件名。

    Jekyll-like 文件名

    设置 dynamicPartialsfalse 来启用 Jekyll-like 文件名,这时文件名不需要用引号包含,会被当作字面量处理。 这样的字符串里面仍然支持 Liquid 输出和过滤器,例如:

    -
    1
    {% include prefix/{{ page.my_variable }}/suffix %}
    +
    {% include prefix/{{ page.my_variable }}/suffix %}

    这样文件名里的 " 就不用转义了。

    -
    1
    {% include prefix/{{name | append: ".html"}} %}
    +
    {% include prefix/{{name | append: ".html"}} %}

    Jekyll include

    v9.33.0

    @@ -132,18 +134,24 @@
  • 参数放到了 include 变量下,而非当前作用域。
  • 例如下面的模板:

    -
    1
    {% include article.html header="HEADER" content="CONTENT" %}
    +
    {% include article.html header="HEADER" content="CONTENT" %}

    其中 article.html 的内容是:

    -
    1
    2
    3
    4
    <article>
    <header>{{include.header}}</header>
    {{include.content}}
    </article>
    +
    <article>
    +  <header>{{include.header}}</header>
    +  {{include.content}}
    +</article>

    注意我们通过 include.header 引用第一个参数,而不是 header。输出如下:

    -
    1
    2
    3
    4
    <article>
    <header>HEADER</header>
    CONTENT
    </article>
    +
    <article>
    +  <header>HEADER</header>
    +  CONTENT
    +</article>
    diff --git a/zh-cn/tags/increment.html b/zh-cn/tags/increment.html index d8e6da9a4..a00e0927c 100644 --- a/zh-cn/tags/increment.html +++ b/zh-cn/tags/increment.html @@ -96,23 +96,34 @@

    创建一个新的数字类型的变量,每次调用都把它的值加一。第一次为 0

    输入

    -
    1
    2
    3
    {% increment my_counter %}
    {% increment my_counter %}
    {% increment my_counter %}
    +
    {% increment my_counter %}
    +{% increment my_counter %}
    +{% increment my_counter %}

    输出

    -
    1
    2
    3
    0
    1
    2
    +
    0
    +1
    +2

    increment 里声明的变量独立于 assigncapture 创建的变量。

    下面的例子中通过 assign 创建了变量 var。然后用 increment 标签在同名变量上多次递增。注意 increment 标签不会影响 assign 创建的 var 的值。

    输入

    -
    1
    2
    3
    4
    5
    {% assign var = 10 %}
    {% increment var %}
    {% increment var %}
    {% increment var %}
    {{ var }}
    +
    {% assign var = 10 %}
    +{% increment var %}
    +{% increment var %}
    +{% increment var %}
    +{{ var }}

    输出

    -
    1
    2
    3
    4
    0
    1
    2
    10
    +
    0
    +1
    +2
    +10
    diff --git a/zh-cn/tags/inline_comment.html b/zh-cn/tags/inline_comment.html index 640a51394..b9c181dde 100644 --- a/zh-cn/tags/inline_comment.html +++ b/zh-cn/tags/inline_comment.html @@ -96,24 +96,39 @@

    在 Liquid 模板中添加注释,注释标签内的文字不会被输出。

    输入

    -
    1
    2
    3
    4
    5
    6
    7
    注释标签内的东西都不会输出。
    {% # this is an inline comment %}
    但每行都必须以 '#' 开头。
    {%
    # this is a comment
    # that spans multiple lines
    %}
    +
    注释标签内的东西都不会输出。
    +{% # this is an inline comment %}
    +但每行都必须以 '#' 开头。
    +{%
    +  # this is a comment
    +  # that spans multiple lines
    +%}

    输出

    -
    1
    2
    注释标签内的东西都不会输出。
    但每行都必须以 '#' 开头。
    +
    注释标签内的东西都不会输出。
    +但每行都必须以 '#' 开头。

    liquid 标签里也可以使用注释标签。

    -
    1
    2
    3
    4
    5
    6
    7
    8
    {% liquid
    # required args
    assign product = collection.products.first

    # optional args
    assign should_show_border = should_show_border | default: true
    assign should_highlight = should_highlight | default: false
    %}
    +
    {% liquid
    +  # required args
    +  assign product = collection.products.first
    +
    +  # optional args
    +  assign should_show_border = should_show_border | default: true
    +  assign should_highlight = should_highlight | default: false
    +%}

    但注释标签不能用于把其他标签注释掉。这时应该使用 comment 标签来临时禁用其他标签。

    输入

    -
    1
    2
    {%- # {% echo 'Welcome to LiquidJS!' %} -%}
    {% comment %}{% echo 'Welcome to LiquidJS!' %}{% endcomment %}
    +
    {%- # {% echo 'Welcome to LiquidJS!' %} -%}
    +{% comment %}{% echo 'Welcome to LiquidJS!' %}{% endcomment %}

    输出

    -
    1
    -%}
    +
    -%}
    diff --git a/zh-cn/tags/layout.html b/zh-cn/tags/layout.html index 106f21c55..1d8745edb 100644 --- a/zh-cn/tags/layout.html +++ b/zh-cn/tags/layout.html @@ -95,23 +95,29 @@

    v1.9.1

    使用布局模板

    套用模板 根路径 下的某个布局模板中。

    -
    1
    {% layout 'footer.liquid' %}
    +
    {% layout 'footer.liquid' %}

    设置 extname 选项为 ".liquid" 后上面的 .liquid 后缀就可以省略了,等价于:

    -
    1
    {% layout 'footer' %}
    +
    {% layout 'footer' %}

    通过 layout 渲染一个子模板时,它内部的代码可以访问父模板的变量,但父模板中不能访问它里面定义的变量。

    变量传递

    当前模板里定义的变量可以通过 layout 标签的参数列表传递给布局模板:

    -
    1
    2
    {% assign my_variable = 'apples' %}
    {% layout 'name', my_variable: my_variable, my_other_variable: 'oranges' %}
    +
    {% assign my_variable = 'apples' %}
    +{% layout 'name', my_variable: my_variable, my_other_variable: 'oranges' %}

    布局模板中可以包含若干 block 标签,这些 block 渲染时会按照子模板提供的内容进行填充。例如我们有布局模板 default-layout.liquid

    -
    1
    2
    3
    Header
    {% block content %}My default content{% endblock %}
    Footer
    +
    Header
    +{% block content %}My default content{% endblock %}
    +Footer

    它被子模板 page.liquid 通过 layout 标签引用:

    -
    1
    2
    {% layout "default-layout" %}
    {% block content %}My page content{% endblock %}
    +
    {% layout "default-layout" %}
    +{% block content %}My page content{% endblock %}

    page.liquid 的渲染结果将会是:

    -
    1
    2
    3
    Header
    My page content
    Footer
    +
    Header
    +My page content
    +Footer
    • 一个布局模板中可以定义多个块;
    • @@ -122,7 +128,7 @@ diff --git a/zh-cn/tags/liquid.html b/zh-cn/tags/liquid.html index e7a59bc8d..bbd0c804a 100644 --- a/zh-cn/tags/liquid.html +++ b/zh-cn/tags/liquid.html @@ -96,14 +96,23 @@

      通过 liquid 标签可以在一个分隔符中使用多个标签, 使 Liquid 逻辑书写更简洁。

      liquid

      输入

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      {% liquid
      assign names = 'Bob, Sally' | split: ', '

      for name in names
      echo 'Hello, ' | append: name
      unless forloop.last
      echo ', '
      endunless
      endfor
      %}
      +
      {% liquid
      +  assign names = 'Bob, Sally' | split: ', '
      +
      +  for name in names
      +    echo 'Hello, ' | append: name
      +    unless forloop.last
      +      echo ', '
      +    endunless
      +  endfor
      +%}

      输出

      -
      1
      Hello, Bob, Hello Sally
      +
      Hello, Bob, Hello Sally
      diff --git a/zh-cn/tags/overview.html b/zh-cn/tags/overview.html index 2064573aa..0f780ee5c 100644 --- a/zh-cn/tags/overview.html +++ b/zh-cn/tags/overview.html @@ -131,7 +131,7 @@
      - +
      diff --git a/zh-cn/tags/raw.html b/zh-cn/tags/raw.html index 1850f28fd..92b8b935b 100644 --- a/zh-cn/tags/raw.html +++ b/zh-cn/tags/raw.html @@ -96,14 +96,17 @@

      raw 标签可以暂时禁用 LiquidJS 的语法。生成和 Liquid 冲突的语言(比如 Nunjucks、Handlebars)时很有用。

      输入

      -
      1
      2
      3
      4
      {% raw %}
      In Handlebars, {{ this }} will be HTML-escaped, but
      {{{ that }}} will not.
      {% endraw %}
      +
      {% raw %}
      +  In Handlebars, {{ this }} will be HTML-escaped, but
      +  {{{ that }}} will not.
      +{% endraw %}

      输出

      -
      1
      In Handlebars, {{ this }} will be HTML-escaped, but {{{ that }}} will not.
      +
      In Handlebars, {{ this }} will be HTML-escaped, but {{{ that }}} will not.
      diff --git a/zh-cn/tags/render.html b/zh-cn/tags/render.html index fec473ab3..b06a202a9 100644 --- a/zh-cn/tags/render.html +++ b/zh-cn/tags/render.html @@ -95,24 +95,27 @@

      v9.2.0

      基本使用

      渲染一个模板

      从模板 根路径 引入一个模板:

      -
      1
      {% render 'footer.liquid' %}
      +
      {% render 'footer.liquid' %}

      设置 extname 选项为 ".liquid" 后上面的 .liquid 后缀就可以省略了,等价于:

      -
      1
      {% render 'footer' %}
      +
      {% render 'footer' %}
      变量作用域

      通过 render 渲染一个子模板时,它内部的代码不能访问父模板的变量,父模板中也不能访问它里面定义的变量。这个封装会让模板代码更容易理解和维护。

      变量传递

      父模板里定义的变量可以通过 render 标签的参数列表传递给子模板:

      -
      1
      2
      {% assign my_variable = 'apples' %}
      {% render 'name', my_variable: my_variable, my_other_variable: 'oranges' %}
      +
      {% assign my_variable = 'apples' %}
      +{% render 'name', my_variable: my_variable, my_other_variable: 'oranges' %}

      全局变量 不需要传递,所有文件都可以访问它们。

      参数

      with 参数

      使用 with...as 语法可以给子模板传递一个变量:

      -
      1
      2
      {% assign featured_product = all_products['product_handle'] %}
      {% render 'product' with featured_product as product %}
      +
      {% assign featured_product = all_products['product_handle'] %}
      +{% render 'product' with featured_product as product %}

      上面的例子中,子模板中 product 会保有父模板中的 featured_product 变量的值。

      for 参数

      for...as 语法可以对可枚举对象的每一个值渲染一次子模板:

      -
      1
      2
      {% assign variants = product.variants %}
      {% render 'variant' for variants as variant %}
      +
      {% assign variants = product.variants %}
      +{% render 'variant' for variants as variant %}

      上面的例子中,对 product 的每个 variants 是指都会渲染一次子模板。子模板中 variant 变量会保有父模板中的 product.variants 中对应元素的值。

      forloop 对象

      使用 for 参数时,在子模板中可以访问 forloop 对象。

      @@ -121,7 +124,7 @@ diff --git a/zh-cn/tags/tablerow.html b/zh-cn/tags/tablerow.html index 85cd0a811..177529ab5 100644 --- a/zh-cn/tags/tablerow.html +++ b/zh-cn/tags/tablerow.html @@ -96,30 +96,101 @@

      生成一个 HTML 表示,上下必须用 <table></table> HTML 标签包裹起来。

      基本使用

      输入

      -
      1
      2
      3
      4
      5
      <table>
      {% tablerow product in collection.products %}
      {{ product.title }}
      {% endtablerow %}
      </table>
      +
      <table>
      +{% tablerow product in collection.products %}
      +  {{ product.title }}
      +{% endtablerow %}
      +</table>

      输出

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      <table>
      <tr class="row1">
      <td class="col1">
      Cool Shirt
      </td>
      <td class="col2">
      Alien Poster
      </td>
      <td class="col3">
      Batman Poster
      </td>
      <td class="col4">
      Bullseye Shirt
      </td>
      <td class="col5">
      Another Classic Vinyl
      </td>
      <td class="col6">
      Awesome Jeans
      </td>
      </tr>
      </table>
      +
      <table>
      +  <tr class="row1">
      +    <td class="col1">
      +      Cool Shirt
      +    </td>
      +    <td class="col2">
      +      Alien Poster
      +    </td>
      +    <td class="col3">
      +      Batman Poster
      +    </td>
      +    <td class="col4">
      +      Bullseye Shirt
      +    </td>
      +    <td class="col5">
      +      Another Classic Vinyl
      +    </td>
      +    <td class="col6">
      +      Awesome Jeans
      +    </td>
      +  </tr>
      +</table>

      参数

      cols

      定义表格的列数。

      输入

      -
      1
      2
      3
      {% tablerow product in collection.products cols:2 %}
      {{ product.title }}
      {% endtablerow %}
      +
      {% tablerow product in collection.products cols:2 %}
      +  {{ product.title }}
      +{% endtablerow %}

      输出

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <table>
      <tr class="row1">
      <td class="col1">
      Cool Shirt
      </td>
      <td class="col2">
      Alien Poster
      </td>
      </tr>
      <tr class="row2">
      <td class="col1">
      Batman Poster
      </td>
      <td class="col2">
      Bullseye Shirt
      </td>
      </tr>
      <tr class="row3">
      <td class="col1">
      Another Classic Vinyl
      </td>
      <td class="col2">
      Awesome Jeans
      </td>
      </tr>
      </table>
      +
      <table>
      +  <tr class="row1">
      +    <td class="col1">
      +      Cool Shirt
      +    </td>
      +    <td class="col2">
      +      Alien Poster
      +    </td>
      +  </tr>
      +  <tr class="row2">
      +    <td class="col1">
      +      Batman Poster
      +    </td>
      +    <td class="col2">
      +      Bullseye Shirt
      +    </td>
      +  </tr>
      +  <tr class="row3">
      +    <td class="col1">
      +      Another Classic Vinyl
      +    </td>
      +    <td class="col2">
      +      Awesome Jeans
      +    </td>
      +  </tr>
      +</table>

      limit

      限制迭代次数。

      -
      1
      2
      3
      {% tablerow product in collection.products cols:2 limit:3 %}
      {{ product.title }}
      {% endtablerow %}
      +
      {% tablerow product in collection.products cols:2 limit:3 %}
      +  {{ product.title }}
      +{% endtablerow %}

      offset

      从指定的下标处开始循环。

      -
      1
      2
      3
      {% tablerow product in collection.products cols:2 offset:3 %}
      {{ product.title }}
      {% endtablerow %}
      +
      {% tablerow product in collection.products cols:2 offset:3 %}
      +  {{ product.title }}
      +{% endtablerow %}

      range

      定义一个用于循环的数字范围。可以用字面量定义范围,也可以用变量定义范围。

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      <!--variable number example-->

      {% assign num = 4 %}
      <table>
      {% tablerow i in (1..num) %}
      {{ i }}
      {% endtablerow %}
      </table>

      <!--literal number example-->

      <table>
      {% tablerow i in (3..5) %}
      {{ i }}
      {% endtablerow %}
      </table>
      +
      <!--variable number example-->
      +
      +{% assign num = 4 %}
      +<table>
      +{% tablerow i in (1..num) %}
      +  {{ i }}
      +{% endtablerow %}
      +</table>
      +
      +<!--literal number example-->
      +
      +<table>
      +{% tablerow i in (3..5) %}
      +  {{ i }}
      +{% endtablerow %}
      +</table>
      diff --git a/zh-cn/tags/unless.html b/zh-cn/tags/unless.html index 7dbfc7049..1f36f1da6 100644 --- a/zh-cn/tags/unless.html +++ b/zh-cn/tags/unless.html @@ -96,17 +96,21 @@

      if 相反 —— 条件 不满足 时执行代码块。

      输入

      -
      1
      2
      3
      {% unless product.title == "Awesome Shoes" %}
      These shoes are not awesome.
      {% endunless %}
      +
      {% unless product.title == "Awesome Shoes" %}
      +  These shoes are not awesome.
      +{% endunless %}

      输出

      -
      1
      These shoes are not awesome.
      +
      These shoes are not awesome.

      等价于执行下面的代码:

      -
      1
      2
      3
      {% if product.title != "Awesome Shoes" %}
      These shoes are not awesome.
      {% endif %}
      +
      {% if product.title != "Awesome Shoes" %}
      +  These shoes are not awesome.
      +{% endif %}
      - +
      diff --git a/zh-cn/tutorials/access-scope-in-filters.html b/zh-cn/tutorials/access-scope-in-filters.html index 72cf8a79c..868321c4c 100644 --- a/zh-cn/tutorials/access-scope-in-filters.html +++ b/zh-cn/tutorials/access-scope-in-filters.html @@ -93,10 +93,19 @@

      注册过滤器和标签 里介绍过,可以在函数参数里直接获得过滤器的参数:

      -
      1
      2
      3
      // Usage: {{ 1 | add: 2, 3 }}
      // Output: 6
      engine.registerFilter('add', (initial, arg1, arg2) => initial + arg1 + arg2)
      +
      // Usage: {{ 1 | add: 2, 3 }}
      +// Output: 6
      +engine.registerFilter('add', (initial, arg1, arg2) => initial + arg1 + arg2)

      但有些过滤器还需要访问当前上下文的变量,比如把 URL 路径转换为完整的 URL 时,需要访问上下文的 origin 变量:

      -
      1
      2
      3
      4
      5
      6
      7
      8
      // Usage: {{ '/index.html' | fullURL }}
      // Scope: { origin: "https://liquidjs.com" }
      // Output: https://liquidjs.com/index.html

      engine.registerFilter('fullURL', function (path) {
      const origin = this.context.get(['origin'])
      return new URL(path, origin).toString()
      })
      +
      // Usage: {{ '/index.html' | fullURL }}
      +// Scope: { origin: "https://liquidjs.com" }
      +// Output: https://liquidjs.com/index.html
      +
      +engine.registerFilter('fullURL', function (path) {
      +    const origin = this.context.get(['origin'])
      +    return new URL(path, origin).toString() 
      +})

      见这个 JSFiddle:http://jsfiddle.net/ctj364up/1/

      箭头函数

      在箭头函数里 this 会绑定到当前 JavaScript 上下文,你需要用 function(){} 来替代 ()=>{} 语法,才能正确地访问 this.context

      @@ -105,7 +114,7 @@
      diff --git a/zh-cn/tutorials/caching.html b/zh-cn/tutorials/caching.html index 045c6cec8..09570886d 100644 --- a/zh-cn/tutorials/caching.html +++ b/zh-cn/tutorials/caching.html @@ -96,19 +96,34 @@

      LiquidJS 在这一方面比较灵活,提供了多种不同的方式来达到提升性能的目的。

      手动缓存

      .parse(), .parseFile(), .parseFileSync() API 可以用来把字符串或文件解析成模板。得到的模板可以用不同的数据去重复地渲染得到不同的 HTML。

      从字符串解析:

      -
      1
      2
      3
      4
      var tpl = engine.parse('{{name | capitalize}}');

      engine.renderSync(tpl, {name: 'alice'}) // 'Alice'
      engine.renderSync(tpl, {name: 'bob'}) // 'Bob'
      +
      var tpl = engine.parse('{{name | capitalize}}');
      +
      +engine.renderSync(tpl, {name: 'alice'}) // 'Alice'
      +engine.renderSync(tpl, {name: 'bob'}) // 'Bob'

      从文件解析:

      -
      1
      2
      3
      4
      var tpl = engine.parseFileSync('hello');    // contents of `hello.liquid`: {{name}}

      engine.renderSync(tpl, {name: 'alice'}) // 'Alice'
      engine.renderSync(tpl, {name: 'bob'}) // 'Bob'
      +
      var tpl = engine.parseFileSync('hello');    // contents of `hello.liquid`: {{name}}
      +
      +engine.renderSync(tpl, {name: 'alice'}) // 'Alice'
      +engine.renderSync(tpl, {name: 'bob'}) // 'Bob'

      上述代码中字符串或文件只被解析了一次,可以反复利用去渲染不同的数据。有很多模板文件时可以把 tpl 变量存在 Map 中,后续再 .render() 时直接从 Map 中拿出解析好的模板去渲染。

      cache 选项

      如果你只用 .renderFile().renderFileSync() 也可以直接设置 cache 选项,LiquidJS 会帮你缓存。

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      var { Liquid } = require('liquidjs');
      var engine = new Liquid({
      cache: true
      });

      // LiquidJS 将会解析 hello.liquid 然后用 {name: 'alice'} 渲染它
      engine.renderFileSync('hello', {name: 'alice'})

      // LiquidJS 会找到上次 hello.liquid 解析的结果模板,再用 {name: 'bob'} 渲染它
      engine.renderFileSync('hello', {name: 'bob'})
      +
      var { Liquid } = require('liquidjs');
      +var engine = new Liquid({
      +    cache: true
      +});
      +
      +// LiquidJS 将会解析 hello.liquid 然后用 {name: 'alice'} 渲染它
      +engine.renderFileSync('hello', {name: 'alice'})
      +
      +// LiquidJS 会找到上次 hello.liquid 解析的结果模板,再用 {name: 'bob'} 渲染它
      +engine.renderFileSync('hello', {name: 'bob'})
      diff --git a/zh-cn/tutorials/changelog.html b/zh-cn/tutorials/changelog.html index ce37a7490..edcd8cdbf 100644 --- a/zh-cn/tutorials/changelog.html +++ b/zh-cn/tutorials/changelog.html @@ -746,7 +746,7 @@
    diff --git a/zh-cn/tutorials/contribution-guidelines.html b/zh-cn/tutorials/contribution-guidelines.html index 79b5c074a..28c5ee779 100644 --- a/zh-cn/tutorials/contribution-guidelines.html +++ b/zh-cn/tutorials/contribution-guidelines.html @@ -102,7 +102,7 @@
    - +
    diff --git a/zh-cn/tutorials/differences.html b/zh-cn/tutorials/differences.html index 111e77a8a..2541aa4db 100644 --- a/zh-cn/tutorials/differences.html +++ b/zh-cn/tutorials/differences.html @@ -126,7 +126,7 @@ diff --git a/zh-cn/tutorials/dos.html b/zh-cn/tutorials/dos.html index 07fca8acb..c2091a47e 100644 --- a/zh-cn/tutorials/dos.html +++ b/zh-cn/tutorials/dos.html @@ -94,23 +94,32 @@

    当模板或数据上下文不可信时,启用DoS预防选项至关重要。LiquidJS 提供了三个选项用于此目的:parseLimitrenderLimitmemoryLimit

    TL;DR

    设置这些选项可以在很大程度上确保你的 LiquidJS 实例不会长时间挂起或消耗过多内存。这些限制基于可用的 JavaScript API,因此它们不是精确的硬性限制,而是确保你的进程不会失败或挂起的阈值。

    -
    1
    2
    3
    4
    5
    const liquid = new Liquid({
    parseLimit: 1e8, // 每次渲染的模板的典型大小
    renderLimit: 1000, // 每次渲染最多 1s
    memoryLimit: 1e9, // LiquidJS 可用的内存(1e9 对应 1GB)
    })
    +
    const liquid = new Liquid({
    +    parseLimit: 1e8, // 每次渲染的模板的典型大小
    +    renderLimit: 1000, // 每次渲染最多 1s
    +    memoryLimit: 1e9, // LiquidJS 可用的内存(1e9 对应 1GB)
    +})

    parseLimit

    parseLimit 限制每次 .parse() 调用中解析的模板大小(字符长度),包括引用的 partials 和 layouts。由于 LiquidJS 解析模板字符串的时间复杂度接近 O(n),限制模板总长度通常就足够了。

    普通电脑可以很容易处理 1e8(100M)个字符的模板。

    renderLimit

    仅限制模板大小是不够的,因为在渲染时可能会出现动态的数组和循环。renderLimit 通过限制每次 render() 调用的时间来缓解这些问题。

    -
    1
    2
    3
    {%- for i in (1..10000000) -%}
    order: {{i}}
    {%- endfor -%}
    +
    {%- for i in (1..10000000) -%}
    +    order: {{i}}
    +{%- endfor -%}

    渲染时间是在渲染每个模板之前检查的。在上面的例子中,循环中有两个模板:order:{{i}},因此会检查 2x10000000 次。

    单个模板内的标签和过滤器仍然可能把进程挂起。要完全控制渲染过程,建议使用类似 paralleljs 的进程管理器。

    memoryLimit

    即使模板和迭代次数较少,内存使用量也可能呈指数增长。在下面的示例中,内存会在每次迭代中翻倍:

    -
    1
    2
    3
    4
    {% assign array = "1,2,3" | split: "," %}
    {% for i in (1..32) %}
    {% assign array = array | concat: array %}
    {% endfor %}
    +
    {% assign array = "1,2,3" | split: "," %}
    +{% for i in (1..32) %}
    +    {% assign array = array | concat: array %}
    +{% endfor %}

    memoryLimit 限制内存敏感的过滤器,以防止过度的内存分配。由于 JavaScript 使用 GC 来管理内存memoryLimit 仅限制 LiquidJS 中内存敏感过滤器分配的对象总数,因此可能无法反映实际的内存占用。

    diff --git a/zh-cn/tutorials/drops.html b/zh-cn/tutorials/drops.html index 149c6f622..967b35c2a 100644 --- a/zh-cn/tutorials/drops.html +++ b/zh-cn/tutorials/drops.html @@ -96,7 +96,23 @@
    JavaScript 中的 Drop

    Drop 接口在 LiquidJS 中实现方式与内置过滤器和其他模板功能不同。由于 LiquidJS 在 JavaScript 中运行,自定义 Drop 在 JavaScript 中一定需要重新实现。JavaScript 类与 Ruby 类之间没有兼容性可言。

    -

    基本用法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import { Liquid, Drop } from 'liquidjs'

    class SettingsDrop extends Drop {
    constructor() {
    super()
    this.foo = 'FOO'
    }
    bar() {
    return 'BAR'
    }
    }

    const engine = new Liquid()
    const template = `foo: {{settings.foo}}, bar: {{settings.bar}}`
    const context = { settings: new SettingsDrop() }
    // 输出: "foo: FOO, bar: BAR"
    engine.parseAndRender(template, context).then(html => console.log(html))
    +

    基本用法

    import { Liquid, Drop } from 'liquidjs'
    +
    +class SettingsDrop extends Drop {
    +  constructor() {
    +    super()
    +    this.foo = 'FOO'
    +  }
    +  bar() {
    +    return 'BAR'
    +  }
    +}
    +
    +const engine = new Liquid()
    +const template = `foo: {{settings.foo}}, bar: {{settings.bar}}`
    +const context = { settings: new SettingsDrop() }
    +// 输出: "foo: FOO, bar: BAR"
    +engine.parseAndRender(template, context).then(html => console.log(html))

    Runkit 链接

    如上所示,除了从上下文作用域中读取属性外,还可以调用方法。您只需创建一个继承自 Drop 的自定义类。

    @@ -104,19 +120,93 @@

    liquidMethodMissing

    如果属性名不能静态地确定的情况下,可以利用 liquidMethodMissing 来动态解析变量的值。

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import { Liquid, Drop } from 'liquidjs'

    class SettingsDrop extends Drop {
    liquidMethodMissing(key) {
    return key.toUpperCase()
    }
    }

    const engine = new Liquid()
    // 输出: "COO"
    engine.parseAndRender("{{settings.coo}}", { settings: new SettingsDrop() })
    .then(html => console.log(html))
    +
    import { Liquid, Drop } from 'liquidjs'
    +
    +class SettingsDrop extends Drop {
    +  liquidMethodMissing(key) {
    +    return key.toUpperCase()
    +  }
    +}
    +
    +const engine = new Liquid()
    +// 输出: "COO"
    +engine.parseAndRender("{{settings.coo}}", { settings: new SettingsDrop() })
    +  .then(html => console.log(html))

    liquidMethodMissing 支持 Promise,这意味着您可以在其中进行异步调用。一个更有用的例子是通过使用 Drop 动态地从数据库获取值。通过使用 Drop,您可以避免将每个属性都硬编码到上下文中。例如:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import { Liquid, Drop } from 'liquidjs'

    class DBDrop extends Drop {
    async liquidMethodMissing(key) {
    const record = await db.getRecordByKey(key)
    return record.value
    }
    }

    const engine = new Liquid()
    const context = { db: new DBDrop() }
    engine.parseAndRender("{{db.coo}}", context).then(html => console.log(html))
    +
    import { Liquid, Drop } from 'liquidjs'
    +
    +class DBDrop extends Drop {
    +  async liquidMethodMissing(key) {
    +    const record = await db.getRecordByKey(key)
    +    return record.value
    +  }
    +}
    +
    +const engine = new Liquid()
    +const context = { db: new DBDrop() }
    +engine.parseAndRender("{{db.coo}}", context).then(html => console.log(html))

    valueOf

    Drop 可以实现一个 valueOf() 方法,用于在输出中替换自身。例如:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import { Liquid, Drop } from 'liquidjs'

    class ColorDrop extends Drop {
    valueOf() {
    return 'red'
    }
    }

    const engine = new Liquid()
    const context = { color: new ColorDrop() }
    // 输出: "red"
    engine.parseAndRender("{{color}}", context).then(html => console.log(html))
    +
    import { Liquid, Drop } from 'liquidjs'
    +
    +class ColorDrop extends Drop {
    +  valueOf() {
    +    return 'red'
    +  }
    +}
    +
    +const engine = new Liquid()
    +const context = { color: new ColorDrop() }
    +// 输出: "red"
    +engine.parseAndRender("{{color}}", context).then(html => console.log(html))

    toLiquid

    toLiquid() 不是 Drop 的方法,但它可以用于返回一个 Drop。在您有一个上下文中固定结构且不能更改其值的情况下,您可以实现 toLiquid(),以便让 LiquidJS 使用返回的值而不是自身来渲染模板。

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import { Liquid, Drop } from 'liquidjs'

    const context = {
    person: {
    firstName: "Jun",
    lastName: "Yang",
    name: "Jun Yang",
    toLiquid: () => ({
    firstName: this.firstName,
    lastName: this.lastName,
    // 使用不同的 `name`
    name: "Yang, Jun"
    })
    }
    }

    const engine = new Liquid()
    // 输出: "Yang, Jun"
    engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))
    +
    import { Liquid, Drop } from 'liquidjs'
    +
    +const context = {
    +  person: {
    +    firstName: "Jun",
    +    lastName: "Yang",
    +    name: "Jun Yang",
    +    toLiquid: () => ({
    +      firstName: this.firstName,
    +      lastName: this.lastName,
    +      // 使用不同的 `name`
    +      name: "Yang, Jun"
    +    })
    +  }
    +}
    +
    +const engine = new Liquid()
    +// 输出: "Yang, Jun"
    +engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))

    当然,您还可以在 toLiquid() 方法中返回一个 PersonDrop 实例,并在 PersonDrop 中实现此功能:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import { Liquid, Drop } from 'liquidjs'

    class PersonDrop extends Drop {
    constructor(person) {
    super()
    this.person = person
    }
    name() {
    return this.person.lastName + ", " + this.person.firstName
    }
    }

    const context = {
    person: {
    firstName: "Jun",
    lastName: "Yang",
    name: "Jun Yang",
    toLiquid: function () { return new PersonDrop(this) }
    }
    }

    const engine = new Liquid()
    // 输出: "Yang, Jun"
    engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))
    +
    import { Liquid, Drop } from 'liquidjs'
    +
    +class PersonDrop extends Drop {
    +  constructor(person) {
    +    super()
    +    this.person = person
    +  }
    +  name() {
    +    return this.person.lastName + ", " + this.person.firstName
    +  }
    +}
    +
    +const context = {
    +  person: {
    +    firstName: "Jun",
    +    lastName: "Yang",
    +    name: "Jun Yang",
    +    toLiquid: function () { return new PersonDrop(this) }
    +  }
    +}
    +
    +const engine = new Liquid()
    +// 输出: "Yang, Jun"
    +engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))
    toLiquid()valueOf() 的区别
    • valueOf() 通常用来定义当前变量如何渲染,toLiquid() 通常用来把一个对象转换为 Drop 或另一个提供给模板的 scope。
    • @@ -126,22 +216,28 @@

      特殊 Drop

      LiquidJS 本身实现了几个内置 Drop,以促进模板编写。此部分与 Shopify Liquid 兼容,因为我们需要模板具有可移植性。

      blank

      用于检查字符串变量是否为 falsenullundefined、空字符串或字符串仅包含空白字符。

      -
      1
      2
      3
      {% unless author == blank %}
      {{author}}
      {% endif %}
      +
      {% unless author == blank %}
      +    {{author}}
      +{% endif %}

      empty

      用于检查数组、字符串或对象是否为空。

      -
      1
      2
      3
      {% if authors == empty %}
      作者列表为空
      {% endif %}
      +
      {% if authors == empty %}
      +    作者列表为空
      +{% endif %}
      empty 的实现

      对于数组和字符串,LiquidJS 检查它们的 .length 属性。对于对象,LiquidJS 调用 Object.keys() 来检查它是否有键。

      nil

      nil Drop 用于检查变量是否未定义或定义为 nullundefined,本质上等同于 JavaScript 的 == null 检查。

      -
      1
      2
      3
      {% if notexist == nil %}
      空变量
      {% endif %}
      +
      {% if notexist == nil %}
      +    空变量
      +{% endif %}

      其他 Drop

      仍然有一些特定标签的 Drop,例如 forlooptablerowloopblock,这些在各自的标签文档中有详细介绍。

      diff --git a/zh-cn/tutorials/escaping.html b/zh-cn/tutorials/escaping.html index 0dcaee11c..05423cef9 100644 --- a/zh-cn/tutorials/escaping.html +++ b/zh-cn/tutorials/escaping.html @@ -99,37 +99,43 @@

      HTML 转义

      默认情况下输出是不转义的,但你可以用 escape 过滤器来做 HTML 转义:

      输入

      -
      1
      {{ "1 < 2" | escape }}
      +
      {{ "1 < 2" | escape }}

      输出

      -
      1
      1 &lt; 2
      +
      1 &lt; 2

      LiquidJS 也提供了其他过滤器来支持不同的转义需求:escape_once, newline_to_br, strip_html

      当输出的变量不被信任时,可以把 outputEscape 参数设置为 "escape" 来启用默认 HTML 转义。这种情况下,如果你需要某个输出不被转义,则需要使用 raw 过滤器:

      输入

      -
      1
      2
      {{ "1 < 2" }}
      {{ "<button>OK</button>" | raw }}
      +
      {{ "1 < 2" }}
      +{{ "<button>OK</button>" | raw }}

      输出

      -
      1
      2
      1 &lt; 2
      <button>OK</button>
      +
      1 &lt; 2
      +<button>OK</button>

      Liquid 转义

      为了输出 Liquid 的特殊字符比如 {{{%,你需要 raw 标签。

      输入

      -
      1
      2
      3
      4
      {% raw %}
      In LiquidJS, {{ this | escape }} will be HTML-escaped, but
      {{{ that }}} will not.
      {% endraw %}
      +
      {% raw %}
      +  In LiquidJS, {{ this | escape }} will be HTML-escaped, but
      +  {{{ that }}} will not.
      +{% endraw %}

      输出

      -
      1
      2
      In LiquidJS, {{ this | escape }} will be HTML-escaped, but
      {{{ that }}} will not.
      +
      In LiquidJS, {{ this | escape }} will be HTML-escaped, but
      +{{{ that }}} will not.

      Within strings literals in LiquidJS template, \ can be used to escape special characters in string syntax. For example:

      输入

      -
      1
      {{ "\"" }}
      +
      {{ "\"" }}

      输出

      -
      1
      "
      +
      "
      diff --git a/zh-cn/tutorials/intro-to-liquid.html b/zh-cn/tutorials/intro-to-liquid.html index a974ddf4f..a8cde0d31 100644 --- a/zh-cn/tutorials/intro-to-liquid.html +++ b/zh-cn/tutorials/intro-to-liquid.html @@ -102,26 +102,30 @@

    输出

    输出 用于转换和输出变量到 HTML。下面的模板将会把 username 的值插入到 input 的 value

    -
    1
    <input type="text" name="user" value="{{username}}">
    +
    <input type="text" name="user" value="{{username}}">

    输出 里的值可以在输出之前经过若干个 过滤器 的转换。比如在变量后面追加一个字符串:

    -
    1
    {{ username | append: ", welcome to LiquidJS!" }}
    +
    {{ username | append: ", welcome to LiquidJS!" }}

    过滤器可以级联,用起来像管道一样:

    -
    1
    {{ username | append: ", welcome to LiquidJS!" | capitalize }}
    +
    {{ username | append: ", welcome to LiquidJS!" | capitalize }}

    这里 是 LiquidJS 支持的完整的过滤器列表。

    标签

    标签 用于控制模板渲染过程,操作模板变量,和其他模板交互等。例如 assign 可以用来定义一个模板中可以使用的变量:

    -
    1
    {% assign foo = "FOO" %}
    +
    {% assign foo = "FOO" %}

    一般标签成对地出现,一个开始标签和一个对应的结束标签,比如:

    -
    1
    2
    3
    4
    5
    {% if foo == "FOO" %}
    Variable `foo` equals "FOO"
    {% else %}
    Variable `foo` not equals "FOO"
    {% endif %}
    +
    {% if foo == "FOO" %}
    +    Variable `foo` equals "FOO"
    +{% else %}
    +    Variable `foo` not equals "FOO"
    +{% endif %}

    这里 是 LiquidJS 支持的完整的标签列表。

    - +
    diff --git a/zh-cn/tutorials/migrate-to-9.html b/zh-cn/tutorials/migrate-to-9.html index e439f1c9a..c339c0e80 100644 --- a/zh-cn/tutorials/migrate-to-9.html +++ b/zh-cn/tutorials/migrate-to-9.html @@ -116,7 +116,7 @@ diff --git a/zh-cn/tutorials/operators.html b/zh-cn/tutorials/operators.html index 246d86800..2d374a6db 100644 --- a/zh-cn/tutorials/operators.html +++ b/zh-cn/tutorials/operators.html @@ -106,7 +106,7 @@ diff --git a/zh-cn/tutorials/options.html b/zh-cn/tutorials/options.html index e2cf85a94..83fcca57e 100644 --- a/zh-cn/tutorials/options.html +++ b/zh-cn/tutorials/options.html @@ -93,7 +93,10 @@

    Liquid 构造函数接受一个参数对象,用来定义各种模板引擎行为。这些参数都是可选的,比如我可以指定其中一个参数 cache

    -
    1
    2
    3
    4
    const { Liquid } = require('liquidjs')
    const engine = new Liquid({
    cache: true
    })
    +
    const { Liquid } = require('liquidjs')
    +const engine = new Liquid({
    +    cache: true
    +})
    API 文档

    下面的所有选项的概述,希望了解具体的类型和签名,请前往 LiquidOptions | API.

    @@ -108,10 +111,10 @@

    注意由于历史原因这个选项叫做 dynamicPartials,但它对 layout 也起作用。

    dynamicPartials 表示是否把传给 include, render, layout 标签的文件名当做变量处理。默认为 true。例如用上下文 { file: 'foo.html' } 渲染下面的模板将会引入文件 foo.html

    -
    1
    {% include file %}
    +
    {% include file %}

    设置 dynamicPartials: false 后 LiquidJS 将会尝试去读取 file。当你的模板之间都是静态引入关系时会很有用:

    -
    1
    {% liquid foo.html %}
    +
    {% liquid foo.html %}
    常见陷阱

    LiquidJS 把这个选项默认值设为 true 以兼容于 shopify/liquid,但如果你在使用 eleventy 它会设置默认值 false (参考 Quoted Include Paths)以兼容于 Jekyll。

    @@ -125,10 +128,18 @@
  • 参数放到了 include 变量下,而非当前作用域。
  • 例如下面的模板中,name.html 没有带引号,header"HEADER"= 分隔,header 参数通过 include.header 来引用。更多详情请参考 include

    -
    1
    2
    3
    4
    5
    6
    7
    8
    // entry template
    {% include article.html header="HEADER" content="CONTENT" %}

    // article.html
    <article>
    <header>{{include.header}}</header>
    {{include.content}}
    </article>
    +
    // entry template
    +{% include article.html header="HEADER" content="CONTENT" %}
    +
    +// article.html
    +<article>
    +  <header>{{include.header}}</header>
    +  {{include.content}}
    +</article>

    extname

    extname 定义了默认的文件后缀,当传入文件名不包含后缀时自动追加。默认值是 '' 也就是说默认是禁用的。如果设置为 .liquid

    -
    1
    2
    {% render "foo" %}  没有后缀,添加 ".liquid" 并加载 foo.liquid
    {% render "foo.html" %} 已经有后缀了,直接加载 foo.html
    +
    {% render "foo" %}  没有后缀,添加 ".liquid" 并加载 foo.liquid
    +{% render "foo.html" %} 已经有后缀了,直接加载 foo.html
    旧版行为

    在 2.0.1 之前,extname 默认值为 .liquid。要禁用它需要明确设置为 extname: ''。详情参考 #41

    @@ -148,7 +159,7 @@

    dateFormat 用于指定输出日期的默认格式. %A, %B %-e, %Y at %-l:%M %P %z 如果未指定,将使用. 例如,设置 dateFormat: %Y-%m-%dT%H:%M:%S:%LZ 以输出 [JavaSrcipt Date.toJson()][https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON] 格式.

    换行和缩进

    greedy, trimOutputLeft, trimOutputRight, trimTagLeft, trimTagRight 选项用来移除 Liquid 语法周围的换行和缩进,详情请参考 Whitespace Control

    自定义分隔符

    outputDelimiterLeft, outputDelimiterRight, tagDelimiterLeft, tagDelimiterRight 用来自定义 LiquidJS 中 标签和过滤器 的分隔符。例如设置了 outputDelimiterLeft: <%=, outputDelimiterRight: %> 后我们可以避免跟其他模板引擎冲突:

    -
    1
    <%= username | append: ", welcome to LiquidJS!" %>
    +
    <%= username | append: ", welcome to LiquidJS!" %>

    严格模式

    strictFilters 用来启用过滤器的严格模式,如果设置为 true 过滤器不存在时解析会抛出异常。默认为 false,这时会跳过不存在的过滤器。

    strictVariables 用来启用变量严格模式。如果设置为 true 变量不存在时渲染会抛出异常,默认为 false 这时不存在的变量会被渲染为空字符串。

    @@ -160,7 +171,7 @@
    diff --git a/zh-cn/tutorials/parse-parameters.html b/zh-cn/tutorials/parse-parameters.html index 7ae166ecc..923b3a4af 100644 --- a/zh-cn/tutorials/parse-parameters.html +++ b/zh-cn/tutorials/parse-parameters.html @@ -93,31 +93,76 @@

    访问原始参数

    注册过滤器和标签 中提到,可以通过 tagToken.args 来得到标签的原始参数字符串。例如:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Usage: {% random foo bar coo %}
    // Output: "foo", "bar" or "coo"
    engine.registerTag('random', {
    parse(tagToken) {
    // tagToken.args === "foo bar coo"
    this.items = tagToken.args.split(' ')
    },
    render(context, emitter) {
    // get a random index
    const index = Math.floor(this.items.length * Math.random())
    // output that item
    emitter.write(this.items[index])
    }
    })
    +
    // Usage: {% random foo bar coo %}
    +// Output: "foo", "bar" or "coo"
    +engine.registerTag('random', {
    +  parse(tagToken) {
    +    // tagToken.args === "foo bar coo"
    +    this.items = tagToken.args.split(' ')
    +  },
    +  render(context, emitter) {
    +    // get a random index
    +    const index = Math.floor(this.items.length * Math.random())
    +    // output that item
    +    emitter.write(this.items[index])
    +  }
    +})

    见这个 JSFiddle:http://jsfiddle.net/ctj364up/2/

    解析参数的值

    除了静态的参数字符串之外,我们更希望把动态的值传递给标签。LiquidJS 中的值可以是字面量(字符串、数字等,也可以是当前上下文的变量。

    下面是修改过的模板,也包含三个值用来随机。但它们表示的是值而不是静态的字符串。第一个是字符串字面量,第二个是标识符(表示变量),第三个是属性访问表达式,包含两个标识符。

    -
    1
    {% random "foo" bar obj.coo %}
    +
    {% random "foo" bar obj.coo %}

    解析这么多种情况会很麻烦,但 LiquidJS 提供了 Tokenizer 类来处理这种情况。

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    const { Liquid, Tokenizer, evalToken } = require('liquidjs')

    engine.registerTag('random', {
    parse(tagToken) {
    const tokenizer = new Tokenizer(tagToken.args)
    this.items = []
    while (!tokenizer.end()) {
    // here readValue() returns a LiteralToken or PropertyAccessToken
    this.items.push(tokenizer.readValue())
    }
    },
    * render(context, emitter) {
    const index = Math.floor(this.items.length * Math.random())
    const token = this.items[index]
    // in LiquidJS, we use yield to wait for async call
    const value = yield evalToken(token, context)
    emitter.write(value)
    }
    })
    +
    const { Liquid, Tokenizer, evalToken } = require('liquidjs')
    +
    +engine.registerTag('random', {
    +  parse(tagToken) {
    +    const tokenizer = new Tokenizer(tagToken.args)
    +    this.items = []
    +    while (!tokenizer.end()) {
    +      // here readValue() returns a LiteralToken or PropertyAccessToken
    +      this.items.push(tokenizer.readValue())
    +    }
    +  },
    +  * render(context, emitter) {
    +    const index = Math.floor(this.items.length * Math.random())
    +    const token = this.items[index]
    +    // in LiquidJS, we use yield to wait for async call
    +    const value = yield evalToken(token, context)
    +    emitter.write(value)
    +  }
    +})

    用上下文 { bar: "bar", obj: { coo: "coo" } } 来调用这个标签可以得到上第一个例子一样的效果。见这个 JSFiddle:http://jsfiddle.net/ctj364up/3/.

    异步和 Promise

    在 LiquidJS 里异步用生成器实现,这样同样一份标签的实现也可以用于同步的 API 比如 renderSync()parseAndRenderSync()renderFileSync()。如果要在标签实现里等待 Promise,只需要把 await somePromise 换成 yield somePromise,并保留 * render() 不要改成 async render()。更多细节请参考 Sync and Async

    把键值对解析为命名参数

    当参数很多时或者有可选参数时,使用命名参数语法会很方便。这时参数由无序的键值对构成,LiquidJS 中的 Hash 类就是来处理这种情况的。

    -
    1
    {% random from:2, to:max %}
    +
    {% random from:2, to:max %}

    上面的例子用来产生 [2, max] 范围内的随机数。我们要用 Hash 来解析 fromto 参数。

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const { Liquid, Hash } = require('liquidjs')

    engine.registerTag('random', {
    parse(tagToken) {
    // 解析参数结果,存到 `this.args` 里
    this.args = new Hash(tagToken.args)
    },
    * render(context, emitter) {
    // 在当前 `context` 下计算参数的值
    const {from, to} = yield this.args.render(context)
    const length = to - from + 1
    const value = from + Math.floor(length * Math.random())
    emitter.write(value)
    }
    })
    +
    const { Liquid, Hash } = require('liquidjs')
    +
    +engine.registerTag('random', {
    +  parse(tagToken) {
    +    // 解析参数结果,存到 `this.args` 里
    +    this.args = new Hash(tagToken.args)
    +  },
    +  * render(context, emitter) {
    +    // 在当前 `context` 下计算参数的值
    +    const {from, to} = yield this.args.render(context)
    +    const length = to - from + 1
    +    const value = from + Math.floor(length * Math.random())
    +    emitter.write(value)
    +  }
    +})

    { max: 10 } 上下文上渲染 {% random from:2, to:max %} 将会得到 [2, 10] 范围内的随机数。见这个 JSFiddle:http://jsfiddle.net/ctj364up/4/

    diff --git a/zh-cn/tutorials/partials-and-layouts.html b/zh-cn/tutorials/partials-and-layouts.html index 60ab30c9e..cfd01742b 100644 --- a/zh-cn/tutorials/partials-and-layouts.html +++ b/zh-cn/tutorials/partials-and-layouts.html @@ -93,19 +93,37 @@

    引用模板片段

    对于如下两个模板文件:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    // 文件:color.liquid
    color: '{{ color }}' shape: '{{ shape }}'

    // 文件:theme.liquid
    {% assign shape = 'circle' %}
    {% include 'color' %}
    {% include 'color' with 'red' %}
    {% include 'color', color: 'yellow', shape: 'square' %}
    +
    // 文件:color.liquid
    +color: '{{ color }}' shape: '{{ shape }}'
    +
    +// 文件:theme.liquid
    +{% assign shape = 'circle' %}
    +{% include 'color' %}
    +{% include 'color' with 'red' %}
    +{% include 'color', color: 'yellow', shape: 'square' %}

    输出为:

    -
    1
    2
    3
    color: '' shape: 'circle'
    color: 'red' shape: 'circle'
    color: 'yellow' shape: 'square'
    +
    color: '' shape: 'circle'
    +color: 'red' shape: 'circle'
    +color: 'yellow' shape: 'square'
    ".liquid" 文件扩展名

    如果设置了 extname: ".liquid" 选项,就可以省略 layout, renderinclude 里面文件名的 “.liquid” 后缀。详情请参考 extname 选项

    布局模板(模板继承)

    对于如下两个模板文件:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    // 文件:default-layout.liquid
    Header
    {% block content %}My default content{% endblock %}
    Footer

    // 文件:page.liquid
    {% layout "default-layout" %}
    {% block content %}My page content{% endblock %}
    +
    // 文件:default-layout.liquid
    +Header
    +{% block content %}My default content{% endblock %}
    +Footer
    +
    +// 文件:page.liquid
    +{% layout "default-layout" %}
    +{% block content %}My page content{% endblock %}

    渲染 page.liquid 将会输出:

    -
    1
    2
    3
    Header
    My page content
    Footer
    +
    Header
    +My page content
    +Footer
    Block
    • 布局文件(父模板)中可以定义多个 block;
    • @@ -114,7 +132,7 @@
    diff --git a/zh-cn/tutorials/plugins.html b/zh-cn/tutorials/plugins.html index b115c9ffb..0f3ea6a48 100644 --- a/zh-cn/tutorials/plugins.html +++ b/zh-cn/tutorials/plugins.html @@ -95,11 +95,21 @@

    一组标签和过滤器可以封装为一个 插件,通常发包到 NPM 来方便使用。本文介绍如何创建和使用插件。

    编写插件

    LiquidJS 插件就是一个简单的函数,它的第一个参数是 Liquid 类,其中的 this 是它被注册到的 Liquid 实例。可以通过 this 来调用 Liquid API,比如 注册标签和过滤器

    现在我们来写一个插件并在其中注册一个过滤器,来把输入字符串转换为大写:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * Inside the plugin function, `this` refers to the Liquid instance.
    *
    * @param Liquid: provides facilities to implement tags and filters.
    */
    module.exports = function (Liquid) {
    this.registerFilter('upup', x => x.toUpperCase());
    }
    +
    /**
    + * Inside the plugin function, `this` refers to the Liquid instance.
    + *
    + * @param Liquid: provides facilities to implement tags and filters.
    + */
    +module.exports = function (Liquid) {
    +    this.registerFilter('upup', x => x.toUpperCase());
    +}

    把上述代码保存为 upup.js

    使用插件

    把插件传递给 .plugin() 方法即可注册插件,例如:

    -
    1
    2
    3
    4
    const engine = new Liquid()

    engine.plugin(require('./upup.js'));
    engine.parseAndRender('{{ "foo" | upup }}').then(console.log)
    +
    const engine = new Liquid()
    +
    +engine.plugin(require('./upup.js'));
    +engine.parseAndRender('{{ "foo" | upup }}').then(console.log)

    上述代码将会输出 "FOO"

    插件列表

    由于本仓库只包含 Shopify/liquid 核心仓库的标签和插件(参考 https://github.com/harttle/liquidjs#differences-and-limitations),Shopify 平台上特有的插件只能通过插件来使用。

    @@ -111,7 +121,7 @@ diff --git a/zh-cn/tutorials/register-filters-tags.html b/zh-cn/tutorials/register-filters-tags.html index 654d2916d..400a8ab28 100644 --- a/zh-cn/tutorials/register-filters-tags.html +++ b/zh-cn/tutorials/register-filters-tags.html @@ -92,30 +92,81 @@
    -

    注册标签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 使用方式: {% upper name %}
    engine.registerTag('upper', {
    parse: function(tagToken, remainTokens) {
    this.value = new Value(token.args, liquid)
    },
    render: function*(scope, hash) {
    const str = yield this.value.value(ctx); // 'alice'
    return str.toUpperCase() // 'Alice'
    }
    });
    +

    注册标签

    // 使用方式: {% upper name %}
    +engine.registerTag('upper', {
    +    parse: function(tagToken, remainTokens) {
    +        this.value = new Value(token.args, liquid)
    +    },
    +    render: function*(scope, hash) {
    +        const str = yield this.value.value(ctx); // 'alice'
    +        return str.toUpperCase()  // 'Alice'
    +    }
    +});
    • parse: 从 remainTokens 中读取后续的标签/输出/HTML,直到找到你期望的结束标签。
    • render: 把 scope 数据和此前解析得到的 Token 结合,输出 HTML 字符串。

    对于更复杂的标签实现,可以提供一个继承自 Tag 的类:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Usage: {% upper name:"alice" %}
    import { Hash, Tag, TagToken, Context, Emitter, TopLevelToken, Liquid } from 'liquidjs'

    engine.registerTag('upper', class UpperTag extends Tag {
    private hash: Hash
    constructor(tagToken: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    super(tagToken, remainTokens, liquid)
    this.hash = new Hash(tagToken.args)
    }
    * render(ctx: Context) {
    const hash = yield this.hash.render();
    return hash.name.toUpperCase() // 'ALICE'
    }
    });
    +
    // Usage: {% upper name:"alice" %}
    +import { Hash, Tag, TagToken, Context, Emitter, TopLevelToken, Liquid } from 'liquidjs'
    +
    +engine.registerTag('upper', class UpperTag extends Tag {
    +    private hash: Hash
    +    constructor(tagToken: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    +        super(tagToken, remainTokens, liquid)
    +        this.hash = new Hash(tagToken.args)
    +    }
    +    * render(ctx: Context) {
    +        const hash = yield this.hash.render();
    +        return hash.name.toUpperCase() // 'ALICE'
    +    }
    +});

    可以参考已有的标签实现:https://github.com/harttle/liquidjs/tree/master/src/tags

    -

    注册过滤器

    1
    2
    // 使用方式: {{ name | upper }}
    engine.registerFilter('upper', v => v.toUpperCase())
    +

    注册过滤器

    // 使用方式: {{ name | upper }}
    +engine.registerFilter('upper', v => v.toUpperCase())

    过滤器的参数将会传递给上面注册的过滤器函数,从第二个参数开始(第一个参数是过滤器左侧的输入),例如:

    -
    1
    2
    // Usage: {{ 1 | add: 2, 3 }}
    engine.registerFilter('add', (initial, arg1, arg2) => initial + arg1 + arg2)
    +
    // Usage: {{ 1 | add: 2, 3 }}
    +engine.registerFilter('add', (initial, arg1, arg2) => initial + arg1 + arg2)

    查看已有的过滤器实现:https://github.com/harttle/liquidjs/tree/master/src/filters。对于复杂的标签,也可以用一个类来实现:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Usage: {% upper name:"alice" %}
    import { Hash, Tag, TagToken, Context, Emitter, TopLevelToken, Liquid } from 'liquidjs'

    engine.registerTag('upper', class UpperTag extends Tag {
    private hash: Hash
    constructor(tagToken: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    super(tagToken, remainTokens, liquid)
    this.hash = new Hash(tagToken.args)
    }
    * render(ctx: Context) {
    const hash = yield this.hash.render();
    return hash.name.toUpperCase() // 'ALICE'
    }
    });
    +
    // Usage: {% upper name:"alice" %}
    +import { Hash, Tag, TagToken, Context, Emitter, TopLevelToken, Liquid } from 'liquidjs'
    +
    +engine.registerTag('upper', class UpperTag extends Tag {
    +    private hash: Hash
    +    constructor(tagToken: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    +        super(tagToken, remainTokens, liquid)
    +        this.hash = new Hash(tagToken.args)
    +    }
    +    * render(ctx: Context) {
    +        const hash = yield this.hash.render();
    +        return hash.name.toUpperCase() // 'ALICE'
    +    }
    +});

    反注册标签/过滤器

    有时可能需要禁用一些标签/过滤器(比如 #324),注册一个假的标签/过滤器并在里面抛出相应的错误即可。

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 禁用标签
    const disabledTag = {
    parse: function(token) {
    throw new Error(`tag "${token.name}" disabled`);
    }
    }
    engine.registerTag('include', disabledTag);

    // 禁用过滤器
    function disabledFilter(name) {
    return function () {
    throw new Error(`filter "${name}" disabled`);
    }
    }
    engine.registerFilter('plus', disabledFilter('plus'));
    +
    // 禁用标签
    +const disabledTag = {
    +    parse: function(token) {
    +        throw new Error(`tag "${token.name}" disabled`);
    +    }
    +}
    +engine.registerTag('include', disabledTag);
    +
    +// 禁用过滤器
    +function disabledFilter(name) {
    +    return function () {
    +        throw new Error(`filter "${name}" disabled`);
    +    }
    +}
    +engine.registerFilter('plus', disabledFilter('plus'));
    diff --git a/zh-cn/tutorials/render-file.html b/zh-cn/tutorials/render-file.html index e905b0ff8..947420ffd 100644 --- a/zh-cn/tutorials/render-file.html +++ b/zh-cn/tutorials/render-file.html @@ -94,20 +94,32 @@

    一个典型的项目会有一个目录下都是模板,最方便的方式就是设置 LiquidJS 的 root 然后调用 .renderFile().renderFileSync() 来渲染其中的一个模板文件。

    渲染一个文件

    例如你有如下的目录结构:

    -
    1
    2
    3
    4
    5
    .
    ├── index.js
    └── views/
    ├── hello.liquid
    └── world.liquid
    +
    .
    +├── index.js
    +└── views/
    +  ├── hello.liquid
    +  └── world.liquid

    其中 hello.liquid 内容为:

    -
    1
    name: {{name}}
    +
    name: {{name}}

    index.js 中可以这样渲染 hello.liquid:

    -
    1
    2
    3
    4
    5
    6
    var engine = new Liquid({
    root: path.resolve(__dirname, 'views/'), // 设置模板查找目录
    extname: '.liquid' // 添加后缀,默认为 "" 表示不添加后缀
    });
    // 将会读取并渲染 `views/hello.liquid`
    engine.renderFile("hello", {name: 'alice'}).then(console.log)
    +
    var engine = new Liquid({
    +    root: path.resolve(__dirname, 'views/'), // 设置模板查找目录
    +    extname: '.liquid' // 添加后缀,默认为 "" 表示不添加后缀
    +});
    +// 将会读取并渲染 `views/hello.liquid`
    +engine.renderFile("hello", {name: 'alice'}).then(console.log)

    执行 node index.js 你将会得到类似这样的输出:

    -
    1
    name: alice
    +
    name: alice

    模板查找

    传递给 .renderFile(), .parseFile() .renderFileSync(), .parseFileSync() 这些 API 的模板名,
    以及传递给 include, layout 这些标签的模板名,将会根据 root 选项来查找。

    root 可以设置为 string 类型的路径(见上面的例子), 也可以设置为一个字符串数组表示路径列表,这时 LiquidJS 将会按顺序去查找。例如:

    -
    1
    2
    3
    4
    var engine = new Liquid({
    root: ['views/', 'views/partials/'],
    extname: '.liquid'
    });
    +
    var engine = new Liquid({
    +    root: ['views/', 'views/partials/'],
    +    extname: '.liquid'
    +});
    相对路径

    root 中使用相对路径将会被解释为相对于 cwd()(当前工作目录)。

    @@ -130,12 +142,31 @@

    文件系统接口

    LiquidJS 定义了一个文件系统接口,在 Node.js 下的默认实现是 src/fs/node.ts,在浏览器打包文件中的默认实现是 src/fs/browser.ts
    你可以通过创建 Liquid 时的 fs 参数来指定一个自定义实现来指定如何读取模板文件。比如从数据库里读取:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    const engine = new Liquid({
    fs: {
    readFileSync (file) {
    return db.model('Template').findByIdSync(file).text
    },
    await readFile (file) {
    const template = await db.model('Template').findById(file)
    return template.text
    },
    existsSync () {
    return true
    },
    await exists () {
    return true
    },
    resolve(root, file, ext) {
    return file
    }
    }
    });
    +
    const engine = new Liquid({
    +    fs: {
    +        readFileSync (file) {
    +            return db.model('Template').findByIdSync(file).text
    +        },
    +        await readFile (file) {
    +            const template = await db.model('Template').findById(file)
    +            return template.text
    +        },
    +        existsSync () {
    +            return true
    +        },
    +        await exists () {
    +            return true
    +        },
    +        resolve(root, file, ext) {
    +            return file
    +        }
    +    }
    +});
    diff --git a/zh-cn/tutorials/render-tag-content.html b/zh-cn/tutorials/render-tag-content.html index c33c7e1be..41bf3363a 100644 --- a/zh-cn/tutorials/render-tag-content.html +++ b/zh-cn/tutorials/render-tag-content.html @@ -94,10 +94,14 @@

    自定义标签可以有内容,也可以嵌套使用。本文描述了如何实现一个由开始标签结束标签和之间的标签内容的自定义标签。

    渲染标签内容

    我们先实现一个简单的 wrap 标签,它会把内容包装在 <div class="wrapper"></div> 元素里:

    -
    1
    2
    3
    {% wrap %}
    {{ "hello world!" | capitalize }}
    {% endwrap %}
    +
    {% wrap %}
    +  {{ "hello world!" | capitalize }}
    +{% endwrap %}

    期望输出:

    -
    1
    2
    3
    <div class='wrapper'>
    Hello world!
    </div>
    +
    <div class='wrapper'>
    +  Hello world!
    +</div>

    首先 注册 一个名为 wrap 的标签,把内容解析到 this.tpls 数组里。parse(tagToken, remainTokens) 中,

      @@ -105,25 +109,77 @@
    • remainTokens 是当前模板中后续所有 Token 的数组。

    我们要做的是从 remainTokens 里拿出来/.shift() 足够的标签直到遇到 endwrap(其实可以是任意名字,但按照惯例应该叫 endwrap)。如果到模板结尾都没遇到 endwrap,需要抛出一个标签未关闭的 Error

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    engine.registerTag('wrap', {
    parse(tagToken, remainTokens) {
    this.tpls = []
    let closed = false
    while(remainTokens.length) {
    let token = remainTokens.shift()
    // 得到了结束标签,停止解析
    if (token.name === 'endwrap') {
    closed = true
    break
    }
    // 把 Token 解析成 Template
    // parseToken() 可能会消耗多个 Token
    // 例如 {% if %}...{% endif %}
    let tpl = this.liquid.parser.parseToken(token, remainTokens)
    this.tpls.push(tpl)
    }
    if (!closed) throw new Error(`tag ${tagToken.getText()} not closed`)
    },
    * render(context, emitter) {
    emitter.write("<div class='wrapper'>")
    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    emitter.write("</div>")
    }
    })
    +
    engine.registerTag('wrap', {
    +  parse(tagToken, remainTokens) {
    +    this.tpls = []
    +    let closed = false
    +    while(remainTokens.length) {
    +      let token = remainTokens.shift()
    +      // 得到了结束标签,停止解析
    +      if (token.name === 'endwrap') {
    +        closed = true
    +        break
    +      }
    +      // 把 Token 解析成 Template
    +      // parseToken() 可能会消耗多个 Token
    +      // 例如 {% if %}...{% endif %}
    +      let tpl = this.liquid.parser.parseToken(token, remainTokens)
    +      this.tpls.push(tpl)
    +    }
    +    if (!closed) throw new Error(`tag ${tagToken.getText()} not closed`)
    +  },
    +  * render(context, emitter) {
    +    emitter.write("<div class='wrapper'>")
    +    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    +    emitter.write("</div>")
    +  }
    +})

    .renderTemplates() 可能是异步的,因此需要 yield 来等它完成。更多关于 LiquidJS 异步的信息可以参考 同步和异步render() 的其他部分比较直观,这是 JSFiddle 版本:http://jsfiddle.net/por0zcn1/3/

    使用 ParseStream

    对于像 forif 这样的复杂标签,parse() 会变得很复杂。使用 ParseStream 工具可以按事件风格来组织 parse() 的逻辑。下面是用 ParseStream 重写过的 parse(),实现了和上面例子中完全一样的功能。

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    parse(tagToken, remainTokens) {
    this.tpls = []
    this.liquid.parser.parseStream(remainTokens)
    .on('template', tpl => this.tpls.push(tpl))
    // 注意这里不能用箭头函数,因为我们需要 `this`
    .on('tag:endwrap', function () { this.stop() })
    .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`) })
    .start()
    }
    +
    parse(tagToken, remainTokens) {
    +  this.tpls = []
    +  this.liquid.parser.parseStream(remainTokens)
    +    .on('template', tpl => this.tpls.push(tpl))
    +    // 注意这里不能用箭头函数,因为我们需要 `this`
    +    .on('tag:endwrap', function () { this.stop() })
    +    .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`) })
    +    .start()
    +}

    这是 JSFiddle 链接:http://jsfiddle.net/por0zcn1/4/。简单起见,下面的例子都借助 ParseStream 来实现。

    操作上下文

    上面的 wrap 标签看起来没什么用,反正没它也可以很容易地渲染那部分内容。我们现在来实现一个 repeat 标签,把内容渲染两次(还可以加个参数让它渲染任意次):

    -
    1
    2
    3
    {% repeat %}
    {{ repeat.i }}. {{ "hello world!" | capitalize }}
    {% endrepeat %}`
    +
    {% repeat %}
    +  {{ repeat.i }}. {{ "hello world!" | capitalize }}
    +{% endrepeat %}`

    期望输出:

    -
    1
    2
    1. Hello world!
    2. Hello world!
    +
    1. Hello world!
    +2. Hello world!

    你可能注意到了在 repeat 上下文里有个额外的变量 repeat.i,这就需要我们操作 上下文

    上下文

    上下文 定义了 Liquid 模板中每个变量的值。在 LiquidJS 中,Context 由一个 Scope 的栈组成。Scope 就是一个普通对象,就像传给 engine.render(tpl, scope)scope 一样。

    每次进入新的 上下文 时,我们需要 push 一个新的 Scope。当结束渲染并退出 上下文 时,再把 Scope上下文 pop 出来。见下面的实现:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    engine.registerTag('repeat', {
    parse(tagToken, remainTokens) {
    this.tpls = []
    this.liquid.parser.parseStream(remainTokens)
    .on('template', tpl => this.tpls.push(tpl))
    .on('tag:endrepeat', function () { this.stop() })
    .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`) })
    .start()
    },
    * render(context, emitter) {
    const repeat = { i: 1 }
    context.push({ repeat })
    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    repeat.i++
    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    context.pop()
    }
    })
    +
    engine.registerTag('repeat', {
    +  parse(tagToken, remainTokens) {
    +    this.tpls = []
    +    this.liquid.parser.parseStream(remainTokens)
    +      .on('template', tpl => this.tpls.push(tpl))
    +      .on('tag:endrepeat', function () { this.stop() })
    +      .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`) })
    +      .start()
    +  },
    +  * render(context, emitter) {
    +    const repeat = { i: 1 }
    +    context.push({ repeat })
    +    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    +    repeat.i++
    +    yield this.liquid.renderer.renderTemplates(this.tpls, context, emitter)
    +    context.pop()
    +  }
    +})

    parse() 部分和 wrap 标签完全相同,在 render() 部分我们通过调用两次 .renderTemplates(this.tpls) 来重复渲染内容。这是 JSFiddle 链接:http://jsfiddle.net/por0zcn1/2/

    成对使用 Push 和 Pop

    必须成对地使用 context.push()context.pop()。如果忘记 pop() 会导致 Scope 泄露给后面的模板内容,也可能损坏 上下文 栈。.

    @@ -132,7 +188,7 @@
    diff --git a/zh-cn/tutorials/setup.html b/zh-cn/tutorials/setup.html index fa5fb3a04..78bb06238 100644 --- a/zh-cn/tutorials/setup.html +++ b/zh-cn/tutorials/setup.html @@ -94,21 +94,32 @@

    如果你还不了解 Liquid 模板语言,请参考 Liquid 模板语言简介

    在 Node.js 里使用

    通过 NPM 安装:

    -
    1
    npm install --save liquidjs
    +
    npm install --save liquidjs
    -
    1
    2
    3
    4
    5
    6
    var { Liquid } = require('liquidjs');
    var engine = new Liquid();

    engine
    .parseAndRender('{{name | capitalize}}', {name: 'alice'})
    .then(console.log); // 输出 'Alice'
    +
    var { Liquid } = require('liquidjs');
    +var engine = new Liquid();
    +
    +engine
    +    .parseAndRender('{{name | capitalize}}', {name: 'alice'})
    +    .then(console.log);     // 输出 'Alice'
    示例

    这里有一个 LiquidJS 在 Node.js 里使用的例子:liquidjs/demo/nodejs/.

    LiquidJS 的类型定义也导出并发布到了 NPM 包里,写 TypeScript 的项目可以直接这样使用:

    -
    1
    2
    3
    4
    5
    6
    import { Liquid } from 'liquidjs';
    const engine = new Liquid();

    engine
    .parseAndRender('{{name | capitalize}}', {name: 'alice'})
    .then(console.log); // 输出 'Alice'
    +
    import { Liquid } from 'liquidjs';
    +const engine = new Liquid();
    +
    +engine
    +    .parseAndRender('{{name | capitalize}}', {name: 'alice'})
    +    .then(console.log);     // 输出 'Alice'
    示例

    这里有一个 LiquidJS 在 TypeScript 下的例子:liquidjs/demo/typescript/.

    在浏览器里使用

    LiquidJS 预先构建了 UMD Bundle,可以通过 jsDelivr CDN 来引用:

    -
    1
    2
    <script src="https://cdn.jsdelivr.net/npm/liquidjs/dist/liquid.browser.min.js"></script>     <!--生产环境-->
    <script src="https://cdn.jsdelivr.net/npm/liquidjs/dist/liquid.browser.umd.js"></script> <!--开发环境-->
    +
    <script src="https://cdn.jsdelivr.net/npm/liquidjs/dist/liquid.browser.min.js"></script>     <!--生产环境-->
    +<script src="https://cdn.jsdelivr.net/npm/liquidjs/dist/liquid.browser.umd.js"></script>         <!--开发环境-->
    示例

    这里有一个 jsFiddle 上的在线例子:jsfiddle.net/x43eb0z6,其源码也可以在 liquidjs/demo/browser/ 找到。

    @@ -117,16 +128,16 @@

    在命令行里使用

    你还可以在命令行里使用 LiquidJS:

    -
    1
    echo '{{"hello" | capitalize}}' | npx liquidjs
    +
    echo '{{"hello" | capitalize}}' | npx liquidjs

    模板来自标准输入,数据则来自参数,这个参数可以是一个 JSON 文件的路径,也可以是一个 JSON 字符串:

    -
    1
    echo 'Hello, {{ name }}.' | npx liquidjs '{"name": "Snake"}'
    +
    echo 'Hello, {{ name }}.' | npx liquidjs '{"name": "Snake"}'

    其他

    @stevenanthonyrevo 还提供了一个 ReactJS demo,请参考 liquidjs/demo/reactjs/

    diff --git a/zh-cn/tutorials/sync-and-async.html b/zh-cn/tutorials/sync-and-async.html index 008a79a19..db801f379 100644 --- a/zh-cn/tutorials/sync-and-async.html +++ b/zh-cn/tutorials/sync-and-async.html @@ -110,7 +110,21 @@
  • evalValueSync()
  • 如何实现兼容同步的标签

    LiquidJS 使用基于生成器的异步实现,来让同一份代码支持同步和异步调用。例如下面的 UpperTag 既可以用于 engine.renderSync() 也可以用于 engine.render()

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid } from 'liquidjs'

    // Usage: {% upper "alice" %}
    // Output: ALICE
    engine.registerTag('upper', class UpperTag extends Tag {
    private value: Value
    constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    super(token, remainTokens, liquid)
    this.value = new Value(token.args, liquid)
    }
    * render (ctx: Context, emitter: Emitter) {
    const title = yield this.value.value(ctx)
    emitter.write(title.toUpperCase())
    }
    })
    +
    import { TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid } from 'liquidjs'
    +
    +// Usage: {% upper "alice" %}
    +// Output: ALICE
    +engine.registerTag('upper', class UpperTag extends Tag {
    +  private value: Value
    +  constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    +    super(token, remainTokens, liquid)
    +    this.value = new Value(token.args, liquid)
    +  }
    +  * render (ctx: Context, emitter: Emitter) {
    +    const title = yield this.value.value(ctx)
    +    emitter.write(title.toUpperCase())
    +  }
    +})

    所有内置标签都兼容同步,可以安全地用于同步或异步 API。实现同时支持同异步的标签,需要:

      @@ -119,19 +133,51 @@
    • 不能调用会返回 Promise 的函数。

    调用返回 Promise 的函数

    但 LiquidJS 是支持 Promise 的,你仍然可以调用返回 Promise 的方法并等它 resolve。只需要把 await 换成 yield。例如:

    -
    1
    2
    3
    4
    5
    * render (ctx: Context, emitter: Emitter) {
    const file = yield this.value.value(ctx)
    const title = yield fs.readFile(file, 'utf8')
    emitter.write(title.toUpperCase())
    }
    +
    * render (ctx: Context, emitter: Emitter) {
    +  const file = yield this.value.value(ctx)
    +  const title = yield fs.readFile(file, 'utf8')
    +  emitter.write(title.toUpperCase())
    +}

    现在 * render() 调用了一个返回 Promise 的 API,它就不再兼容同步了。不兼容同步的标签也仍然是合法标签,在异步 API 下也会正常运行。被同步调用时,返回 Promise 的标签会被渲染成 [object Promise]

    把 LiquidJS 生成器转换成 Promise

    有些 LiquidJS API 会返回 Promise,有些会返回生成器。你可以用 toPromise 来把生成器转换为 Promise,比如:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid, toPromise } from 'liquidjs'

    // Usage: {% upper "alice" %}
    // Output: ALICE
    engine.registerTag('upper', class UpperTag extends Tag {
    private value: Value
    constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    super(token, remainTokens, liquid)
    this.value = new Value(token.args, liquid)
    }
    async render (ctx: Context, emitter: Emitter) {
    const title = await toPromise(this.value.value(ctx))
    emitter.write(title.toUpperCase())
    }
    })
    +
    import { TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid, toPromise } from 'liquidjs'
    +
    +// Usage: {% upper "alice" %}
    +// Output: ALICE
    +engine.registerTag('upper', class UpperTag extends Tag {
    +  private value: Value
    +  constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    +    super(token, remainTokens, liquid)
    +    this.value = new Value(token.args, liquid)
    +  }
    +  async render (ctx: Context, emitter: Emitter) {
    +    const title = await toPromise(this.value.value(ctx))
    +    emitter.write(title.toUpperCase())
    +  }
    +})

    纯异步标签

    如果你的标签就不打算支持同步,可以干脆实现成 async render(),这样就可以使用更熟悉的 await 了:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { toPromise, TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid } from 'liquidjs'

    // Usage: {% upper "alice" %}
    // Output: ALICE
    engine.registerTag('upper', class UpperTag extends Tag {
    private value: Value
    constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    super(token, remainTokens, liquid)
    this.value = new Value(token.args, liquid)
    }
    async render (ctx: Context, emitter: Emitter) {
    const title = await toPromise(this.value.value(ctx))
    emitter.write(`<h1>${title}</h1>`)
    }
    })
    +
    import { toPromise, TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid } from 'liquidjs'
    +
    +// Usage: {% upper "alice" %}
    +// Output: ALICE
    +engine.registerTag('upper', class UpperTag extends Tag {
    +  private value: Value
    +  constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
    +    super(token, remainTokens, liquid)
    +    this.value = new Value(token.args, liquid)
    +  }
    +  async render (ctx: Context, emitter: Emitter) {
    +    const title = await toPromise(this.value.value(ctx))
    +    emitter.write(`<h1>${title}</h1>`)
    +  }
    +})
    diff --git a/zh-cn/tutorials/truthy-and-falsy.html b/zh-cn/tutorials/truthy-and-falsy.html index 1dea3b670..c85430e69 100644 --- a/zh-cn/tutorials/truthy-and-falsy.html +++ b/zh-cn/tutorials/truthy-and-falsy.html @@ -226,7 +226,7 @@ diff --git a/zh-cn/tutorials/use-in-expressjs.html b/zh-cn/tutorials/use-in-expressjs.html index b9a067034..176b666db 100644 --- a/zh-cn/tutorials/use-in-expressjs.html +++ b/zh-cn/tutorials/use-in-expressjs.html @@ -93,28 +93,49 @@

    LiquidJS 可以用来作为 Express 的模板引擎。可以把 Liquid 设置到 view engine 选项上即可:

    -
    1
    2
    3
    4
    5
    6
    7
    var { Liquid } = require('liquidjs');
    var engine = new Liquid();

    // 注册为 liquid 文件的模板引擎
    app.engine('liquid', engine.express());
    app.set('views', './views'); // 指定模板目录
    app.set('view engine', 'liquid'); // 把 liquid 文件设为默认模板
    +
    var { Liquid } = require('liquidjs');
    +var engine = new Liquid();
    +
    +// 注册为 liquid 文件的模板引擎
    +app.engine('liquid', engine.express()); 
    +app.set('views', './views');            // 指定模板目录
    +app.set('view engine', 'liquid');       // 把 liquid 文件设为默认模板
    示例

    这是一个在 Express.js 中使用 LiquidJS 的例子:liquidjs/demo/express/.

    模板查找

    LiquidJS 仍然会去 root 指定的目录查找(参考 Render A Template File),也会去 Express.js 的 views 选项指定的目录里(上述例子中是 ./views)去查找。例如你有这样的目录结构:

    -
    1
    2
    3
    4
    5
    .
    ├── views1/
    │ └── hello.liquid
    └── views2/
    └── world.liquid
    +
    .
    +├── views1/
    +│ └── hello.liquid
    +└── views2/
    +  └── world.liquid

    LiquidJS 的模板 root 设置到了 views1,Express.js 的 views 设置到了 views2

    -
    1
    2
    3
    4
    5
    6
    7
    8
    var { Liquid } = require('liquidjs');
    var engine = new Liquid({
    root: './views1/'
    });

    app.engine('liquid', engine.express());
    app.set('views', './views2');
    app.set('view engine', 'liquid');
    +
    var { Liquid } = require('liquidjs');
    +var engine = new Liquid({
    +    root: './views1/'
    +});
    +
    +app.engine('liquid', engine.express()); 
    +app.set('views', './views2');
    +app.set('view engine', 'liquid');

    hello.liquidworld.liquid 两个文件都可以找到并且成功渲染:

    -
    1
    2
    res.render('hello')
    res.render('world')
    +
    res.render('hello')
    +res.render('world')

    缓存

    直接把 cache 选项 设为 true 即可开启模板缓存,参考 缓存 一文。推荐在生产环境中开启缓存,可以用如下代码:

    -
    1
    2
    3
    4
    var { Liquid } = require('liquidjs');
    var engine = new Liquid({
    cache: process.env.NODE_ENV === 'production'
    });
    +
    var { Liquid } = require('liquidjs');
    +var engine = new Liquid({
    +    cache: process.env.NODE_ENV === 'production'
    +});

    cache 还可以是一个数字表示最大缓存的模板数量,也可以是一个自定义的缓存实现,详情请参考 cache 选项

    diff --git a/zh-cn/tutorials/whitespace-control.html b/zh-cn/tutorials/whitespace-control.html index eb27aa75b..1d92bbdea 100644 --- a/zh-cn/tutorials/whitespace-control.html +++ b/zh-cn/tutorials/whitespace-control.html @@ -94,16 +94,19 @@

    为了让源代码缩进好看,我们会加很多空白字符比如把不会产生输出的标签也单独一行。LiquidJS 提供了空白字符控制机制,可以避免这些多余的空白字符输出到 HTML 中。

    通过标记的方式

    默认所有标签和输出的行,都会在行尾产生一个换行(\n),如果有缩进的话还会产生很多前导空格。例如:

    -
    1
    2
    {%  author = "harttle" %}
    {{ author }}
    +
    {%  author = "harttle" %}
    +{{ author }}

    将会输出(注意前面的空行):

    -
    1
    2

    harttle
    +
    
    +harttle

    可以在标签和输出的标记里面加横线({{-, -}}, {%-, -%})来移除左侧/右侧的空白。例如:

    -
    1
    2
    {% assign author = "harttle" -%}
    {{ author }}
    +
    {% assign author = "harttle" -%}
    +{{ author }}

    将会输出:

    -
    1
    harttle
    +
    harttle

    这个例子中 -%} 移除了 assign 标签右侧的空白。

    通过选项

    此外 LiquidJS 还提供了一系列选项来帮助扫代码式地移除空白:

    @@ -118,7 +121,7 @@