run:R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
23.8 KB
2026-03-11 16:18:51
R W Run
7.8 KB
2026-03-11 16:18:52
R W Run
36.1 KB
2026-03-11 16:18:51
R W Run
11.9 KB
2026-03-11 16:18:52
R W Run
18.94 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:52
R W Run
28.6 KB
2026-03-11 16:18:51
R W Run
316 By
2026-03-11 16:18:51
R W Run
12.9 KB
2026-03-11 16:18:51
R W Run
61.02 KB
2026-03-11 16:18:52
R W Run
15 KB
2026-03-11 16:18:51
R W Run
112.05 KB
2026-03-11 16:18:51
R W Run
12.47 KB
2026-03-11 16:18:51
R W Run
15.07 KB
2026-03-11 16:18:52
R W Run
9.84 KB
2026-03-11 16:18:52
R W Run
13.17 KB
2026-03-11 16:18:52
R W Run
33.83 KB
2026-03-11 16:18:51
R W Run
42.63 KB
2026-03-11 16:18:51
R W Run
55.71 KB
2026-03-11 16:18:52
R W Run
12.53 KB
2026-03-11 16:18:51
R W Run
2.55 KB
2026-03-11 16:18:52
R W Run
28.92 KB
2026-03-11 16:18:52
R W Run
539 By
2026-03-11 16:18:51
R W Run
367 By
2026-03-11 16:18:52
R W Run
42.65 KB
2026-03-11 16:18:51
R W Run
401 By
2026-03-11 16:18:51
R W Run
6.61 KB
2026-03-11 16:18:51
R W Run
664 By
2026-03-11 16:18:52
R W Run
20.63 KB
2026-03-11 16:18:51
R W Run
2.18 KB
2026-03-11 16:18:52
R W Run
453 By
2026-03-11 16:18:52
R W Run
457 By
2026-03-11 16:18:51
R W Run
36.83 KB
2026-03-11 16:18:52
R W Run
2.41 KB
2026-03-11 16:18:52
R W Run
8.28 KB
2026-03-11 16:18:51
R W Run
13.89 KB
2026-03-11 16:18:51
R W Run
11.76 KB
2026-03-11 16:18:51
R W Run
2.65 KB
2026-03-11 16:18:51
R W Run
7.43 KB
2026-03-11 16:18:51
R W Run
17.46 KB
2026-03-11 16:18:51
R W Run
5.14 KB
2026-03-11 16:18:52
R W Run
16.7 KB
2026-03-11 16:18:51
R W Run
8.28 KB
2026-03-11 16:18:52
R W Run
2.92 KB
2026-03-11 16:18:52
R W Run
1.32 KB
2026-03-11 16:18:51
R W Run
4.6 KB
2026-03-11 16:18:52
R W Run
11.62 KB
2026-03-11 16:18:52
R W Run
2.5 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
11.25 KB
2026-03-11 16:18:52
R W Run
5.32 KB
2026-03-11 16:18:51
R W Run
10.99 KB
2026-03-11 16:18:52
R W Run
68.32 KB
2026-03-11 16:18:51
R W Run
6.34 KB
2026-03-11 16:18:51
R W Run
5.49 KB
2026-03-11 16:18:51
R W Run
1.99 KB
2026-03-11 16:18:52
R W Run
7.02 KB
2026-03-11 16:18:51
R W Run
4.91 KB
2026-03-11 16:18:52
R W Run
16.86 KB
2026-03-11 16:18:51
R W Run
24.23 KB
2026-03-11 16:18:51
R W Run
3.97 KB
2026-03-11 16:18:51
R W Run
47.66 KB
2026-03-11 16:18:51
R W Run
9.22 KB
2026-03-11 16:18:51
R W Run
25.51 KB
2026-03-11 16:18:51
R W Run
198.38 KB
2026-03-11 16:18:52
R W Run
56.65 KB
2026-03-11 16:18:51
R W Run
10.46 KB
2026-03-11 16:18:51
R W Run
10.95 KB
2026-03-11 16:18:52
R W Run
29.26 KB
2026-03-11 16:18:51
R W Run
70.91 KB
2026-03-11 16:18:52
R W Run
35.3 KB
2026-03-11 16:18:52
R W Run
16.61 KB
2026-03-11 16:18:52
R W Run
2.57 KB
2026-03-11 16:18:52
R W Run
39.83 KB
2026-03-11 16:18:51
R W Run
70.64 KB
2026-03-11 16:18:51
R W Run
15.56 KB
2026-03-11 16:18:52
R W Run
7.33 KB
2026-03-11 16:18:52
R W Run
253 By
2026-03-11 16:18:51
R W Run
7.96 KB
2026-03-11 16:18:52
R W Run
3.23 KB
2026-03-11 16:18:52
R W Run
969 By
2026-03-11 16:18:52
R W Run
16.28 KB
2026-03-11 16:18:51
R W Run
7.22 KB
2026-03-11 16:18:51
R W Run
12.95 KB
2026-03-11 16:18:51
R W Run
6.53 KB
2026-03-11 16:18:51
R W Run
3.42 KB
2026-03-11 16:18:52
R W Run
5.84 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
4.3 KB
2026-03-11 16:18:52
R W Run
2.91 KB
2026-03-11 16:18:51
R W Run
16.46 KB
2026-03-11 16:18:52
R W Run
40.6 KB
2026-03-11 16:18:51
R W Run
20.22 KB
2026-03-11 16:18:51
R W Run
36.11 KB
2026-03-11 16:18:52
R W Run
17.01 KB
2026-03-11 16:18:51
R W Run
7.27 KB
2026-03-11 16:18:52
R W Run
6.62 KB
2026-03-11 16:18:52
R W Run
16.49 KB
2026-03-11 16:18:52
R W Run
1.79 KB
2026-03-11 16:18:52
R W Run
29.82 KB
2026-03-11 16:18:51
R W Run
6.67 KB
2026-03-11 16:18:52
R W Run
8.98 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:51
R W Run
12.01 KB
2026-03-11 16:18:51
R W Run
17.11 KB
2026-03-11 16:18:51
R W Run
6.74 KB
2026-03-11 16:18:52
R W Run
30.93 KB
2026-03-11 16:18:51
R W Run
4.99 KB
2026-03-11 16:18:51
R W Run
4.25 KB
2026-03-11 16:18:51
R W Run
24.72 KB
2026-03-11 16:18:51
R W Run
29.96 KB
2026-03-11 16:18:52
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
160 KB
2026-03-11 16:18:51
R W Run
6.72 KB
2026-03-11 16:18:52
R W Run
10.92 KB
2026-03-11 16:18:51
R W Run
4.77 KB
2026-03-11 16:18:51
R W Run
3.38 KB
2026-03-11 16:18:51
R W Run
11.18 KB
2026-03-11 16:18:51
R W Run
62.19 KB
2026-03-11 16:18:51
R W Run
2.46 KB
2026-03-11 16:18:51
R W Run
9.17 KB
2026-03-11 16:18:51
R W Run
32.15 KB
2026-03-11 16:18:51
R W Run
34.05 KB
2026-03-11 16:18:52
R W Run
7.15 KB
2026-03-11 16:18:51
R W Run
3.47 KB
2026-03-11 16:18:52
R W Run
1.87 KB
2026-03-11 16:18:52
R W Run
30.91 KB
2026-03-11 16:18:51
R W Run
7.29 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:51
R W Run
12.54 KB
2026-03-11 16:18:51
R W Run
19.12 KB
2026-03-11 16:18:51
R W Run
18.12 KB
2026-03-11 16:18:52
R W Run
39.99 KB
2026-03-11 16:18:52
R W Run
5.17 KB
2026-03-11 16:18:52
R W Run
979 By
2026-03-11 16:18:51
R W Run
18.44 KB
2026-03-11 16:18:52
R W Run
10.24 KB
2026-03-11 16:18:51
R W Run
1.77 KB
2026-03-11 16:18:52
R W Run
34.9 KB
2026-03-11 16:18:51
R W Run
7.19 KB
2026-03-11 16:18:52
R W Run
160.5 KB
2026-03-11 16:18:51
R W Run
64.27 KB
2026-03-11 16:18:51
R W Run
27.95 KB
2026-03-11 16:18:51
R W Run
4.69 KB
2026-03-11 16:18:51
R W Run
2.94 KB
2026-03-11 16:18:51
R W Run
43.13 KB
2026-03-11 16:18:52
R W Run
2.25 KB
2026-03-11 16:18:52
R W Run
22.5 KB
2026-03-11 16:18:51
R W Run
13.01 KB
2026-03-11 16:18:52
R W Run
3.27 KB
2026-03-11 16:18:51
R W Run
18 KB
2026-03-11 16:18:51
R W Run
210.4 KB
2026-03-11 16:18:52
R W Run
25.86 KB
2026-03-11 16:18:52
R W Run
115.85 KB
2026-03-11 16:18:51
R W Run
373 By
2026-03-11 16:18:52
R W Run
343 By
2026-03-11 16:18:52
R W Run
338 By
2026-03-11 16:18:51
R W Run
100.73 KB
2026-03-11 16:18:52
R W Run
130.93 KB
2026-03-11 16:18:51
R W Run
19.1 KB
2026-03-11 16:18:51
R W Run
17.41 KB
2026-03-11 16:18:52
R W Run
41.98 KB
2026-03-11 16:18:52
R W Run
400 By
2026-03-11 16:18:52
R W Run
11.1 KB
2026-03-11 16:18:52
R W Run
37.02 KB
2026-03-11 16:18:51
R W Run
2.24 KB
2026-03-11 16:18:51
R W Run
188.13 KB
2026-03-11 16:18:51
R W Run
338 By
2026-03-11 16:18:51
R W Run
38 KB
2026-03-11 16:18:51
R W Run
4.02 KB
2026-03-11 16:18:52
R W Run
5.38 KB
2026-03-11 16:18:51
R W Run
3.05 KB
2026-03-11 16:18:52
R W Run
2.61 KB
2026-03-11 16:18:51
R W Run
1.16 KB
2026-03-11 16:18:52
R W Run
4.04 KB
2026-03-11 16:18:51
R W Run
3.71 KB
2026-03-11 16:18:51
R W Run
24.6 KB
2026-03-11 16:18:51
R W Run
9.56 KB
2026-03-11 16:18:51
R W Run
346.43 KB
2026-03-11 16:18:52
R W Run
281.84 KB
2026-03-11 16:18:52
R W Run
14.95 KB
2026-03-11 16:18:51
R W Run
8.44 KB
2026-03-11 16:18:52
R W Run
168.95 KB
2026-03-11 16:18:52
R W Run
20.71 KB
2026-03-11 16:18:52
R W Run
25.27 KB
2026-03-11 16:18:51
R W Run
5.72 KB
2026-03-11 16:18:51
R W Run
4.63 KB
2026-03-11 16:18:52
R W Run
81.73 KB
2026-03-11 16:18:51
R W Run
67.18 KB
2026-03-11 16:18:51
R W Run
156.36 KB
2026-03-11 16:18:52
R W Run
55.19 KB
2026-03-11 16:18:51
R W Run
162 By
2026-03-11 16:18:51
R W Run
61.72 KB
2026-03-11 16:18:51
R W Run
216.06 KB
2026-03-11 16:18:52
R W Run
65.09 KB
2026-03-11 16:18:51
R W Run
25.24 KB
2026-03-11 16:18:52
R W Run
4.81 KB
2026-03-11 16:18:51
R W Run
6.48 KB
2026-03-11 16:18:52
R W Run
21.25 KB
2026-03-11 16:18:51
R W Run
2.79 KB
2026-03-11 16:18:52
R W Run
89.69 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:52
R W Run
3.69 KB
2026-03-11 16:18:52
R W Run
4.11 KB
2026-03-11 16:18:51
R W Run
40.74 KB
2026-03-11 16:18:51
R W Run
25.38 KB
2026-03-11 16:18:51
R W Run
43.31 KB
2026-03-11 16:18:52
R W Run
102.57 KB
2026-03-11 16:18:52
R W Run
6.18 KB
2026-03-11 16:18:51
R W Run
124.47 KB
2026-03-11 16:18:52
R W Run
35.65 KB
2026-03-11 16:18:52
R W Run
6.94 KB
2026-03-11 16:18:52
R W Run
67.04 KB
2026-03-11 16:18:52
R W Run
10.62 KB
2026-03-11 16:18:51
R W Run
289.35 KB
2026-03-11 16:18:52
R W Run
36.23 KB
2026-03-11 16:18:51
R W Run
200 By
2026-03-11 16:18:52
R W Run
200 By
2026-03-11 16:18:52
R W Run
98.29 KB
2026-03-11 16:18:52
R W Run
30.02 KB
2026-03-11 16:18:52
R W Run
19.03 KB
2026-03-11 16:18:52
R W Run
5.06 KB
2026-03-11 16:18:52
R W Run
255 By
2026-03-11 16:18:51
R W Run
22.66 KB
2026-03-11 16:18:52
R W Run
154.63 KB
2026-03-11 16:18:51
R W Run
9.68 KB
2026-03-11 16:18:51
R W Run
258 By
2026-03-11 16:18:51
R W Run
23.49 KB
2026-03-11 16:18:51
R W Run
3.16 KB
2026-03-11 16:18:51
R W Run
8.4 KB
2026-03-11 16:18:52
R W Run
441 By
2026-03-11 16:18:51
R W Run
7.39 KB
2026-03-11 16:18:51
R W Run
173 KB
2026-03-11 16:18:52
R W Run
544 By
2026-03-11 16:18:52
R W Run
4.17 KB
2026-03-11 16:18:51
R W Run
35.97 KB
2026-03-11 16:18:52
R W Run
1.69 KB
2026-03-11 16:18:51
R W Run
2.84 KB
2026-03-11 16:18:52
R W Run
6.09 KB
2026-03-11 16:18:51
R W Run
8.71 KB
2026-03-11 16:18:51
R W Run
131.84 KB
2026-03-11 16:18:51
R W Run
37.45 KB
2026-03-11 16:18:51
R W Run
173.89 KB
2026-03-11 16:18:51
R W Run
7.09 KB
2026-03-11 16:18:51
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
1.08 KB
2026-03-11 16:18:51
R W Run
69.46 KB
2026-03-11 16:18:52
R W Run
445 By
2026-03-11 16:18:51
R W Run
799 By
2026-03-11 16:18:52
R W Run
error_log
📄class-wp-duotone.php
1<?php
2/**
3 * WP_Duotone class
4 *
5 * Parts of this source were derived and modified from colord,
6 * released under the MIT license.
7 *
8 * https://github.com/omgovich/colord
9 *
10 * Copyright (c) 2020 Vlad Shilov omgovich@ya.ru
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining
13 * a copy of this software and associated documentation files (the
14 * "Software"), to deal in the Software without restriction, including
15 * without limitation the rights to use, copy, modify, merge, publish,
16 * distribute, sublicense, and/or sell copies of the Software, and to
17 * permit persons to whom the Software is furnished to do so, subject to
18 * the following conditions:
19 *
20 * The above copyright notice and this permission notice shall be
21 * included in all copies or substantial portions of the Software.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 *
31 * @package WordPress
32 * @since 6.3.0
33 */
34
35/**
36 * Manages duotone block supports and global styles.
37 *
38 * @access private
39 */
40class WP_Duotone {
41 /**
42 * Block names from global, theme, and custom styles that use duotone presets and the slug of
43 * the preset they are using.
44 *
45 * Example:
46 * [
47 * 'core/featured-image' => 'blue-orange',
48 * …
49 * ]
50 *
51 * @internal
52 *
53 * @since 6.3.0
54 *
55 * @var array
56 */
57 private static $global_styles_block_names;
58
59 /**
60 * An array of duotone filter data from global, theme, and custom presets.
61 *
62 * Example:
63 * [
64 * 'wp-duotone-blue-orange' => [
65 * 'slug' => 'blue-orange',
66 * 'colors' => [ '#0000ff', '#ffcc00' ],
67 * ],
68 * 'wp-duotone-red-yellow' => [
69 * 'slug' => 'red-yellow',
70 * 'colors' => [ '#cc0000', '#ffff33' ],
71 * ],
72 * …
73 * ]
74 *
75 * @internal
76 *
77 * @since 6.3.0
78 *
79 * @var array
80 */
81 private static $global_styles_presets;
82
83 /**
84 * All of the duotone filter data from presets for CSS custom properties on
85 * the page.
86 *
87 * Example:
88 * [
89 * 'wp-duotone-blue-orange' => [
90 * 'slug' => 'blue-orange',
91 * 'colors' => [ '#0000ff', '#ffcc00' ],
92 * ],
93 * …
94 * ]
95 *
96 * @internal
97 *
98 * @since 6.3.0
99 *
100 * @var array
101 */
102 private static $used_global_styles_presets = array();
103
104 /**
105 * All of the duotone filter data for SVGs on the page. Includes both
106 * presets and custom filters.
107 *
108 * Example:
109 * [
110 * 'wp-duotone-blue-orange' => [
111 * 'slug' => 'blue-orange',
112 * 'colors' => [ '#0000ff', '#ffcc00' ],
113 * ],
114 * 'wp-duotone-000000-ffffff-2' => [
115 * 'slug' => '000000-ffffff-2',
116 * 'colors' => [ '#000000', '#ffffff' ],
117 * ],
118 * …
119 * ]
120 *
121 * @internal
122 *
123 * @since 6.3.0
124 *
125 * @var array
126 */
127 private static $used_svg_filter_data = array();
128
129 /**
130 * All of the block CSS declarations for styles on the page.
131 *
132 * Example:
133 * [
134 * [
135 * 'selector' => '.wp-duotone-000000-ffffff-2.wp-block-image img',
136 * 'declarations' => [
137 * 'filter' => 'url(#wp-duotone-000000-ffffff-2)',
138 * ],
139 * ],
140 * …
141 * ]
142 *
143 * @internal
144 *
145 * @since 6.3.0
146 *
147 * @var array
148 */
149 private static $block_css_declarations = array();
150
151 /**
152 * Clamps a value between an upper and lower bound.
153 *
154 * Direct port of colord's clamp function.
155 *
156 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/helpers.ts#L23 Sourced from colord.
157 *
158 * @internal
159 *
160 * @since 6.3.0
161 *
162 * @param float $number The number to clamp.
163 * @param float $min The minimum value.
164 * @param float $max The maximum value.
165 * @return float The clamped value.
166 */
167 private static function colord_clamp( $number, $min = 0, $max = 1 ) {
168 return $number > $max ? $max : ( $number > $min ? $number : $min );
169 }
170
171 /**
172 * Processes and clamps a degree (angle) value properly.
173 *
174 * Direct port of colord's clampHue function.
175 *
176 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/helpers.ts#L32 Sourced from colord.
177 *
178 * @internal
179 *
180 * @since 6.3.0
181 *
182 * @param float $degrees The hue to clamp.
183 * @return float The clamped hue.
184 */
185 private static function colord_clamp_hue( $degrees ) {
186 $degrees = is_finite( $degrees ) ? $degrees % 360 : 0;
187 return $degrees > 0 ? $degrees : $degrees + 360;
188 }
189
190 /**
191 * Converts a hue value to degrees from 0 to 360 inclusive.
192 *
193 * Direct port of colord's parseHue function.
194 *
195 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/helpers.ts#L40 Sourced from colord.
196 *
197 * @internal
198 *
199 * @since 6.3.0
200 *
201 * @param float $value The hue value to parse.
202 * @param string $unit The unit of the hue value.
203 * @return float The parsed hue value.
204 */
205 private static function colord_parse_hue( $value, $unit = 'deg' ) {
206 $angle_units = array(
207 'grad' => 360 / 400,
208 'turn' => 360,
209 'rad' => 360 / ( M_PI * 2 ),
210 );
211
212 $factor = isset( $angle_units[ $unit ] ) ? $angle_units[ $unit ] : 1;
213
214 return (float) $value * $factor;
215 }
216
217 /**
218 * Parses any valid Hex3, Hex4, Hex6 or Hex8 string and converts it to an RGBA object.
219 *
220 * Direct port of colord's parseHex function.
221 *
222 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/colorModels/hex.ts#L8 Sourced from colord.
223 *
224 * @internal
225 *
226 * @since 6.3.0
227 *
228 * @param string $hex The hex string to parse.
229 * @return array|null An array of RGBA values or null if the hex string is invalid.
230 */
231 private static function colord_parse_hex( $hex ) {
232 $is_match = preg_match(
233 '/^#([0-9a-f]{3,8})$/i',
234 $hex,
235 $hex_match
236 );
237
238 if ( ! $is_match ) {
239 return null;
240 }
241
242 $hex = $hex_match[1];
243
244 if ( 4 >= strlen( $hex ) ) {
245 return array(
246 'r' => (int) base_convert( $hex[0] . $hex[0], 16, 10 ),
247 'g' => (int) base_convert( $hex[1] . $hex[1], 16, 10 ),
248 'b' => (int) base_convert( $hex[2] . $hex[2], 16, 10 ),
249 'a' => 4 === strlen( $hex ) ? round( base_convert( $hex[3] . $hex[3], 16, 10 ) / 255, 2 ) : 1,
250 );
251 }
252
253 if ( 6 === strlen( $hex ) || 8 === strlen( $hex ) ) {
254 return array(
255 'r' => (int) base_convert( substr( $hex, 0, 2 ), 16, 10 ),
256 'g' => (int) base_convert( substr( $hex, 2, 2 ), 16, 10 ),
257 'b' => (int) base_convert( substr( $hex, 4, 2 ), 16, 10 ),
258 'a' => 8 === strlen( $hex ) ? round( (int) base_convert( substr( $hex, 6, 2 ), 16, 10 ) / 255, 2 ) : 1,
259 );
260 }
261
262 return null;
263 }
264
265 /**
266 * Clamps an array of RGBA values.
267 *
268 * Direct port of colord's clampRgba function.
269 *
270 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/colorModels/rgb.ts#L5 Sourced from colord.
271 *
272 * @internal
273 *
274 * @since 6.3.0
275 *
276 * @param array $rgba The RGBA array to clamp.
277 * @return array The clamped RGBA array.
278 */
279 private static function colord_clamp_rgba( $rgba ) {
280 $rgba['r'] = self::colord_clamp( $rgba['r'], 0, 255 );
281 $rgba['g'] = self::colord_clamp( $rgba['g'], 0, 255 );
282 $rgba['b'] = self::colord_clamp( $rgba['b'], 0, 255 );
283 $rgba['a'] = self::colord_clamp( $rgba['a'] );
284
285 return $rgba;
286 }
287
288 /**
289 * Parses a valid RGB[A] CSS color function/string.
290 *
291 * Direct port of colord's parseRgbaString function.
292 *
293 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/colorModels/rgbString.ts#L18 Sourced from colord.
294 *
295 * @internal
296 *
297 * @since 6.3.0
298 *
299 * @param string $input The RGBA string to parse.
300 * @return array|null An array of RGBA values or null if the RGB string is invalid.
301 */
302 private static function colord_parse_rgba_string( $input ) {
303 // Functional syntax.
304 $is_match = preg_match(
305 '/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i',
306 $input,
307 $match
308 );
309
310 if ( ! $is_match ) {
311 // Whitespace syntax.
312 $is_match = preg_match(
313 '/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i',
314 $input,
315 $match
316 );
317 }
318
319 if ( ! $is_match ) {
320 return null;
321 }
322
323 /*
324 * For some reason, preg_match doesn't include empty matches at the end
325 * of the array, so we add them manually to make things easier later.
326 */
327 for ( $i = 1; $i <= 8; $i++ ) {
328 if ( ! isset( $match[ $i ] ) ) {
329 $match[ $i ] = '';
330 }
331 }
332
333 if ( $match[2] !== $match[4] || $match[4] !== $match[6] ) {
334 return null;
335 }
336
337 return self::colord_clamp_rgba(
338 array(
339 'r' => (float) $match[1] / ( $match[2] ? 100 / 255 : 1 ),
340 'g' => (float) $match[3] / ( $match[4] ? 100 / 255 : 1 ),
341 'b' => (float) $match[5] / ( $match[6] ? 100 / 255 : 1 ),
342 'a' => '' === $match[7] ? 1 : (float) $match[7] / ( $match[8] ? 100 : 1 ),
343 )
344 );
345 }
346
347 /**
348 * Clamps an array of HSLA values.
349 *
350 * Direct port of colord's clampHsla function.
351 *
352 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/colorModels/hsl.ts#L6 Sourced from colord.
353 *
354 * @internal
355 *
356 * @since 6.3.0
357 *
358 * @param array $hsla The HSLA array to clamp.
359 * @return array The clamped HSLA array.
360 */
361 private static function colord_clamp_hsla( $hsla ) {
362 $hsla['h'] = self::colord_clamp_hue( $hsla['h'] );
363 $hsla['s'] = self::colord_clamp( $hsla['s'], 0, 100 );
364 $hsla['l'] = self::colord_clamp( $hsla['l'], 0, 100 );
365 $hsla['a'] = self::colord_clamp( $hsla['a'] );
366
367 return $hsla;
368 }
369
370 /**
371 * Converts an HSVA array to RGBA.
372 *
373 * Direct port of colord's hsvaToRgba function.
374 *
375 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/colorModels/hsv.ts#L52 Sourced from colord.
376 *
377 * @internal
378 *
379 * @since 6.3.0
380 *
381 * @param array $hsva The HSVA array to convert.
382 * @return array The RGBA array.
383 */
384 private static function colord_hsva_to_rgba( $hsva ) {
385 $h = ( $hsva['h'] / 360 ) * 6;
386 $s = $hsva['s'] / 100;
387 $v = $hsva['v'] / 100;
388 $a = $hsva['a'];
389
390 $hh = floor( $h );
391 $b = $v * ( 1 - $s );
392 $c = $v * ( 1 - ( $h - $hh ) * $s );
393 $d = $v * ( 1 - ( 1 - $h + $hh ) * $s );
394 $module = $hh % 6;
395
396 return array(
397 'r' => array( $v, $c, $b, $b, $d, $v )[ $module ] * 255,
398 'g' => array( $d, $v, $v, $c, $b, $b )[ $module ] * 255,
399 'b' => array( $b, $b, $d, $v, $v, $c )[ $module ] * 255,
400 'a' => $a,
401 );
402 }
403
404 /**
405 * Converts an HSLA array to HSVA.
406 *
407 * Direct port of colord's hslaToHsva function.
408 *
409 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/colorModels/hsl.ts#L33 Sourced from colord.
410 *
411 * @internal
412 *
413 * @since 6.3.0
414 *
415 * @param array $hsla The HSLA array to convert.
416 * @return array The HSVA array.
417 */
418 private static function colord_hsla_to_hsva( $hsla ) {
419 $h = $hsla['h'];
420 $s = $hsla['s'];
421 $l = $hsla['l'];
422 $a = $hsla['a'];
423
424 $s *= ( $l < 50 ? $l : 100 - $l ) / 100;
425
426 return array(
427 'h' => $h,
428 's' => $s > 0 ? ( ( 2 * $s ) / ( $l + $s ) ) * 100 : 0,
429 'v' => $l + $s,
430 'a' => $a,
431 );
432 }
433
434 /**
435 * Converts an HSLA array to RGBA.
436 *
437 * Direct port of colord's hslaToRgba function.
438 *
439 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/colorModels/hsl.ts#L55 Sourced from colord.
440 *
441 * @internal
442 *
443 * @since 6.3.0
444 *
445 * @param array $hsla The HSLA array to convert.
446 * @return array The RGBA array.
447 */
448 private static function colord_hsla_to_rgba( $hsla ) {
449 return self::colord_hsva_to_rgba( self::colord_hsla_to_hsva( $hsla ) );
450 }
451
452 /**
453 * Parses a valid HSL[A] CSS color function/string.
454 *
455 * Direct port of colord's parseHslaString function.
456 *
457 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/colorModels/hslString.ts#L17 Sourced from colord.
458 *
459 * @internal
460 *
461 * @since 6.3.0
462 *
463 * @param string $input The HSLA string to parse.
464 * @return array|null An array of RGBA values or null if the RGB string is invalid.
465 */
466 private static function colord_parse_hsla_string( $input ) {
467 // Functional syntax.
468 $is_match = preg_match(
469 '/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s*,\s*([+-]?\d*\.?\d+)%\s*,\s*([+-]?\d*\.?\d+)%\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i',
470 $input,
471 $match
472 );
473
474 if ( ! $is_match ) {
475 // Whitespace syntax.
476 $is_match = preg_match(
477 '/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s+([+-]?\d*\.?\d+)%\s+([+-]?\d*\.?\d+)%\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i',
478 $input,
479 $match
480 );
481 }
482
483 if ( ! $is_match ) {
484 return null;
485 }
486
487 /*
488 * For some reason, preg_match doesn't include empty matches at the end
489 * of the array, so we add them manually to make things easier later.
490 */
491 for ( $i = 1; $i <= 6; $i++ ) {
492 if ( ! isset( $match[ $i ] ) ) {
493 $match[ $i ] = '';
494 }
495 }
496
497 $hsla = self::colord_clamp_hsla(
498 array(
499 'h' => self::colord_parse_hue( $match[1], $match[2] ),
500 's' => (float) $match[3],
501 'l' => (float) $match[4],
502 'a' => '' === $match[5] ? 1 : (float) $match[5] / ( $match[6] ? 100 : 1 ),
503 )
504 );
505
506 return self::colord_hsla_to_rgba( $hsla );
507 }
508
509 /**
510 * Tries to convert an incoming string into RGBA values.
511 *
512 * Direct port of colord's parse function simplified for our use case. This
513 * version only supports string parsing and only returns RGBA values.
514 *
515 * @link https://github.com/omgovich/colord/blob/3f859e03b0ca622eb15480f611371a0f15c9427f/src/parse.ts#L37 Sourced from colord.
516 *
517 * @internal
518 *
519 * @since 6.3.0
520 *
521 * @param string $input The string to parse.
522 * @return array|null An array of RGBA values or null if the string is invalid.
523 */
524 private static function colord_parse( $input ) {
525 $result = self::colord_parse_hex( $input );
526
527 if ( ! $result ) {
528 $result = self::colord_parse_rgba_string( $input );
529 }
530
531 if ( ! $result ) {
532 $result = self::colord_parse_hsla_string( $input );
533 }
534
535 return $result;
536 }
537
538 /**
539 * Takes the inline CSS duotone variable from a block and return the slug.
540 *
541 * Handles styles slugs like:
542 * var:preset|duotone|blue-orange
543 * var(--wp--preset--duotone--blue-orange)
544 *
545 * @internal
546 *
547 * @since 6.3.0
548 *
549 * @param string $duotone_attr The duotone attribute from a block.
550 * @return string The slug of the duotone preset or an empty string if no slug is found.
551 */
552 private static function get_slug_from_attribute( $duotone_attr ) {
553 // Uses Branch Reset Groups `(?|…)` to return one capture group.
554 preg_match( '/(?|var:preset\|duotone\|(\S+)|var\(--wp--preset--duotone--(\S+)\))/', $duotone_attr, $matches );
555
556 return ! empty( $matches[1] ) ? $matches[1] : '';
557 }
558
559 /**
560 * Checks if we have a valid duotone preset.
561 *
562 * Valid presets are defined in the $global_styles_presets array.
563 *
564 * @internal
565 *
566 * @since 6.3.0
567 *
568 * @param string $duotone_attr The duotone attribute from a block.
569 * @return bool True if the duotone preset present and valid.
570 */
571 private static function is_preset( $duotone_attr ) {
572 $slug = self::get_slug_from_attribute( $duotone_attr );
573 $filter_id = self::get_filter_id( $slug );
574
575 return array_key_exists( $filter_id, self::get_all_global_styles_presets() );
576 }
577
578 /**
579 * Gets the CSS variable name for a duotone preset.
580 *
581 * Example output:
582 * --wp--preset--duotone--blue-orange
583 *
584 * @internal
585 *
586 * @since 6.3.0
587 *
588 * @param string $slug The slug of the duotone preset.
589 * @return string The CSS variable name.
590 */
591 private static function get_css_custom_property_name( $slug ) {
592 return "--wp--preset--duotone--$slug";
593 }
594
595 /**
596 * Get the ID of the duotone filter.
597 *
598 * Example output:
599 * wp-duotone-blue-orange
600 *
601 * @internal
602 *
603 * @since 6.3.0
604 *
605 * @param string $slug The slug of the duotone preset.
606 * @return string The ID of the duotone filter.
607 */
608 private static function get_filter_id( $slug ) {
609 return "wp-duotone-$slug";
610 }
611
612 /**
613 * Get the CSS variable for a duotone preset.
614 *
615 * Example output:
616 * var(--wp--preset--duotone--blue-orange)
617 *
618 * @internal
619 *
620 * @since 6.3.0
621 *
622 * @param string $slug The slug of the duotone preset.
623 * @return string The CSS variable.
624 */
625 private static function get_css_var( $slug ) {
626 $name = self::get_css_custom_property_name( $slug );
627 return "var($name)";
628 }
629
630 /**
631 * Get the URL for a duotone filter.
632 *
633 * Example output:
634 * url(#wp-duotone-blue-orange)
635 *
636 * @internal
637 *
638 * @since 6.3.0
639 *
640 * @param string $filter_id The ID of the filter.
641 * @return string The URL for the duotone filter.
642 */
643 private static function get_filter_url( $filter_id ) {
644 return "url(#$filter_id)";
645 }
646
647 /**
648 * Gets the SVG for the duotone filter definition.
649 *
650 * Whitespace is removed when SCRIPT_DEBUG is not enabled.
651 *
652 * @internal
653 *
654 * @since 6.3.0
655 *
656 * @param string $filter_id The ID of the filter.
657 * @param array $colors An array of color strings.
658 * @return string An SVG with a duotone filter definition.
659 */
660 private static function get_filter_svg( $filter_id, $colors ) {
661 $duotone_values = array(
662 'r' => array(),
663 'g' => array(),
664 'b' => array(),
665 'a' => array(),
666 );
667
668 foreach ( $colors as $color_str ) {
669 $color = self::colord_parse( $color_str );
670
671 if ( null === $color ) {
672 $error_message = sprintf(
673 /* translators: 1: Duotone colors, 2: theme.json, 3: settings.color.duotone */
674 __( '"%1$s" in %2$s %3$s is not a hex or rgb string.' ),
675 $color_str,
676 'theme.json',
677 'settings.color.duotone'
678 );
679 _doing_it_wrong( __METHOD__, $error_message, '6.3.0' );
680 } else {
681 $duotone_values['r'][] = $color['r'] / 255;
682 $duotone_values['g'][] = $color['g'] / 255;
683 $duotone_values['b'][] = $color['b'] / 255;
684 $duotone_values['a'][] = $color['a'];
685 }
686 }
687
688 ob_start();
689
690 ?>
691
692 <svg
693 xmlns="http://www.w3.org/2000/svg"
694 viewBox="0 0 0 0"
695 width="0"
696 height="0"
697 focusable="false"
698 role="none"
699 style="visibility: hidden; position: absolute; left: -9999px; overflow: hidden;"
700 >
701 <defs>
702 <filter id="<?php echo esc_attr( $filter_id ); ?>">
703 <feColorMatrix
704 color-interpolation-filters="sRGB"
705 type="matrix"
706 values="
707 .299 .587 .114 0 0
708 .299 .587 .114 0 0
709 .299 .587 .114 0 0
710 .299 .587 .114 0 0
711 "
712 />
713 <feComponentTransfer color-interpolation-filters="sRGB" >
714 <feFuncR type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['r'] ) ); ?>" />
715 <feFuncG type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['g'] ) ); ?>" />
716 <feFuncB type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['b'] ) ); ?>" />
717 <feFuncA type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['a'] ) ); ?>" />
718 </feComponentTransfer>
719 <feComposite in2="SourceGraphic" operator="in" />
720 </filter>
721 </defs>
722 </svg>
723
724 <?php
725
726 $svg = ob_get_clean();
727
728 if ( ! SCRIPT_DEBUG ) {
729 // Clean up the whitespace.
730 $svg = preg_replace( "/[\r\n\t ]+/", ' ', $svg );
731 $svg = str_replace( '> <', '><', $svg );
732 $svg = trim( $svg );
733 }
734
735 return $svg;
736 }
737
738 /**
739 * Returns the prefixed id for the duotone filter for use as a CSS id.
740 *
741 * Exported for the deprecated function wp_get_duotone_filter_id().
742 *
743 * @internal
744 *
745 * @since 6.3.0
746 * @deprecated 6.3.0
747 *
748 * @param array $preset Duotone preset value as seen in theme.json.
749 * @return string Duotone filter CSS id.
750 */
751 public static function get_filter_id_from_preset( $preset ) {
752 _deprecated_function( __FUNCTION__, '6.3.0' );
753
754 $filter_id = '';
755 if ( isset( $preset['slug'] ) ) {
756 $filter_id = self::get_filter_id( $preset['slug'] );
757 }
758 return $filter_id;
759 }
760
761 /**
762 * Gets the SVG for the duotone filter definition from a preset.
763 *
764 * Exported for the deprecated function wp_get_duotone_filter_property().
765 *
766 * @internal
767 *
768 * @since 6.3.0
769 * @deprecated 6.3.0
770 *
771 * @param array $preset The duotone preset.
772 * @return string The SVG for the filter definition.
773 */
774 public static function get_filter_svg_from_preset( $preset ) {
775 _deprecated_function( __FUNCTION__, '6.3.0' );
776
777 $filter_id = self::get_filter_id_from_preset( $preset );
778 return self::get_filter_svg( $filter_id, $preset['colors'] );
779 }
780
781 /**
782 * Get the SVGs for the duotone filters.
783 *
784 * Example output:
785 * <svg><defs><filter id="wp-duotone-blue-orange">…</filter></defs></svg><svg>…</svg>
786 *
787 * @internal
788 *
789 * @since 6.3.0
790 *
791 * @param array $sources The duotone presets.
792 * @return string The SVGs for the duotone filters.
793 */
794 private static function get_svg_definitions( $sources ) {
795 $svgs = '';
796 foreach ( $sources as $filter_id => $filter_data ) {
797 $colors = $filter_data['colors'];
798 $svgs .= self::get_filter_svg( $filter_id, $colors );
799 }
800 return $svgs;
801 }
802
803 /**
804 * Get the CSS for global styles.
805 *
806 * Example output:
807 * body{--wp--preset--duotone--blue-orange:url('#wp-duotone-blue-orange');}
808 *
809 * @internal
810 *
811 * @since 6.3.0
812 * @since 6.6.0 Replaced body selector with `WP_Theme_JSON::ROOT_CSS_PROPERTIES_SELECTOR`.
813 *
814 * @param array $sources The duotone presets.
815 * @return string The CSS for global styles.
816 */
817 private static function get_global_styles_presets( $sources ) {
818 $css = WP_Theme_JSON::ROOT_CSS_PROPERTIES_SELECTOR . '{';
819 foreach ( $sources as $filter_id => $filter_data ) {
820 $slug = $filter_data['slug'];
821 $colors = $filter_data['colors'];
822 $css_property_name = self::get_css_custom_property_name( $slug );
823 $declaration_value = is_string( $colors ) ? $colors : self::get_filter_url( $filter_id );
824 $css .= "$css_property_name:$declaration_value;";
825 }
826 $css .= '}';
827 return $css;
828 }
829
830 /**
831 * Enqueue a block CSS declaration for the page.
832 *
833 * This does not include any SVGs.
834 *
835 * @internal
836 *
837 * @since 6.3.0
838 *
839 * @param string $filter_id The filter ID. e.g. 'wp-duotone-000000-ffffff-2'.
840 * @param string $duotone_selector The block's duotone selector. e.g. '.wp-block-image img'.
841 * @param string $filter_value The filter CSS value. e.g. 'url(#wp-duotone-000000-ffffff-2)' or 'unset'.
842 */
843 private static function enqueue_block_css( $filter_id, $duotone_selector, $filter_value ) {
844 // Build the CSS selectors to which the filter will be applied.
845 $selectors = explode( ',', $duotone_selector );
846
847 $selectors_scoped = array();
848 foreach ( $selectors as $selector_part ) {
849 /*
850 * Assuming the selector part is a subclass selector (not a tag name)
851 * so we can prepend the filter id class. If we want to support elements
852 * such as `img` or namespaces, we'll need to add a case for that here.
853 */
854 $selectors_scoped[] = '.' . $filter_id . trim( $selector_part );
855 }
856
857 $selector = implode( ', ', $selectors_scoped );
858
859 self::$block_css_declarations[] = array(
860 'selector' => $selector,
861 'declarations' => array(
862 'filter' => $filter_value,
863 ),
864 );
865 }
866
867 /**
868 * Enqueue custom filter assets for the page.
869 *
870 * Includes an SVG filter and block CSS declaration.
871 *
872 * @internal
873 *
874 * @since 6.3.0
875 *
876 * @param string $filter_id The filter ID. e.g. 'wp-duotone-000000-ffffff-2'.
877 * @param string $duotone_selector The block's duotone selector. e.g. '.wp-block-image img'.
878 * @param string $filter_value The filter CSS value. e.g. 'url(#wp-duotone-000000-ffffff-2)' or 'unset'.
879 * @param array $filter_data Duotone filter data with 'slug' and 'colors' keys.
880 */
881 private static function enqueue_custom_filter( $filter_id, $duotone_selector, $filter_value, $filter_data ) {
882 self::$used_svg_filter_data[ $filter_id ] = $filter_data;
883 self::enqueue_block_css( $filter_id, $duotone_selector, $filter_value );
884 }
885
886 /**
887 * Enqueue preset assets for the page.
888 *
889 * Includes a CSS custom property, SVG filter, and block CSS declaration.
890 *
891 * @internal
892 *
893 * @since 6.3.0
894 *
895 * @param string $filter_id The filter ID. e.g. 'wp-duotone-blue-orange'.
896 * @param string $duotone_selector The block's duotone selector. e.g. '.wp-block-image img'.
897 * @param string $filter_value The filter CSS value. e.g. 'url(#wp-duotone-blue-orange)' or 'unset'.
898 */
899 private static function enqueue_global_styles_preset( $filter_id, $duotone_selector, $filter_value ) {
900 $global_styles_presets = self::get_all_global_styles_presets();
901 if ( ! array_key_exists( $filter_id, $global_styles_presets ) ) {
902 $error_message = sprintf(
903 /* translators: 1: Duotone filter ID, 2: theme.json */
904 __( 'The duotone id "%1$s" is not registered in %2$s settings' ),
905 $filter_id,
906 'theme.json'
907 );
908 _doing_it_wrong( __METHOD__, $error_message, '6.3.0' );
909 return;
910 }
911 self::$used_global_styles_presets[ $filter_id ] = $global_styles_presets[ $filter_id ];
912 self::enqueue_custom_filter( $filter_id, $duotone_selector, $filter_value, $global_styles_presets[ $filter_id ] );
913 }
914
915 /**
916 * Registers the style and colors block attributes for block types that support it.
917 *
918 * Block support is added with `supports.filter.duotone` in block.json.
919 *
920 * @since 6.3.0
921 *
922 * @param WP_Block_Type $block_type Block Type.
923 */
924 public static function register_duotone_support( $block_type ) {
925 /*
926 * Previous `color.__experimentalDuotone` support flag is migrated
927 * to `filter.duotone` via `block_type_metadata_settings` filter.
928 */
929 if ( block_has_support( $block_type, array( 'filter', 'duotone' ), null ) ) {
930 if ( ! $block_type->attributes ) {
931 $block_type->attributes = array();
932 }
933
934 if ( ! array_key_exists( 'style', $block_type->attributes ) ) {
935 $block_type->attributes['style'] = array(
936 'type' => 'object',
937 );
938 }
939 }
940 }
941
942 /**
943 * Get the CSS selector for a block type.
944 *
945 * This handles selectors defined in `color.__experimentalDuotone` support
946 * if `filter.duotone` support is not defined.
947 *
948 * @internal
949 * @since 6.3.0
950 *
951 * @param WP_Block_Type $block_type Block type to check for support.
952 * @return string|null The CSS selector or null if there is no support.
953 */
954 private static function get_selector( $block_type ) {
955 if ( ! ( $block_type instanceof WP_Block_Type ) ) {
956 return null;
957 }
958
959 /*
960 * Backward compatibility with `supports.color.__experimentalDuotone`
961 * is provided via the `block_type_metadata_settings` filter. If
962 * `supports.filter.duotone` has not been set and the experimental
963 * property has been, the experimental property value is copied into
964 * `supports.filter.duotone`.
965 */
966 $duotone_support = block_has_support( $block_type, array( 'filter', 'duotone' ) );
967 if ( ! $duotone_support ) {
968 return null;
969 }
970
971 /*
972 * If the experimental duotone support was set, that value is to be
973 * treated as a selector and requires scoping.
974 */
975 $experimental_duotone = isset( $block_type->supports['color']['__experimentalDuotone'] )
976 ? $block_type->supports['color']['__experimentalDuotone']
977 : false;
978 if ( $experimental_duotone ) {
979 $root_selector = wp_get_block_css_selector( $block_type );
980 return is_string( $experimental_duotone )
981 ? WP_Theme_JSON::scope_selector( $root_selector, $experimental_duotone )
982 : $root_selector;
983 }
984
985 // Regular filter.duotone support uses filter.duotone selectors with fallbacks.
986 return wp_get_block_css_selector( $block_type, array( 'filter', 'duotone' ), true );
987 }
988
989 /**
990 * Scrape all possible duotone presets from global and theme styles and
991 * store them in self::$global_styles_presets.
992 *
993 * Used in conjunction with self::render_duotone_support for blocks that
994 * use duotone preset filters.
995 *
996 * @since 6.3.0
997 *
998 * @return array An array of global styles presets, keyed on the filter ID.
999 */
1000 private static function get_all_global_styles_presets() {
1001 if ( isset( self::$global_styles_presets ) ) {
1002 return self::$global_styles_presets;
1003 }
1004 // Get the per block settings from the theme.json.
1005 $tree = wp_get_global_settings();
1006 $presets_by_origin = isset( $tree['color']['duotone'] ) ? $tree['color']['duotone'] : array();
1007
1008 self::$global_styles_presets = array();
1009 foreach ( $presets_by_origin as $presets ) {
1010 foreach ( $presets as $preset ) {
1011 $filter_id = self::get_filter_id( _wp_to_kebab_case( $preset['slug'] ) );
1012
1013 self::$global_styles_presets[ $filter_id ] = $preset;
1014 }
1015 }
1016
1017 return self::$global_styles_presets;
1018 }
1019
1020 /**
1021 * Scrape all block names from global styles and store in self::$global_styles_block_names.
1022 *
1023 * Used in conjunction with self::render_duotone_support to output the
1024 * duotone filters defined in the theme.json global styles.
1025 *
1026 * @since 6.3.0
1027 *
1028 * @return string[] An array of global style block slugs, keyed on the block name.
1029 */
1030 private static function get_all_global_style_block_names() {
1031 if ( isset( self::$global_styles_block_names ) ) {
1032 return self::$global_styles_block_names;
1033 }
1034 // Get the per block settings from the theme.json.
1035 $tree = WP_Theme_JSON_Resolver::get_merged_data();
1036 $block_nodes = $tree->get_styles_block_nodes();
1037 $theme_json = $tree->get_raw_data();
1038
1039 self::$global_styles_block_names = array();
1040
1041 foreach ( $block_nodes as $block_node ) {
1042 // This block definition doesn't include any duotone settings. Skip it.
1043 if ( empty( $block_node['duotone'] ) ) {
1044 continue;
1045 }
1046
1047 // Value looks like this: 'var(--wp--preset--duotone--blue-orange)' or 'var:preset|duotone|blue-orange'.
1048 $duotone_attr_path = array_merge( $block_node['path'], array( 'filter', 'duotone' ) );
1049 $duotone_attr = _wp_array_get( $theme_json, $duotone_attr_path, array() );
1050
1051 if ( empty( $duotone_attr ) ) {
1052 continue;
1053 }
1054 // If it has a duotone filter preset, save the block name and the preset slug.
1055 $slug = self::get_slug_from_attribute( $duotone_attr );
1056
1057 if ( $slug && $slug !== $duotone_attr ) {
1058 self::$global_styles_block_names[ $block_node['name'] ] = $slug;
1059 }
1060 }
1061 return self::$global_styles_block_names;
1062 }
1063
1064 /**
1065 * Render out the duotone CSS styles and SVG.
1066 *
1067 * The hooks self::set_global_style_block_names and self::set_global_styles_presets
1068 * must be called before this function.
1069 *
1070 * @since 6.3.0
1071 *
1072 * @param string $block_content Rendered block content.
1073 * @param array $block Block object.
1074 * @param WP_Block $wp_block The block instance.
1075 * @return string Filtered block content.
1076 */
1077 public static function render_duotone_support( $block_content, $block, $wp_block ) {
1078 if ( ! $block['blockName'] ) {
1079 return $block_content;
1080 }
1081 $duotone_selector = self::get_selector( $wp_block->block_type );
1082
1083 if ( ! $duotone_selector ) {
1084 return $block_content;
1085 }
1086
1087 $global_styles_block_names = self::get_all_global_style_block_names();
1088
1089 // The block should have a duotone attribute or have duotone defined in its theme.json to be processed.
1090 $has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] );
1091 $has_global_styles_duotone = array_key_exists( $block['blockName'], $global_styles_block_names );
1092
1093 if ( ! $has_duotone_attribute && ! $has_global_styles_duotone ) {
1094 return $block_content;
1095 }
1096
1097 // Generate the pieces needed for rendering a duotone to the page.
1098 if ( $has_duotone_attribute ) {
1099
1100 /*
1101 * Possible values for duotone attribute:
1102 * 1. Array of colors - e.g. array('#000000', '#ffffff').
1103 * 2. Variable for an existing Duotone preset - e.g. 'var:preset|duotone|blue-orange' or 'var(--wp--preset--duotone--blue-orange)''
1104 * 3. A CSS string - e.g. 'unset' to remove globally applied duotone.
1105 */
1106
1107 $duotone_attr = $block['attrs']['style']['color']['duotone'];
1108 $is_preset = is_string( $duotone_attr ) && self::is_preset( $duotone_attr );
1109 $is_css = is_string( $duotone_attr ) && ! $is_preset;
1110 $is_custom = is_array( $duotone_attr );
1111
1112 if ( $is_preset ) {
1113
1114 $slug = self::get_slug_from_attribute( $duotone_attr ); // e.g. 'blue-orange'.
1115 $filter_id = self::get_filter_id( $slug ); // e.g. 'wp-duotone-filter-blue-orange'.
1116 $filter_value = self::get_css_var( $slug ); // e.g. 'var(--wp--preset--duotone--blue-orange)'.
1117
1118 // CSS custom property, SVG filter, and block CSS.
1119 self::enqueue_global_styles_preset( $filter_id, $duotone_selector, $filter_value );
1120
1121 } elseif ( $is_css ) {
1122 $slug = wp_unique_id( sanitize_key( $duotone_attr . '-' ) ); // e.g. 'unset-1'.
1123 $filter_id = self::get_filter_id( $slug ); // e.g. 'wp-duotone-filter-unset-1'.
1124 $filter_value = $duotone_attr; // e.g. 'unset'.
1125
1126 // Just block CSS.
1127 self::enqueue_block_css( $filter_id, $duotone_selector, $filter_value );
1128 } elseif ( $is_custom ) {
1129 $slug = wp_unique_id( sanitize_key( implode( '-', $duotone_attr ) . '-' ) ); // e.g. '000000-ffffff-2'.
1130 $filter_id = self::get_filter_id( $slug ); // e.g. 'wp-duotone-filter-000000-ffffff-2'.
1131 $filter_value = self::get_filter_url( $filter_id ); // e.g. 'url(#wp-duotone-filter-000000-ffffff-2)'.
1132 $filter_data = array(
1133 'slug' => $slug,
1134 'colors' => $duotone_attr,
1135 );
1136
1137 // SVG filter and block CSS.
1138 self::enqueue_custom_filter( $filter_id, $duotone_selector, $filter_value, $filter_data );
1139 }
1140 } elseif ( $has_global_styles_duotone ) {
1141 $slug = $global_styles_block_names[ $block['blockName'] ]; // e.g. 'blue-orange'.
1142 $filter_id = self::get_filter_id( $slug ); // e.g. 'wp-duotone-filter-blue-orange'.
1143 $filter_value = self::get_css_var( $slug ); // e.g. 'var(--wp--preset--duotone--blue-orange)'.
1144
1145 // CSS custom property, SVG filter, and block CSS.
1146 self::enqueue_global_styles_preset( $filter_id, $duotone_selector, $filter_value );
1147 }
1148
1149 // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
1150 $tags = new WP_HTML_Tag_Processor( $block_content );
1151 if ( $tags->next_tag() ) {
1152 $tags->add_class( $filter_id );
1153 }
1154 return $tags->get_updated_html();
1155 }
1156
1157 /**
1158 * Fixes the issue with our generated class name not being added to the block's outer container
1159 * in classic themes due to gutenberg_restore_image_outer_container from layout block supports.
1160 *
1161 * @since 6.6.0
1162 *
1163 * @param string $block_content Rendered block content.
1164 * @return string Filtered block content.
1165 */
1166 public static function restore_image_outer_container( $block_content ) {
1167 if ( wp_theme_has_theme_json() ) {
1168 return $block_content;
1169 }
1170
1171 $tags = new WP_HTML_Tag_Processor( $block_content );
1172 $wrapper_query = array(
1173 'tag_name' => 'div',
1174 'class_name' => 'wp-block-image',
1175 );
1176 if ( ! $tags->next_tag( $wrapper_query ) ) {
1177 return $block_content;
1178 }
1179
1180 $tags->set_bookmark( 'wrapper-div' );
1181 $tags->next_tag();
1182
1183 $inner_classnames = explode( ' ', $tags->get_attribute( 'class' ) );
1184 foreach ( $inner_classnames as $classname ) {
1185 if ( 0 === strpos( $classname, 'wp-duotone' ) ) {
1186 $tags->remove_class( $classname );
1187 $tags->seek( 'wrapper-div' );
1188 $tags->add_class( $classname );
1189 break;
1190 }
1191 }
1192
1193 return $tags->get_updated_html();
1194 }
1195
1196 /**
1197 * Appends the used block duotone filter declarations to the inline block supports CSS.
1198 *
1199 * Uses the declarations saved in earlier calls to self::enqueue_block_css.
1200 *
1201 * @since 6.3.0
1202 */
1203 public static function output_block_styles() {
1204 if ( ! empty( self::$block_css_declarations ) ) {
1205 wp_style_engine_get_stylesheet_from_css_rules(
1206 self::$block_css_declarations,
1207 array(
1208 'context' => 'block-supports',
1209 )
1210 );
1211 }
1212 }
1213
1214 /**
1215 * Appends the used global style duotone filter presets (CSS custom
1216 * properties) to the inline global styles CSS.
1217 *
1218 * Uses the declarations saved in earlier calls to self::enqueue_global_styles_preset.
1219 *
1220 * @since 6.3.0
1221 */
1222 public static function output_global_styles() {
1223 if ( ! empty( self::$used_global_styles_presets ) ) {
1224 wp_add_inline_style( 'global-styles', self::get_global_styles_presets( self::$used_global_styles_presets ) );
1225 }
1226 }
1227
1228 /**
1229 * Outputs all necessary SVG for duotone filters, CSS for classic themes.
1230 *
1231 * Uses the declarations saved in earlier calls to self::enqueue_global_styles_preset
1232 * and self::enqueue_custom_filter.
1233 *
1234 * @since 6.3.0
1235 */
1236 public static function output_footer_assets() {
1237 if ( ! empty( self::$used_svg_filter_data ) ) {
1238 echo self::get_svg_definitions( self::$used_svg_filter_data );
1239 }
1240
1241 // In block themes, the CSS is added in the head via wp_add_inline_style in the wp_enqueue_scripts action.
1242 if ( ! wp_is_block_theme() ) {
1243 $style_tag_id = 'core-block-supports-duotone';
1244 wp_register_style( $style_tag_id, false );
1245 if ( ! empty( self::$used_global_styles_presets ) ) {
1246 wp_add_inline_style( $style_tag_id, self::get_global_styles_presets( self::$used_global_styles_presets ) );
1247 }
1248 if ( ! empty( self::$block_css_declarations ) ) {
1249 wp_add_inline_style( $style_tag_id, wp_style_engine_get_stylesheet_from_css_rules( self::$block_css_declarations ) );
1250 }
1251 wp_enqueue_style( $style_tag_id );
1252 }
1253 }
1254
1255 /**
1256 * Adds the duotone SVGs and CSS custom properties to the editor settings.
1257 *
1258 * This allows the properties to be pulled in by the EditorStyles component
1259 * in JS and rendered in the post editor.
1260 *
1261 * @since 6.3.0
1262 *
1263 * @param array $settings The block editor settings from the `block_editor_settings_all` filter.
1264 * @return array The editor settings with duotone SVGs and CSS custom properties.
1265 */
1266 public static function add_editor_settings( $settings ) {
1267 $global_styles_presets = self::get_all_global_styles_presets();
1268 if ( ! empty( $global_styles_presets ) ) {
1269 if ( ! isset( $settings['styles'] ) ) {
1270 $settings['styles'] = array();
1271 }
1272
1273 $settings['styles'][] = array(
1274 // For the editor we can add all of the presets by default.
1275 'assets' => self::get_svg_definitions( $global_styles_presets ),
1276 // The 'svgs' type is new in 6.3 and requires the corresponding JS changes in the EditorStyles component to work.
1277 '__unstableType' => 'svgs',
1278 // These styles not generated by global styles, so this must be false or they will be stripped out in wp_get_block_editor_settings.
1279 'isGlobalStyles' => false,
1280 );
1281
1282 $settings['styles'][] = array(
1283 // For the editor we can add all of the presets by default.
1284 'css' => self::get_global_styles_presets( $global_styles_presets ),
1285 // This must be set and must be something other than 'theme' or they will be stripped out in the post editor <Editor> component.
1286 '__unstableType' => 'presets',
1287 // These styles are no longer generated by global styles, so this must be false or they will be stripped out in wp_get_block_editor_settings.
1288 'isGlobalStyles' => false,
1289 );
1290 }
1291
1292 return $settings;
1293 }
1294
1295 /**
1296 * Migrates the experimental duotone support flag to the stabilized location.
1297 *
1298 * This moves `supports.color.__experimentalDuotone` to `supports.filter.duotone`.
1299 *
1300 * @since 6.3.0
1301 *
1302 * @param array $settings Current block type settings.
1303 * @param array $metadata Block metadata as read in via block.json.
1304 * @return array Filtered block type settings.
1305 */
1306 public static function migrate_experimental_duotone_support_flag( $settings, $metadata ) {
1307 $duotone_support = isset( $metadata['supports']['color']['__experimentalDuotone'] )
1308 ? $metadata['supports']['color']['__experimentalDuotone']
1309 : null;
1310
1311 if ( ! isset( $settings['supports']['filter']['duotone'] ) && null !== $duotone_support ) {
1312 _wp_array_set( $settings, array( 'supports', 'filter', 'duotone' ), (bool) $duotone_support );
1313 }
1314
1315 return $settings;
1316 }
1317
1318 /**
1319 * Gets the CSS filter property value from a preset.
1320 *
1321 * Exported for the deprecated function wp_get_duotone_filter_id().
1322 *
1323 * @internal
1324 *
1325 * @since 6.3.0
1326 * @deprecated 6.3.0
1327 *
1328 * @param array $preset The duotone preset.
1329 * @return string The CSS filter property value.
1330 */
1331 public static function get_filter_css_property_value_from_preset( $preset ) {
1332 _deprecated_function( __FUNCTION__, '6.3.0' );
1333
1334 if ( isset( $preset['colors'] ) && is_string( $preset['colors'] ) ) {
1335 return $preset['colors'];
1336 }
1337
1338 $filter_id = self::get_filter_id_from_preset( $preset );
1339
1340 return 'url(#' . $filter_id . ')';
1341 }
1342}
1343
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1