├── .deployignore ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .phpunit-watcher.yml ├── CHANGELOG.md ├── README.md ├── asset-manager.php ├── composer.json ├── js └── loadCSS.min.js ├── license.txt ├── package-lock.json ├── package.json ├── src ├── class-asset-manager.php ├── class-preload.php ├── class-scripts.php ├── class-styles.php ├── class-svg-sprite.php ├── concerns │ ├── trait-asset-error.php │ ├── trait-conditions.php │ └── trait-singleton.php ├── helpers.php └── kses-svg.php └── wp-asset-manager.php /.deployignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .github 3 | .phpcs 4 | .phpcs.xml 5 | .phpunit.result.cache 6 | .scaffolder 7 | .scoper 8 | *.sql 9 | *.tar.gz 10 | *.zip 11 | bin 12 | composer.lock 13 | configure.php 14 | DOCKER_ENV 15 | Dockerfile 16 | Makefile 17 | node_modules/ 18 | output.log 19 | phpunit.xml 20 | phpunit.xml 21 | tests 22 | tests 23 | Thumbs.db 24 | wp-cli.local.yml 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # Unix-style newlines with a newline ending every file. 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | indent_style = tab 10 | 11 | [*.{js,jsx,ts,tsx,scss,css,json,yaml,yml,feature}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | # Dotfiles 16 | [.*] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # Exclude these files from release archives. 3 | # 4 | /.github export-ignore 5 | /.phpcs export-ignore 6 | /phpcs.xml export-ignore 7 | /phpunit.xml export-ignore 8 | /bin export-ignore 9 | /tests export-ignore 10 | 11 | # 12 | # Auto detect text files and perform LF normalization. 13 | # 14 | # http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/ 15 | # 16 | * text=auto 17 | 18 | # 19 | # The above will handle all files not found below. 20 | # 21 | *.md text 22 | *.php text 23 | *.inc text 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | tests/php/build 4 | tests/php/wp-unit-test 5 | 6 | # Silly Macs 7 | .DS_Store 8 | 9 | # For those using Git in their SVN 10 | # ignore files in the WPcom plugin directory 11 | wpcom-helper.php 12 | VERSION 13 | tests/php/tests 14 | todo.txt 15 | 16 | # Created by Apigen for logs 17 | /build 18 | 19 | /docs 20 | 21 | /report/ 22 | 23 | .svn 24 | 25 | composer.lock 26 | /vendor/ 27 | /node_modules 28 | .phpunit.result.cache 29 | -------------------------------------------------------------------------------- /.phpunit-watcher.yml: -------------------------------------------------------------------------------- 1 | watch: 2 | directories: 3 | - src 4 | - tests 5 | fileMask: '*.php' 6 | phpunit: 7 | timeout: 300 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | 6 | ## 1.4.3 7 | 8 | ### Changed 9 | 10 | * Register an asset to the WP dependency registry. (#66) 11 | * Available only if `QueryMonitor` is active. 12 | * Updated plugin header `description` to match the one in package and in the git repo. 13 | * Added support for `.phpunit-watcher.yml`. 14 | * Fix alignment for `package.json` and `composer.json` (Use spaces instead of tabs per `.editorconfig` rules). 15 | 16 | ## 1.4.2 17 | 18 | ### Fixed 19 | 20 | - Fixed issue with array of data being passed to the `$src` argument of `am_enqueue_script()`. 21 | 22 | ## 1.4.1 23 | 24 | ### Fixed 25 | 26 | - Ensure that `$version` can be null when passed to helper methods. 27 | 28 | ## 1.4.0 29 | 30 | ### Fixed 31 | 32 | - Added proper types for helper functions that support array in the first argument. 33 | - Fix: `Asset_Manager_Preload::set_asset_types()` should return an empty array if no valid arguments are passed. 34 | 35 | ### Changed 36 | 37 | * Migrates code into the `Alley\WP\Asset_Manager` namespace. Legacy classes such 38 | as `Asset_Manager_Scripts` and `Asset_Manager_Styles` are aliased to their new 39 | namespace for backward compatibility. Helper functions are left un-namespaced. 40 | * Adds a dependency on Composer autoloader. For submodules, you can track the 41 | `production-built` branch of the plugin or any tagged release (which will be 42 | built) to include the dependencies. 43 | 44 | ## 1.3.7 45 | 46 | * Adds support for async and defer using the 'strategy' argument added for wp_enqueue_script in WordPress 6.3. 47 | 48 | ## 1.3.6 49 | 50 | * Adds support for running the plugin on a Windows hosting environment (#57) 51 | 52 | ## 1.3.5 53 | 54 | * Removes default kses allowed SVG attributes 55 | 56 | ## 1.3.4 57 | 58 | * Fix PHP 8.2 deprecations. 59 | 60 | ## 1.3.3 61 | 62 | * Removes `visibility:hidden` SVG sprite style declaration, which breaks some SVG element references (#50) 63 | * Upgrades to `mantle-framework/testkit` v0.11 (#50) 64 | 65 | ## 1.3.2 66 | 67 | **Changed** 68 | 69 | * The SVG Sprite is no longer hidden with `display:none` and instead visually hidden and moved offscreen (#49) 70 | 71 | ## 1.3.1 72 | 73 | * Check for array key before using when preloading assets (#47). 74 | * Upgrades to `mantle-framework/testkit` v0.10.1 (#47). 75 | 76 | ## 1.3.0 77 | 78 | **Changed** 79 | 80 | * Use `am_view_asset_error` meta capability to determine whether to display error messages related to asset enqueuing. `am_view_asset_error` is mapped to `manage_options` by default. 81 | 82 | ## 1.2.0 83 | 84 | **Added** 85 | 86 | * `am_symbol_is_registered` for determining if a symbol is registered (#41) 87 | 88 | **Changed** 89 | 90 | * Filters kses allowed svg+use tags & attributes (#43) 91 | 92 | **Fixed** 93 | 94 | * `print_asset()` no longer fails for local files on WP VIP environments (#40) 95 | 96 | ## 1.1.2 97 | 98 | * Updates the requirements on `mantle-framework/testkit` to permit the latest version (#36) 99 | * Addresses PHP 8 compatibility issue with the global usage in `Asset_Manager::add_core_asset()` (#37) 100 | * Fixes a bug where `get_svg()` returns false for local files on WP VIP environments (#39) 101 | 102 | ## 1.1.1 103 | 104 | Adds support for registering SVG assets to be added to a template's sprite sheet, with methods for displaying those assets 105 | 106 | ## 1.0.0 107 | 108 | Stable release 🎊 109 | 110 | No large changes since [v0.1.3](https://github.com/alleyinteractive/wp-asset-manager/releases/tag/0.1.3) other than switching the unit tests over to Mantle Teskit 111 | 112 | ## 0.1.3 113 | 114 | **Added** 115 | 116 | * `am_inline_stylesheet` filter for inline stylesheets 117 | 118 | ## 0.1.2 119 | 120 | **Added** 121 | 122 | * GPL License 123 | * Caching for `am_asset_conditions` 124 | * Composer Autoloader 125 | * GitHub Actions CI 126 | 127 | ## 0.1.1 128 | 129 | **Added** 130 | 131 | * Supports `include_any` in the `condition` parameter for matching _any_ condition (#10, #19) 132 | * Adds `am_preload` for preloading assets of any supported type (#16) 133 | * Improves overall plugin documentation 134 | 135 | **Changed** 136 | 137 | * Uses the print media swap method for async-loaded styles, based on Filament Group's [The Simplest Way to Load CSS Asynchronously](https://www.filamentgroup.com/lab/load-css-simpler/) (#13) 138 | 139 | **Fixed** 140 | 141 | * Includes the `media` attribute only if `media` exists (#7) 142 | * Uses self-closing link tags (#7) 143 | * Ensures `in_footer` is set (#8) 144 | * Corrects an issue where `loadCSS` was being output for load_methods other than defer (3510e8c) 145 | 146 | **Removed** 147 | 148 | * `am_enqueue_style` with `load_method => preload` is no longer supported. For backward compatibility this configuration will patch in a call to `am_preload` and also `sync` enqueue the asset (127acbc) 149 | * The `loadCSS` preload polyfill is removed since it is [no longer supported](https://github.com/filamentgroup/loadCSS#changes-in-version-30-no-more-preload-polyfill) (5d820d9) 150 | 151 | ## 0.1.0 152 | 153 | Initial release. 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WordPress Asset Manager 2 | 3 | Asset Manager is a toolkit for managing front-end assets and more tightly controlling where, when, and how they're loaded. 4 | 5 | * [Using Asset Manager in your WordPress Project](#using-asset-manager-in-your-wordpress-project) 6 | * [Enqueue Functions](#enqueue-functions) 7 | * [am_enqueue_script](#am_enqueue_script) 8 | * [am_enqueue_style](#am_enqueue_style) 9 | * [Conditions](#conditions) 10 | * [Inline Assets](#inline-assets) 11 | * [Enqueue Options](#enqueue-options) 12 | * [Preload Function](#preload-function) 13 | * [Preload Options](#preload-options) 14 | * [SVG Sprite](#svg-sprite) 15 | * [Setup](#setup) 16 | * [Admin pages](#admin-pages) 17 | * [Registering Symbols](#registering-symbols) 18 | * [Verifying a symbol is registered](#verifying-a-symbol-is-registered) 19 | * [Changing the SVG Directory](#changing-the-svg-directory) 20 | * [Setting Global Attributes](#setting-global-attributes) 21 | * [Update `$sprite_allowed_tags`](#update-sprite_allowed_tags) 22 | * [Removing a Symbol](#removing-a-symbol) 23 | * [Replacing a Symbol](#replacing-a-symbol) 24 | * [Displaying a Symbol](#displaying-a-symbol) 25 | * [Notes on SVG sizing](#notes-on-svg-sizing) 26 | * [Getting a Symbol](#getting-a-symbol) 27 | * [Requirements](#requirements) 28 | * [Downloads and Versioning](#downloads-and-versioning) 29 | * [Contributing to Development](#contributing-to-development) 30 | 31 | ## Using Asset Manager in your WordPress Project 32 | 33 | To get started, simply [download](#downloads-and-versioning) and install this plugin into your plugins directory and activate it on the plugins screen. 34 | 35 | ## Enqueue Functions 36 | 37 | The `am_enqueue_*` functions will enqueue an asset with additional attributes based upon its `load_method` value. Options can be passed in as an array or individual parameters. 38 | 39 | ### `am_enqueue_script` 40 | 41 | 42 | 43 | ```php 44 | // Enqueue a JavaScript asset. 45 | am_enqueue_script( 46 | [ 47 | 'handle' => 'footer-script', 48 | 'src' => 'js/script.js', 49 | 'deps' => [], 50 | 'condition' => 'global', 51 | 'load_method' => 'sync', // 'sync', 'inline', 'async', 'defer', 'async-defer' 52 | 'version' => '1.0.0', 53 | 'load_hook' => 'wp_footer', 54 | ] 55 | ); 56 | ``` 57 | 58 | Use `am_modify_load_method` to modify the load method of an already-enqueued script. 59 | 60 | ```php 61 | // Defer an enqueued JavaScript asset. 62 | am_modify_load_method( 63 | [ 64 | 'handle' => 'footer-script', 65 | 'load_method' => 'defer', 66 | ] 67 | ); 68 | ``` 69 | 70 | ### `am_enqueue_style` 71 | 72 | ```php 73 | // Load a CSS asset asynchronously. 74 | am_enqueue_style( 75 | [ 76 | 'handle' => 'site-styles', 77 | 'src' => 'css/styles.css', 78 | 'deps' => [], 79 | 'condition' => 'global', 80 | 'load_method' => 'async', // 'sync', 'inline', 'async', 'defer', 'preload' 81 | 'version' => '1.0.0', 82 | 'load_hook' => 'wp_head', 83 | 'media' => 'all', // 'print', 'screen', or any valid media query 84 | ] 85 | ); 86 | ``` 87 | 88 | ### Conditions 89 | 90 | The `condition` parameter determines under which condition(s) the asset should load. 91 | 92 | **`include`** 93 | 94 | > `string|array` 95 | > 96 | > Requires that all conditions be truthy in order for the asset to load. 97 | 98 | The `include` property is implied if the `condition` parameter is a string or array of strings; otherwise the `condition` parameter must contain the `include` property. 99 | 100 | **`include_any`** 101 | 102 | > `string|array` 103 | > 104 | > Allows for _any_ condition to be truthy, instead of requiring that all conditions be. 105 | 106 | **`exclude`** 107 | 108 | > `string|array` 109 | > 110 | > Requires that all conditions be falsey in order for the asset to load. This is skipped if neither `include` nor `include_any` are truthy. 111 | 112 | #### Custom Conditions 113 | 114 | There are a few default conditions included out-of-the-box: 115 | 116 | | Name | Condition | 117 | |:-----------|---------------| 118 | | `'global'` | `true` | 119 | | `'single'` | `is_single()` | 120 | | `'search'` | `is_search()` | 121 | 122 | Use the `am_asset_conditions` filter to add or replace conditions. 123 | 124 | ```php 125 | function asset_conditions( $conditions ) { 126 | return array_merge( 127 | $conditions, 128 | [ 129 | 'home' => ( is_home() || is_front_page() ), 130 | 'archive' => is_archive(), 131 | 'page' => is_page(), 132 | ] 133 | ); 134 | } 135 | 136 | add_filter( 'am_asset_conditions', 'asset_conditions', 10 ); 137 | ``` 138 | 139 | ### Inline Assets 140 | 141 | Use `load_method => inline` with an absolute `src` path for either enqueue function to print the contents of the file to the document head. 142 | 143 | **Print the contents of a file** 144 | 145 | ```php 146 | // Print the contents of this CSS asset into a ', 60 | esc_attr( implode( ' ', $classes ) ), 61 | /** 62 | * Filter the inline stylesheet. 63 | * 64 | * @param string $contents Contents to filter. 65 | * @param array $stylesheet Stylesheet being rendered. 66 | */ 67 | apply_filters( 'am_inline_stylesheet', file_get_contents( $stylesheet['src'] ), $stylesheet ), // phpcs:ignore 68 | ); 69 | } else { 70 | $this->generate_asset_error( 'unsafe_inline', $stylesheet ); 71 | } 72 | } elseif ( 'async' === $stylesheet['load_method'] || 'defer' === $stylesheet['load_method'] ) { 73 | $media = false; 74 | $src = $stylesheet['src']; 75 | 76 | if ( ! empty( $stylesheet['version'] ) ) { 77 | $src = add_query_arg( 'ver', $stylesheet['version'], $stylesheet['src'] ); 78 | } 79 | 80 | if ( ! empty( $stylesheet['media'] ) ) { 81 | $media = $stylesheet['media']; 82 | } 83 | 84 | if ( 'async' === $stylesheet['load_method'] ) { 85 | $onload_media = empty( $media ) ? 'all' : $media; 86 | $print_string = ''; 87 | } elseif ( 'defer' === $stylesheet['load_method'] ) { 88 | $print_string = ''; 89 | } 90 | 91 | echo wp_kses( 92 | sprintf( 93 | $print_string, 94 | esc_url( $src ), 95 | esc_attr( implode( ' ', $classes ) ), 96 | ! empty( $media ) ? sprintf( 'media="%s" ', esc_attr( $media ) ) : '' 97 | ), 98 | [ 99 | 'link' => [ 100 | 'rel' => [], 101 | 'href' => [], 102 | 'class' => [], 103 | 'media' => [], 104 | 'as' => [], 105 | 'onload' => [], 106 | ], 107 | 'script' => [ 108 | 'class' => [], 109 | 'type' => [], 110 | ], 111 | 'noscript' => [], 112 | ] 113 | ); 114 | } 115 | } 116 | } 117 | 118 | /** 119 | * Add loadCSS if necessary. 120 | * 121 | * @param array $stylesheet Stylesheet to check. 122 | * @return array 123 | */ 124 | public function pre_add_asset( $stylesheet ) { 125 | // Add loadCSS for defer method. 126 | if ( 'defer' === $stylesheet['load_method'] && ! $this->loadcss_added ) { 127 | am_enqueue_script( 128 | [ 129 | 'handle' => 'loadCSS', 130 | 'src' => AM_BASE_DIR . '/js/loadCSS.min.js', 131 | 'load_method' => 'inline', 132 | 'load_hook' => 'am_critical', 133 | ] 134 | ); 135 | $this->loadcss_added = true; 136 | } 137 | 138 | return $stylesheet; 139 | } 140 | 141 | /** 142 | * Perform mutations to stylesheet after validation. 143 | * 144 | * @param array $stylesheet Stylesheet to mutate. 145 | * @return array 146 | */ 147 | public function post_validate_asset( $stylesheet ) { 148 | if ( 149 | ! empty( $stylesheet['dependents'] ) && 150 | ( 'async' === $stylesheet['load_method'] || 'defer' === $stylesheet['load_method'] ) 151 | ) { 152 | $this->generate_asset_error( 'unsafe_load_method', $stylesheet, $this->assets_by_handle[ $stylesheet['dependents'][0] ] ); 153 | } 154 | 155 | return $stylesheet; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/class-svg-sprite.php: -------------------------------------------------------------------------------- 1 | and elements. 65 | * 66 | * @var array 67 | */ 68 | public array $kses_svg_allowed_tags = [ 69 | 'svg' => [], 70 | 'use' => [ 71 | 'href' => true, 72 | ], 73 | ]; 74 | 75 | /** 76 | * Constructor. 77 | */ 78 | protected function __construct() { 79 | /** 80 | * Ensures the sprite's `style` attribute isn't escaped. 81 | * 82 | * @param string[] $styles Array of allowed CSS properties. 83 | * @return string[] Modified safe inline style properties. 84 | */ 85 | add_filter( 86 | 'safe_style_css', 87 | fn ( $styles ) => [ 88 | ...array_values( $styles ), 89 | [ 'left', 'overflow', 'position' ], 90 | ], 91 | ); 92 | 93 | add_filter( 'wp_kses_allowed_html', [ $this, 'extend_kses_post_with_use_svg' ] ); 94 | 95 | $this->create_sprite_sheet(); 96 | } 97 | 98 | /** 99 | * Get the SVG directory. 100 | */ 101 | public function get_svg_directory() { 102 | if ( ! isset( static::$_svg_directory ) ) { 103 | /** 104 | * Filter function for updating the directory upon which a symbol's relative 105 | * path will be based. 106 | * 107 | * @since 1.1.0 108 | * 109 | * @param string $path The absolute root for relative SVG paths. 110 | */ 111 | static::$_svg_directory = apply_filters( 'am_modify_svg_directory', get_stylesheet_directory() ); 112 | } 113 | 114 | return static::$_svg_directory; 115 | } 116 | 117 | /** 118 | * Get the SVG directory. 119 | */ 120 | public function get_global_attributes() { 121 | if ( ! isset( static::$_global_attributes ) ) { 122 | static::$_global_attributes = []; 123 | } 124 | 125 | /** 126 | * Filter function for configuring attributes to be added to all SVG symbols. 127 | * 128 | * @since 1.1.0 129 | * 130 | * @param array $attributes { 131 | * A list of attributes to be added to all SVG symbols. 132 | * 133 | * @type array The key represents an HTML attribute. 134 | * The value represents attribute's value. 135 | * } 136 | */ 137 | return apply_filters( 'am_global_svg_attributes', static::$_global_attributes ); 138 | } 139 | 140 | /** 141 | * Creates the sprite sheet. 142 | */ 143 | public function create_sprite_sheet() { 144 | $this->sprite_document = new DOMDocument(); 145 | 146 | $this->svg_root = $this->sprite_document->createElementNS( 'http://www.w3.org/2000/svg', 'svg' ); 147 | 148 | $this->svg_root->setAttribute( 'style', 'left:-9999px;overflow:hidden;position:absolute' ); 149 | 150 | $this->svg_root->setAttribute( 'focusable', 'false' ); 151 | $this->svg_root->setAttribute( 'height', '0' ); 152 | $this->svg_root->setAttribute( 'role', 'none' ); 153 | $this->svg_root->setAttribute( 'viewBox', '0 0 0 0' ); 154 | $this->svg_root->setAttribute( 'width', '0' ); 155 | 156 | $this->sprite_document->appendChild( $this->svg_root ); 157 | 158 | add_action( 'wp_body_open', [ $this, 'print_sprite_sheet' ], 10 ); 159 | } 160 | 161 | /** 162 | * Prints the sprite sheet to the page at `wp_body_open`. 163 | */ 164 | public function print_sprite_sheet() { 165 | // Allowed tags and attributes for SVG. 166 | include __DIR__ . '/kses-svg.php'; 167 | 168 | /** 169 | * Filter function for patching in missing attributes and elements for escaping with `wp_kses`. 170 | * 171 | * @since 1.1.0 172 | * 173 | * @param array $am_svg_allowed_tags wp_kses allowed SVG for the sprite sheet. 174 | */ 175 | $kses_sprite_allowed_tags = apply_filters( 'am_sprite_allowed_tags', $am_kses_svg ?? [] ); 176 | 177 | echo wp_kses( 178 | $this->sprite_document->C14N(), 179 | $kses_sprite_allowed_tags 180 | ); 181 | } 182 | 183 | /** 184 | * Convenience function that returns the symbol id based on the asset handle. 185 | * 186 | * @param string $handle The asset handle. 187 | * @return string The asset handle formatted for use as the symbol id. 188 | */ 189 | public function format_handle_as_symbol_id( $handle ) { 190 | return empty( $handle ) 191 | ? '' 192 | : "am-symbol-{$handle}"; 193 | } 194 | 195 | 196 | /** 197 | * Evaluates and returns the filepath for a given file. 198 | * 199 | * @param string $path The relative or absolute path to the SVG file. 200 | * @return string The absolute filepath. 201 | */ 202 | public function get_the_normalized_filepath( $path ) { 203 | if ( empty( $path ) ) { 204 | return ''; 205 | } 206 | 207 | // Build the file path, validating absolute or relative path. 208 | return ( DIRECTORY_SEPARATOR === $path[0] ) 209 | ? $path 210 | : rtrim( $this->get_svg_directory(), DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR . $path; 211 | } 212 | 213 | /** 214 | * Update allowed SVG. 215 | * 216 | * @param array $attributes Asset attributes. 217 | */ 218 | public function update_svg_allowed_tags( $attributes ) { 219 | foreach ( array_keys( $attributes ) as $attr ) { 220 | $this->kses_svg_allowed_tags['svg'][ $attr ] = true; 221 | } 222 | } 223 | 224 | /** 225 | * Returns a formatted integer value. 226 | * 227 | * @param int|float $value The value to format. 228 | * @param int $precision The formatting precision. 229 | * @return int|float The value with the proper precision. 230 | */ 231 | public function format_precision( $value, $precision = 2 ) { 232 | $pow = pow( 10, $precision ); 233 | return ( intval( $value * $pow ) / $pow ); 234 | } 235 | 236 | /** 237 | * Returns the contents of an SVG file. 238 | * 239 | * @param string $path The SVG file path. 240 | * @return DOMDocument The SVG file contents. 241 | */ 242 | public function get_svg( $path ) { 243 | if ( empty( $path ) ) { 244 | return ''; 245 | } 246 | 247 | if ( am_validate_path( $path ) ) { 248 | $file_contents = file_get_contents( $path ); // phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown 249 | 250 | if ( ! empty( $file_contents ) ) { 251 | $doc = new DOMDocument(); 252 | $doc->loadXML( $file_contents ); 253 | $svg = $doc->getElementsByTagName( 'svg' ); 254 | 255 | if ( ! empty( $svg->item( 0 ) ) ) { 256 | return $svg->item( 0 ); 257 | } 258 | } 259 | } 260 | 261 | return false; 262 | } 263 | 264 | /** 265 | * Determine an asset's default dimensions. 266 | * 267 | * @param DOMDocument $svg The SVG contents. 268 | * @param array $asset The asset definition. 269 | * @return array The height and width to use for the asset. 270 | */ 271 | public function get_default_dimensions( $svg, $asset ) { 272 | $attributes = $asset['attributes'] ?? []; 273 | 274 | // Default to the height and width attributes from the asset definition. 275 | if ( ! empty( $attributes['height'] ) && ! empty( $attributes['width'] ) ) { 276 | return [ 277 | 'width' => (int) $attributes['width'], 278 | 'height' => (int) $attributes['height'], 279 | ]; 280 | } 281 | 282 | // Fall back to attribute values if we have both. 283 | $width_attr = (int) $svg->getAttribute( 'width' ) ?? 0; 284 | $height_attr = (int) $svg->getAttribute( 'height' ) ?? 0; 285 | 286 | if ( ! empty( $width_attr ) && ! empty( $height_attr ) ) { 287 | return [ 288 | 'width' => $width_attr, 289 | 'height' => $height_attr, 290 | ]; 291 | } 292 | 293 | // Use the viewBox attribute values if neither of the above are present. 294 | $viewbox = $svg->getAttribute( 'viewBox' ) ?? ''; 295 | 296 | if ( ! empty( $viewbox ) ) { 297 | // 0. min-x, 1. min-y, 2. width, 3. height. 298 | $viewbox_attr = explode( ' ', $viewbox ); 299 | 300 | if ( ! empty( $viewbox_attr[2] ) && ! empty( $viewbox_attr[3] ) ) { 301 | return [ 302 | 'width' => (int) $viewbox_attr[2], 303 | 'height' => (int) $viewbox_attr[3], 304 | ]; 305 | } 306 | } 307 | 308 | // We tried... 309 | return [ 310 | 'width' => 0, 311 | 'height' => 0, 312 | ]; 313 | } 314 | 315 | /** 316 | * Perform final mutations before adding an asset to sprite. 317 | * 318 | * @param array $asset Asset to mutate. 319 | * @return array The modified asset definition. 320 | */ 321 | public function pre_add_asset( $asset ) { 322 | $src = $this->get_the_normalized_filepath( $asset['src'] ); 323 | 324 | return ( empty( $src ) ) 325 | ? $asset 326 | : array_merge( $asset, [ 'src' => $src ] ); 327 | } 328 | 329 | /** 330 | * Create the element based on a given asset. 331 | * 332 | * @todo Simplify this so we don't have to pass the asset around. 333 | * 334 | * @param array $asset The asset definition. 335 | * @return array The modified asset and the symbol element. 336 | */ 337 | public function create_symbol( $asset ) { 338 | $asset = $this->pre_add_asset( $asset ); 339 | 340 | // Get the SVG file contents. 341 | $svg = $this->get_svg( $asset['src'] ?? '' ); 342 | 343 | if ( ! ( $svg instanceof DOMElement ) ) { 344 | return; 345 | } 346 | 347 | /* 348 | * Try to determine a default size for the SVG. 349 | * These dimensions are used to create a ratio for setting the symbol 350 | * size when only one dimension is passed via `am_use_symbol()` 351 | */ 352 | $default_dimensions = $this->get_default_dimensions( $svg, $asset ); 353 | 354 | if ( ! empty( $default_dimensions['width'] ) && ! empty( $default_dimensions['height'] ) ) { 355 | $asset['attributes'] = array_merge( $asset['attributes'] ?? [], $default_dimensions ); 356 | } 357 | 358 | // Create the element. 359 | $symbol = $this->sprite_document->createElement( 'symbol' ); 360 | 361 | // Add the id attribute. 362 | $symbol->setAttribute( 'id', $this->format_handle_as_symbol_id( $asset['handle'] ) ); 363 | 364 | // DOMDocument::getElementById will only work if we set this attribute as the ID. 365 | $symbol->setIdAttribute( 'id', true ); 366 | 367 | // Use the viewBox attribute from the SVG asset. 368 | $viewbox = $svg->getAttribute( 'viewBox' ) ?? ''; 369 | 370 | if ( ! empty( $viewbox ) ) { 371 | $symbol->setAttribute( 'viewBox', $viewbox ); 372 | } 373 | 374 | // Add the SVG's childNodes to the symbol. 375 | foreach ( iterator_to_array( $svg->childNodes ) as $child_node ) { 376 | // Exclude text nodes. 377 | if ( ! ( $child_node instanceof DOMText ) ) { 378 | $symbol->appendChild( $this->sprite_document->importNode( $child_node, true ) ); 379 | } 380 | } 381 | 382 | // Return the asset too, as it may have been modified. 383 | return [ $asset, $symbol ]; 384 | } 385 | 386 | /** 387 | * Adds an asset to the sprite sheet. 388 | * 389 | * @param array $asset An asset definition. 390 | * @return void 391 | */ 392 | public function add_asset( $asset ): void { 393 | if ( ! $this->asset_should_add( $asset ) ) { 394 | return; 395 | } 396 | 397 | [ $asset, $symbol ] = $this->create_symbol( $asset ); 398 | 399 | if ( ! ( $symbol instanceof DOMElement ) ) { 400 | return; 401 | } 402 | 403 | // Append the symbol to the SVG sprite. 404 | $this->svg_root->appendChild( $symbol ); 405 | 406 | $this->asset_handles[] = $asset['handle']; 407 | $this->sprite_map[ $asset['handle'] ] = $asset; 408 | } 409 | 410 | /** 411 | * Remove a registered symbol. 412 | * 413 | * @param array $handle The symbol handle. 414 | * @return bool Whether the symbol was removed, or wasn't registered. 415 | */ 416 | public function remove_symbol( $handle ): bool { 417 | if ( ! in_array( $handle, $this->asset_handles ) ) { 418 | // Success: Handle not previously registered. 419 | return true; 420 | } 421 | 422 | // Remove the registered asset handle. 423 | $idx = array_search( $handle, $this->asset_handles, true ); 424 | unset( $this->asset_handles[ $idx ] ); 425 | 426 | // Remove the entry in the sprite_map. 427 | unset( $this->sprite_map[ $handle ] ); 428 | 429 | // Get the registered symbol from the sprite sheet. 430 | $existing_symbol = $this->sprite_document->getElementById( 431 | $this->format_handle_as_symbol_id( $handle ) 432 | ); 433 | 434 | if ( ! ( $existing_symbol instanceof DOMElement ) ) { 435 | // Success: There's nothing to remove. 436 | return true; 437 | } 438 | 439 | // Remove the symbol. 440 | $symbol_was_removed = $existing_symbol->parentNode->removeChild( $existing_symbol ); 441 | 442 | // `removeChild` returns the old child on success. 443 | return ! empty( $symbol_was_removed ); 444 | } 445 | 446 | /** 447 | * Filter allowed HTML to allow svg & use tags and attributes. 448 | * 449 | * @param array $allowed Allowed tags, attributes, and/or entities. 450 | * @return array filtered tags. 451 | */ 452 | public function extend_kses_post_with_use_svg( $allowed ) { 453 | $use_svg_tags = $this->kses_svg_allowed_tags; 454 | return array_merge_recursive( $allowed, $use_svg_tags ); 455 | } 456 | 457 | /** 458 | * Returns the SVG markup for displaying a symbol. 459 | * 460 | * @param string $handle The symbol handle. 461 | * @param array $attrs Additional attributes to add to the element. 462 | * @return string The and elements for displaying a symbol. 463 | */ 464 | public function get_symbol( $handle, $attrs = [] ) { 465 | if ( empty( $handle ) || ! in_array( $handle, array_keys( $this->sprite_map ), true ) ) { 466 | return ''; 467 | } 468 | 469 | $asset = $this->sprite_map[ $handle ]; 470 | 471 | if ( empty( $asset ) ) { 472 | return ''; 473 | } 474 | 475 | /* 476 | * Use the dimensions from `get_default_dimensions()` to calculate the 477 | * expected size when only one dimension is provided in $attrs. 478 | */ 479 | if ( ! empty( $asset['attributes']['width'] ) && ! empty( $asset['attributes']['height'] ) ) { 480 | $use_ratio_for_width = ( empty( $attrs['width'] ) && ! empty( $attrs['height'] ) ); 481 | $use_ratio_for_height = ( empty( $attrs['height'] ) && ! empty( $attrs['width'] ) ); 482 | 483 | $ratio = ( $asset['attributes']['width'] / $asset['attributes']['height'] ); 484 | 485 | if ( $use_ratio_for_width ) { 486 | // width from height: ratio * height. 487 | $attrs['width'] = $this->format_precision( $ratio * $attrs['height'] ); 488 | } elseif ( $use_ratio_for_height ) { 489 | // height from width: width / ratio. 490 | $attrs['height'] = $this->format_precision( $attrs['width'] / $ratio ); 491 | } 492 | } 493 | 494 | // Merge attributes. 495 | $local_attrs = array_merge( 496 | $this->get_global_attributes(), 497 | $asset['attributes'] ?? [], 498 | $attrs 499 | ); 500 | $local_attrs = array_map( 'esc_attr', $local_attrs ); 501 | 502 | // Ensure attributes are in allowed_html. 503 | $this->update_svg_allowed_tags( $local_attrs ); 504 | 505 | // Build a string of all attributes. 506 | $attrs = ''; 507 | foreach ( $local_attrs as $name => $value ) { 508 | if ( empty( $value ) ) { 509 | continue; 510 | } 511 | $attrs .= " $name=" . '"' . $value . '"'; 512 | } 513 | 514 | return sprintf( 515 | '', 516 | trim( $attrs ), 517 | esc_attr( $this->format_handle_as_symbol_id( $handle ) ) 518 | ); 519 | } 520 | 521 | /** 522 | * Print a symbol's SVG markup. 523 | * 524 | * @param string $handle The asset handle. 525 | * @param array $attrs Additional HTML attributes to add to the SVG markup. 526 | */ 527 | public function use_symbol( $handle, $attrs = [] ) { 528 | $symbol_markup = $this->get_symbol( $handle, $attrs ); 529 | 530 | if ( ! empty( $symbol_markup ) ) { 531 | echo wp_kses_post( $symbol_markup ); 532 | } 533 | } 534 | } 535 | -------------------------------------------------------------------------------- /src/concerns/trait-asset-error.php: -------------------------------------------------------------------------------- 1 | %1$s and %2$s require each other as dependencies.', 'am' ), $asset['handle'], $info ); 28 | break; 29 | 30 | case 'invalid_load_hook': 31 | $message = sprintf( __( 'Asset %1$s is using an invalid load_hook. The asset is configured to load on hook %2$s, but this hook does not exist.', 'am' ), $asset['handle'], $asset['load_hook'] ); 32 | break; 33 | 34 | case 'unsafe_load_hook': 35 | $message = sprintf( __( 'Asset %1$s, configured to load on hook %2$s, is loading after an asset that depends on it: %3$s, configured to load on hook %4$s', 'am' ), $asset['handle'], $asset['load_hook'], $info['handle'], $info['load_hook'] ); 36 | break; 37 | 38 | case 'missing': 39 | $message = sprintf( __( 'A dependency you listed for this asset is invalid. %1$s lists %2$s as a dependency, but that asset is not configured to load on this page.', 'am' ), $asset['handle'], $info ); 40 | break; 41 | 42 | case 'cannot_print': 43 | $message = sprintf( __( 'Asset of type %1$s does not exist or does not have a print_asset() function configured.', 'am' ), $asset['type'] ); 44 | break; 45 | 46 | case 'invalid_enqueue_function': 47 | $message = sprintf( __( 'You attempted to enqueue an asset with function %1$s, which does not exist.', 'am' ), $info ); 48 | break; 49 | 50 | case 'unsafe_load_method': 51 | $message = sprintf( __( 'Asset %1$s uses the %2$s load method, meaning there is no guarantee it will be available for its dependent asset %3$s, using %4$s load method.', 'am' ), $asset['handle'], $asset['load_method'], $info['handle'], $info['load_method'] ); 52 | break; 53 | 54 | case 'unsafe_inline': 55 | $message = sprintf( __( 'You attempted to load %1$s using the "inline" load method, but it is an external asset or the asset does not exist.', 'am' ), $asset['src'] ); 56 | break; 57 | 58 | case 'invalid_preload_as_attribute': 59 | $message = sprintf( __( 'You attempted to preload %1$s with a missing or invalid as attribute. The `as` attribute helps the browser prioritize and accept the preloaded asset.', 'am' ), $asset['src'] ); 60 | break; 61 | 62 | default: 63 | $message = sprintf( __( 'Something went wrong when enqueueing %s.', 'am' ), $asset['handle'] ); 64 | break; 65 | } 66 | // phpcs:enable 67 | 68 | $this->format_error( new WP_Error( $code, $message, $asset ) ); 69 | } 70 | 71 | /** 72 | * Display an error to the user 73 | * 74 | * @param WP_Error $error Error to display to user. 75 | */ 76 | public function format_error( $error ) { 77 | if ( current_user_can( 'am_view_asset_error', $error ) ) { 78 | $code = $error->get_error_code(); 79 | echo wp_kses( 80 | // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r 81 | '
ENQUEUE ERROR: ' . $code . ' - ' . $error->get_error_message( $code ) . ' Bad asset:
' . print_r( $error->get_error_data( $code ), true ) . '
', 82 | [ 83 | 'div' => [ 'class' ], 84 | 'strong' => [], 85 | 'em' => [], 86 | 'pre' => [], 87 | ] 88 | ); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/concerns/trait-conditions.php: -------------------------------------------------------------------------------- 1 | true, 42 | 'single' => is_single(), 43 | 'search' => is_search(), 44 | ] 45 | ); 46 | } 47 | 48 | return static::$_conditions; 49 | } 50 | 51 | /** 52 | * Determine if an asset should be added (enqueued) or not. 53 | * 54 | * @param string $asset Type of asset. 55 | * @return bool|WP_Error 56 | */ 57 | public function asset_should_add( $asset ) { 58 | /** 59 | * Filter function for preventing an asset from loading, regardless of conditions 60 | * 61 | * @since 0.0.1 62 | * 63 | * @param bool $add_asset Whether or not to forcefully prevent asset from loading 64 | * @param array $asset Asset to prevent from loading 65 | */ 66 | if ( ! apply_filters( 'am_asset_should_add', true, $asset ) ) { 67 | return false; 68 | } 69 | 70 | // Already-added assets should not be added again. 71 | if ( empty( $asset['handle'] ) || in_array( $asset['handle'], $this->asset_handles, true ) ) { 72 | return false; 73 | } 74 | 75 | // If there's no condition, asset should load. 76 | if ( empty( $asset['condition'] ) ) { 77 | return true; 78 | } 79 | 80 | $conditions = static::get_conditions(); 81 | $condition_result = true; 82 | 83 | // Default functionality of condition is 'include'. 84 | if ( ! empty( $asset['condition']['include'] ) ) { 85 | $condition_include = $asset['condition']['include']; 86 | } elseif ( ! empty( $asset['condition']['include_any'] ) ) { 87 | $condition_include_any = $asset['condition']['include_any']; 88 | } elseif ( empty( $asset['condition']['exclude'] ) ) { 89 | $condition_include = $asset['condition']; 90 | } 91 | 92 | // Check 'include' conditions (all must be true for asset to load) 93 | // There might only be an 'exclude' condition, so check empty() first. 94 | if ( ! empty( $condition_include ) ) { 95 | $condition_include = ! is_array( $condition_include ) ? [ $condition_include ] : $condition_include; 96 | 97 | foreach ( $condition_include as $condition_true ) { 98 | if ( ! empty( $conditions[ $condition_true ] ) ) { 99 | continue; 100 | } else { 101 | $condition_result = false; 102 | break; 103 | } 104 | } 105 | } 106 | 107 | // Check for 'include_any' to allow for matching of _any_ condition instead of all conditions. 108 | if ( ! empty( $condition_include_any ) ) { 109 | $condition_result = false; 110 | $condition_include_any = ! is_array( $condition_include_any ) ? [ $condition_include_any ] : $condition_include_any; 111 | 112 | foreach ( $condition_include_any as $condition_true ) { 113 | if ( $conditions[ $condition_true ] ) { 114 | $condition_result = true; 115 | } 116 | } 117 | } 118 | 119 | // Check 'exclude' conditions (all must be false for asset to load) 120 | // Verify $condition_result is true. If it's already false, we don't need to check excludes. 121 | if ( ! empty( $asset['condition']['exclude'] ) && $condition_result ) { 122 | $condition_exclude = ! is_array( $asset['condition']['exclude'] ) ? [ $asset['condition']['exclude'] ] : $asset['condition']['exclude']; 123 | 124 | foreach ( $condition_exclude as $condition_false ) { 125 | if ( ! $conditions[ $condition_false ] ) { 126 | continue; 127 | } else { 128 | $condition_result = false; 129 | break; 130 | } 131 | } 132 | } 133 | 134 | return $condition_result; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/concerns/trait-singleton.php: -------------------------------------------------------------------------------- 1 | |string|null $src URI to script or array of inline script data. 37 | * @param array $deps This script's dependencies. 38 | * @param array|string $condition Corresponds to a configured loading condition that, if matches, 39 | * will allow the script to load. 40 | * 'global' is assumed if no condition is declared. 41 | * @param string $load_method How to load this asset. 42 | * @param string|null $version Version of the script. 43 | * @param string $load_hook Hook on which to load this asset. 44 | * 45 | * @phpstan-param string|array{ 46 | * handle: string, 47 | * src?: array|string, 48 | * condition?: string, 49 | * deps?: array, 50 | * load_hook?: string, 51 | * load_method?: string, 52 | * version?: string 53 | * } $handle 54 | */ 55 | function am_enqueue_script( array|string $handle, array|string|null $src = null, array $deps = [], array|string $condition = 'global', string $load_method = 'sync', ?string $version = '1.0.0', string $load_hook = 'wp_head' ): void { 56 | $defaults = compact( 'handle', 'src', 'deps', 'condition', 'load_method', 'version', 'load_hook' ); 57 | $args = is_array( $handle ) ? array_merge( $defaults, $handle ) : $defaults; 58 | Scripts::instance()->add_asset( $args ); 59 | } 60 | 61 | endif; 62 | 63 | if ( ! function_exists( 'am_modify_load_method' ) ) : 64 | 65 | /** 66 | * Modify the load method of an already-enqueued script 67 | * 68 | * @param string $handle Handle for script. 69 | * @param string $load_method How to load this asset. 70 | */ 71 | function am_modify_load_method( string $handle, string $load_method = 'sync' ): void { 72 | Scripts::instance()->modify_load_method( $handle, $load_method ); 73 | } 74 | 75 | endif; 76 | 77 | if ( ! function_exists( 'am_enqueue_style' ) ) : 78 | 79 | /** 80 | * Load an external stylesheet. Options can be passed in as an array or individual parameters. 81 | * 82 | * @param string|array $handle Handle for stylesheet. This is necessary for dependency management. 83 | * @param string $src URI to stylesheet. 84 | * @param array $deps List of dependencies. 85 | * @param array|string $condition Corresponds to a configured loading condition that, if matches, 86 | * will allow the stylesheet to load. 87 | * 'global' is assumed if no condition is declared. 88 | * @param string $load_method How to load this asset. 89 | * @param string|null $version Version of the script. 90 | * @param string $load_hook Hook on which to load this asset. 91 | * @param string $media Media query to restrict when this asset is loaded. 92 | * 93 | * @phpstan-param string|array{ 94 | * handle: string, 95 | * src?: string, 96 | * deps?: array, 97 | * condition?: array|string, 98 | * load_method?: string, 99 | * version?: string, 100 | * load_hook?: string, 101 | * media?: string 102 | * } $handle 103 | */ 104 | function am_enqueue_style( array|string $handle, ?string $src = null, array $deps = [], array|string $condition = 'global', string $load_method = 'sync', ?string $version = '1.0.0', string $load_hook = 'wp_head', ?string $media = null ): void { 105 | $defaults = compact( 'handle', 'src', 'deps', 'condition', 'load_method', 'version', 'load_hook', 'media' ); 106 | $args = is_array( $handle ) ? array_merge( $defaults, $handle ) : $defaults; 107 | 108 | /** 109 | * Using am_enqueue_style with `load_method => preload` is no longer supported. 110 | * This patches in a call to am_preload and updates the enqueued style's 111 | * load_method to 'sync', which replicates the deprecated behavior. 112 | */ 113 | if ( 'preload' === $args['load_method'] ) { 114 | Preload::instance()->add_asset( $args ); 115 | $args['load_method'] = 'sync'; 116 | } 117 | 118 | Styles::instance()->add_asset( $args ); 119 | } 120 | 121 | endif; 122 | 123 | if ( ! function_exists( 'am_preload' ) ) : 124 | 125 | /** 126 | * Provide an asset with a `preload` resource hint for the browser to prioritize. 127 | * 128 | * @param array|string $handle Handle for asset. This is necessary for dependency management. 129 | * @param string $src URI to asset. 130 | * @param array|string $condition Corresponds to a configured loading condition that, if matches, 131 | * will allow the asset to load. 132 | * 'global' is assumed if no condition is declared. 133 | * @param string|null $version Version of the asset. 134 | * @param string $media Media query to restrict when this asset is loaded. 135 | * @param string $as A hint to the browser about what type of asset this is. 136 | * See $preload_as for valid options. 137 | * @param boolean $crossorigin Preload this asset cross-origin. 138 | * @param string $mime_type The MIME type for the preloaded asset. 139 | * 140 | * @phpstan-param string|array{ 141 | * handle: string, 142 | * src?: string, 143 | * condition?: array|string, 144 | * version?: string, 145 | * media?: string, 146 | * as?: string, 147 | * crossorigin?: bool, 148 | * mime_type?: string 149 | * } $handle Handle for asset. This is necessary for dependency management. 150 | */ 151 | function am_preload( array|string $handle, ?string $src = null, array|string $condition = 'global', ?string $version = '1.0.0', string $media = 'all', ?string $as = null, bool $crossorigin = false, ?string $mime_type = null ): void { 152 | $defaults = compact( 'handle', 'src', 'condition', 'version', 'media', 'as', 'crossorigin', 'mime_type' ); 153 | $args = is_array( $handle ) ? array_merge( $defaults, $handle ) : $defaults; 154 | Preload::instance()->add_asset( $args ); 155 | } 156 | 157 | endif; 158 | 159 | if ( ! function_exists( 'am_register_symbol' ) ) : 160 | 161 | /** 162 | * Define a symbol to be added to the SVG sprite. 163 | * 164 | * @param string|array $handle Handle for asset, used to refer to the symbol in `am_use_symbol`. 165 | * @param string $src Absolute path from the current theme root, or a relative path 166 | * based on the current theme root. Use the `am_modify_svg_directory` 167 | * filter to update the directory from which relative paths will be 168 | * completed. 169 | * @param array|string $condition Corresponds to a configured loading condition that, if matches, 170 | * will allow the asset to be added to the sprite sheet. 171 | * 'global' is assumed if no condition is declared. 172 | * @param array $attributes An array of attribute names and values to add to the resulting 173 | * everywhere it is printed. 174 | * 175 | * @phpstan-param string|array{ 176 | * handle: string, 177 | * src?: string, 178 | * condition?: array|string, 179 | * attributes?: array 180 | * } $handle 181 | */ 182 | function am_register_symbol( array|string $handle, ?string $src = null, array|string $condition = 'global', array $attributes = [] ): void { 183 | $defaults = compact( 'handle', 'src', 'condition', 'attributes' ); 184 | $args = is_array( $handle ) ? array_merge( $defaults, $handle ) : $defaults; 185 | SVG_Sprite::instance()->add_asset( $args ); 186 | } 187 | 188 | endif; 189 | 190 | if ( ! function_exists( 'am_deregister_symbol' ) ) : 191 | 192 | /** 193 | * Remove a previously-registered symbol. 194 | * 195 | * @param string $handle Handle for the asset to be removed. 196 | */ 197 | function am_deregister_symbol( string $handle ): bool { 198 | return SVG_Sprite::instance()->remove_symbol( $handle ); 199 | } 200 | 201 | endif; 202 | 203 | if ( ! function_exists( 'am_get_symbol' ) ) : 204 | 205 | /** 206 | * Returns the SVG with `` element referencing the symbol. 207 | * 208 | * @param string $handle The symbol name. 209 | * @param array $attrs The attributes to add to the SVG element. 210 | */ 211 | function am_get_symbol( string $handle, array $attrs = [] ): string { 212 | return SVG_Sprite::instance()->get_symbol( $handle, $attrs ); 213 | } 214 | 215 | endif; 216 | 217 | if ( ! function_exists( 'am_use_symbol' ) ) : 218 | 219 | /** 220 | * Prints the SVG with `` element referencing the symbol. 221 | * 222 | * @param string $handle The symbol name. 223 | * @param array $attrs The attributes to add to the SVG element. 224 | */ 225 | function am_use_symbol( string $handle, array $attrs = [] ): void { 226 | SVG_Sprite::instance()->use_symbol( $handle, $attrs ); 227 | } 228 | 229 | endif; 230 | 231 | if ( ! function_exists( 'am_symbol_is_registered' ) ) : 232 | 233 | /** 234 | * Returns true if a symbol is registered. 235 | * 236 | * @param string $handle The registered SVG asset handle. 237 | * @return bool Whether the symbol is registered. 238 | */ 239 | function am_symbol_is_registered( string $handle ): bool { 240 | if ( empty( $handle ) ) { 241 | return false; 242 | } 243 | 244 | return in_array( $handle, SVG_Sprite::instance()->asset_handles, true ); 245 | } 246 | 247 | endif; 248 | -------------------------------------------------------------------------------- /src/kses-svg.php: -------------------------------------------------------------------------------- 1 | true, 25 | 'lang' => true, 26 | 'tabindex' => true, 27 | 'xml:base' => true, 28 | 'xml:lang' => true, 29 | 'xml:space' => true, 30 | ]; 31 | 32 | // Attributes that can be specified on any SVG element to apply CSS styling effects. 33 | $am_style_attributes = [ 34 | 'class' => true, 35 | 'style' => true, 36 | ]; 37 | 38 | // CSS properties that can be used as attributes on SVG elements. 39 | $am_presentation_attributes = [ 40 | 'alignment-baseline' => true, 41 | 'baseline-shift' => true, 42 | 'clip' => true, 43 | 'clip-path' => true, 44 | 'clip-rule' => true, 45 | 'color' => true, 46 | 'color-interpolation' => true, 47 | 'color-interpolation-filters' => true, 48 | 'color-profile' => true, 49 | 'color-rendering' => true, 50 | 'cursor' => true, 51 | 'direction' => true, 52 | 'display' => true, 53 | 'dominant-baseline' => true, 54 | 'enable-background' => true, 55 | 'fill' => true, 56 | 'fill-opacity' => true, 57 | 'fill-rule' => true, 58 | 'filter' => true, 59 | 'flood-color' => true, 60 | 'flood-opacity' => true, 61 | 'font-family' => true, 62 | 'font-size' => true, 63 | 'font-size-adjust' => true, 64 | 'font-stretch' => true, 65 | 'font-style' => true, 66 | 'font-variant' => true, 67 | 'font-weight' => true, 68 | 'glyph-orientation-horizontal' => true, 69 | 'glyph-orientation-vertical' => true, 70 | 'image-rendering' => true, 71 | 'kerning' => true, 72 | 'letter-spacing' => true, 73 | 'lighting-color' => true, 74 | 'marker-end' => true, 75 | 'marker-mid' => true, 76 | 'marker-start' => true, 77 | 'mask' => true, 78 | 'opacity' => true, 79 | 'overflow' => true, 80 | 'pointer-events' => true, 81 | 'shape-rendering' => true, 82 | 'stop-color' => true, 83 | 'stop-opacity' => true, 84 | 'stroke' => true, 85 | 'stroke-dasharray' => true, 86 | 'stroke-dashoffset' => true, 87 | 'stroke-linecap' => true, 88 | 'stroke-linejoin' => true, 89 | 'stroke-miterlimit' => true, 90 | 'stroke-opacity' => true, 91 | 'stroke-width' => true, 92 | 'text-anchor' => true, 93 | 'text-decoration' => true, 94 | 'text-rendering' => true, 95 | 'transform' => true, 96 | 'transform-origin' => true, 97 | 'unicode-bidi' => true, 98 | 'vector-effect' => true, 99 | 'visibility' => true, 100 | 'word-spacing' => true, 101 | 'writing-mode' => true, 102 | ]; 103 | 104 | // Filters attributes. 105 | $am_filter_primitive_attributes = [ 106 | 'height' => true, 107 | 'result' => true, 108 | 'width' => true, 109 | 'x' => true, 110 | 'y' => true, 111 | ]; 112 | 113 | $am_transfer_function_attributes = [ 114 | 'type' => true, 115 | 'tablevalues' => true, 116 | 'slope' => true, 117 | 'intercept' => true, 118 | 'amplitude' => true, 119 | 'exponent' => true, 120 | 'offset' => true, 121 | ]; 122 | 123 | // Animation attributes. 124 | $am_animation_target_element_attributes = [ 125 | 'href' => true, 126 | 'xlink:href' => true, // Deprecated. 127 | ]; 128 | 129 | $am_animation_attribute_target_attributes = [ 130 | 'attributetype' => true, // @todo 131 | 'attributename' => true, 132 | ]; 133 | 134 | $am_animation_timing_attributes = [ 135 | 'begin' => true, 136 | 'dur' => true, 137 | 'end' => true, 138 | 'min' => true, 139 | 'max' => true, 140 | 'restart' => true, 141 | 'repeatcount' => true, 142 | 'repeatdur' => true, 143 | 'fill' => true, 144 | ]; 145 | 146 | $am_animation_value_attributes = [ 147 | 'calcmode' => true, 148 | 'values' => true, 149 | 'keytimes' => true, 150 | 'keysplines' => true, 151 | 'from' => true, 152 | 'to' => true, 153 | 'by' => true, 154 | 'autoreverse' => true, 155 | 'accelerate' => true, 156 | 'decelerate' => true, 157 | ]; 158 | 159 | $am_animation_addition_attributes = [ 160 | 'additive' => true, 161 | 'accumulate' => true, 162 | ]; 163 | 164 | // Additional attributes. 165 | $am_conditional_processing_attributes = [ 166 | 'requiredextensions' => true, 167 | 'requiredfeatures' => true, 168 | 'systemlanguage' => true, 169 | ]; 170 | 171 | $am_xlink_attributes = [ 172 | 'xlink:type' => true, 173 | 'xlink:role' => true, 174 | 'xlink:arcrole' => true, 175 | 'xlink:title' => true, 176 | 'xlink:show' => true, 177 | 'xlink:actuate' => true, 178 | ]; 179 | 180 | $am_xmlns_attributes = [ 181 | 'xmlns' => true, 182 | 'xmlns:xlink' => true, 183 | 'xmlns:se' => true, 184 | ]; 185 | 186 | // ARIA attributes. 187 | $am_aria_attributes = [ 188 | 'aria-activedescendant' => true, 189 | 'aria-atomic' => true, 190 | 'aria-autocomplete' => true, 191 | 'aria-busy' => true, 192 | 'aria-checked' => true, 193 | 'aria-colcount' => true, 194 | 'aria-colindex' => true, 195 | 'aria-colspan' => true, 196 | 'aria-controls' => true, 197 | 'aria-current' => true, 198 | 'aria-describedby' => true, 199 | 'aria-details' => true, 200 | 'aria-disabled' => true, 201 | 'aria-dropeffect' => true, 202 | 'aria-errormessage' => true, 203 | 'aria-expanded' => true, 204 | 'aria-flowto' => true, 205 | 'aria-grabbed' => true, 206 | 'aria-haspopup' => true, 207 | 'aria-hidden' => true, 208 | 'aria-invalid' => true, 209 | 'aria-keyshortcuts' => true, 210 | 'aria-label' => true, 211 | 'aria-labelledby' => true, 212 | 'aria-level' => true, 213 | 'aria-live' => true, 214 | 'aria-modal' => true, 215 | 'aria-multiline' => true, 216 | 'aria-multiselectable' => true, 217 | 'aria-orientation' => true, 218 | 'aria-owns' => true, 219 | 'aria-placeholder' => true, 220 | 'aria-posinset' => true, 221 | 'aria-pressed' => true, 222 | 'aria-readonly' => true, 223 | 'aria-relevant' => true, 224 | 'aria-required' => true, 225 | 'aria-roledescription' => true, 226 | 'aria-rowcount' => true, 227 | 'aria-rowindex' => true, 228 | 'aria-rowspan' => true, 229 | 'aria-selected' => true, 230 | 'aria-setsize' => true, 231 | 'aria-sort' => true, 232 | 'aria-valuemax' => true, 233 | 'aria-valuemin' => true, 234 | 'aria-valuenow' => true, 235 | 'aria-valuetext' => true, 236 | 'role' => true, 237 | ]; 238 | 239 | /** 240 | * SVG allowed tags and attributes. 241 | * Some elements accept specific attributes in addition to the categories of attributes. 242 | */ 243 | $am_kses_svg = [ 244 | 'a' => array_merge( 245 | $am_animation_target_element_attributes, 246 | $am_aria_attributes, 247 | $am_conditional_processing_attributes, 248 | $am_core_attributes, 249 | $am_presentation_attributes, 250 | $am_style_attributes, 251 | $am_xlink_attributes, 252 | ), 253 | 'animate' => array_merge( 254 | $am_animation_addition_attributes, 255 | $am_animation_attribute_target_attributes, 256 | $am_animation_target_element_attributes, 257 | $am_animation_target_element_attributes, 258 | $am_animation_timing_attributes, 259 | $am_animation_value_attributes, 260 | $am_core_attributes, 261 | $am_style_attributes, 262 | ), 263 | 'animatemotion' => array_merge( 264 | [ 265 | 'keypoints' => true, 266 | 'path' => true, 267 | 'rotate' => true, 268 | ], 269 | $am_animation_addition_attributes, 270 | $am_animation_attribute_target_attributes, 271 | $am_animation_target_element_attributes, 272 | $am_animation_target_element_attributes, 273 | $am_animation_timing_attributes, 274 | $am_animation_value_attributes, 275 | $am_core_attributes, 276 | $am_style_attributes, 277 | ), 278 | 'animatetransform' => array_merge( 279 | $am_animation_addition_attributes, 280 | $am_animation_attribute_target_attributes, 281 | $am_animation_target_element_attributes, 282 | $am_animation_timing_attributes, 283 | $am_animation_value_attributes, 284 | $am_conditional_processing_attributes, 285 | $am_core_attributes, 286 | $am_xlink_attributes, 287 | ), 288 | 'circle' => array_merge( 289 | [ 290 | 'cx' => true, 291 | 'cy' => true, 292 | 'pathlength' => true, 293 | 'r' => true, 294 | ], 295 | $am_aria_attributes, 296 | $am_conditional_processing_attributes, 297 | $am_core_attributes, 298 | $am_presentation_attributes, 299 | $am_style_attributes, 300 | ), 301 | 'clippath' => array_merge( 302 | [ 303 | 'clippathunits' => true, 304 | ], 305 | $am_conditional_processing_attributes, 306 | $am_core_attributes, 307 | $am_presentation_attributes, 308 | $am_style_attributes, 309 | ), 310 | 'defs' => array_merge( 311 | $am_core_attributes, 312 | $am_presentation_attributes, 313 | $am_style_attributes, 314 | ), 315 | 'desc' => array_merge( 316 | $am_core_attributes, 317 | $am_style_attributes, 318 | ), 319 | 'discard' => array_merge( 320 | [ 321 | 'begiun' => true, 322 | ], 323 | $am_animation_target_element_attributes, 324 | $am_aria_attributes, 325 | $am_conditional_processing_attributes, 326 | $am_core_attributes, 327 | ), 328 | 'ellipse' => array_merge( 329 | [ 330 | 'cx' => true, 331 | 'cy' => true, 332 | 'pathlength' => true, 333 | 'rx' => true, 334 | 'ry' => true, 335 | ], 336 | $am_aria_attributes, 337 | $am_conditional_processing_attributes, 338 | $am_core_attributes, 339 | $am_presentation_attributes, 340 | $am_style_attributes, 341 | ), 342 | 'feblend' => array_merge( 343 | [ 344 | 'in' => true, 345 | 'in2' => true, 346 | 'mode' => true, 347 | ], 348 | $am_core_attributes, 349 | $am_filter_primitive_attributes, 350 | $am_presentation_attributes, 351 | $am_style_attributes, 352 | ), 353 | 'fecolormatrix' => array_merge( 354 | [ 355 | 'in' => true, 356 | 'type' => true, 357 | 'values' => true, 358 | ], 359 | $am_core_attributes, 360 | $am_filter_primitive_attributes, 361 | $am_presentation_attributes, 362 | $am_style_attributes, 363 | ), 364 | 'fecomponenttransfer' => array_merge( 365 | [ 366 | 'in' => true, 367 | ], 368 | $am_core_attributes, 369 | $am_filter_primitive_attributes, 370 | $am_presentation_attributes, 371 | $am_style_attributes, 372 | ), 373 | 'fecomposite' => array_merge( 374 | [ 375 | 'in' => true, 376 | 'in2' => true, 377 | 'k1' => true, 378 | 'k2' => true, 379 | 'k3' => true, 380 | 'k4' => true, 381 | 'operator' => true, 382 | ], 383 | $am_core_attributes, 384 | $am_filter_primitive_attributes, 385 | $am_presentation_attributes, 386 | $am_style_attributes, 387 | ), 388 | 'feconvolvematrix' => array_merge( 389 | [ 390 | 'bias' => true, 391 | 'divisor' => true, 392 | 'edgemode' => true, 393 | 'in' => true, 394 | 'kernelmatrix' => true, 395 | 'kernelunitlength' => true, 396 | 'order' => true, 397 | 'preservealpha' => true, 398 | 'targetx' => true, 399 | 'targety' => true, 400 | ], 401 | $am_core_attributes, 402 | $am_filter_primitive_attributes, 403 | $am_presentation_attributes, 404 | $am_style_attributes, 405 | ), 406 | 'fediffuselighting' => array_merge( 407 | [ 408 | 'diffuseconstant' => true, 409 | 'in' => true, 410 | 'kernelunitlength' => true, 411 | 'surfacescale' => true, 412 | ], 413 | $am_core_attributes, 414 | $am_filter_primitive_attributes, 415 | $am_presentation_attributes, 416 | $am_style_attributes, 417 | ), 418 | 'fedisplacementmap' => array_merge( 419 | [ 420 | 'in' => true, 421 | 'in2' => true, 422 | 'scale' => true, 423 | 'xchannelselector' => true, 424 | 'ychannelselector' => true, 425 | ], 426 | $am_core_attributes, 427 | $am_filter_primitive_attributes, 428 | $am_presentation_attributes, 429 | $am_style_attributes, 430 | ), 431 | 'fedistantlight' => array_merge( 432 | [ 433 | 'azimuth' => true, 434 | 'elevation' => true, 435 | ], 436 | $am_core_attributes, 437 | ), 438 | 'fedropshadow' => array_merge( 439 | [ 440 | 'dx' => true, 441 | 'dy' => true, 442 | 'stddeviation' => true, 443 | ], 444 | $am_core_attributes, 445 | $am_filter_primitive_attributes, 446 | $am_presentation_attributes, 447 | $am_style_attributes, 448 | ), 449 | 'feflood' => array_merge( 450 | [ 451 | 'flood-color' => true, 452 | 'flood-opacity' => true, 453 | ], 454 | $am_core_attributes, 455 | $am_filter_primitive_attributes, 456 | $am_presentation_attributes, 457 | $am_style_attributes, 458 | ), 459 | 'fefunca' => array_merge( 460 | $am_core_attributes, 461 | $am_transfer_function_attributes, 462 | ), 463 | 'fefuncb' => array_merge( 464 | $am_core_attributes, 465 | $am_transfer_function_attributes, 466 | ), 467 | 'fefuncg' => array_merge( 468 | $am_core_attributes, 469 | $am_transfer_function_attributes, 470 | ), 471 | 'fefuncr' => array_merge( 472 | $am_core_attributes, 473 | $am_transfer_function_attributes, 474 | ), 475 | 'fegaussianblur' => array_merge( 476 | [ 477 | 'edgemode' => true, 478 | 'in' => true, 479 | 'stddeviation' => true, 480 | ], 481 | $am_core_attributes, 482 | $am_filter_primitive_attributes, 483 | $am_presentation_attributes, 484 | $am_style_attributes, 485 | ), 486 | 'feimage' => array_merge( 487 | [ 488 | 'preserveaspectratio' => true, 489 | ], 490 | $am_animation_target_element_attributes, 491 | $am_core_attributes, 492 | $am_filter_primitive_attributes, 493 | $am_presentation_attributes, 494 | $am_style_attributes, 495 | $am_xlink_attributes, 496 | ), 497 | 'femerge' => array_merge( 498 | $am_core_attributes, 499 | $am_filter_primitive_attributes, 500 | $am_presentation_attributes, 501 | $am_style_attributes, 502 | ), 503 | 'femergenode' => array_merge( 504 | [ 505 | 'in' => true, 506 | ], 507 | $am_core_attributes, 508 | ), 509 | 'femorphology' => array_merge( 510 | [ 511 | 'in' => true, 512 | 'operator' => true, 513 | 'radius' => true, 514 | ], 515 | $am_core_attributes, 516 | $am_filter_primitive_attributes, 517 | $am_presentation_attributes, 518 | $am_style_attributes, 519 | ), 520 | 'feoffset' => array_merge( 521 | [ 522 | 'dx' => true, 523 | 'dy' => true, 524 | 'in' => true, 525 | ], 526 | $am_core_attributes, 527 | $am_filter_primitive_attributes, 528 | $am_presentation_attributes, 529 | $am_style_attributes, 530 | ), 531 | 'fepointlight' => array_merge( 532 | [ 533 | 'x' => true, 534 | 'y' => true, 535 | 'z' => true, 536 | ], 537 | $am_core_attributes, 538 | ), 539 | 'fespecularlighting' => array_merge( 540 | [ 541 | 'in' => true, 542 | 'kernelunitlength' => true, 543 | 'specularconstant' => true, 544 | 'specularexponent' => true, 545 | 'surfacescale' => true, 546 | ], 547 | $am_core_attributes, 548 | $am_filter_primitive_attributes, 549 | $am_presentation_attributes, 550 | $am_style_attributes, 551 | ), 552 | 'fespotlight' => array_merge( 553 | [ 554 | 'limitingconeangle' => true, 555 | 'pointsatx' => true, 556 | 'pointsaty' => true, 557 | 'pointsatz' => true, 558 | 'specularexponent' => true, 559 | 'x' => true, 560 | 'y' => true, 561 | 'z' => true, 562 | ], 563 | $am_core_attributes, 564 | ), 565 | 'fetile' => array_merge( 566 | [ 567 | 'in' => true, 568 | ], 569 | $am_core_attributes, 570 | $am_filter_primitive_attributes, 571 | $am_presentation_attributes, 572 | $am_style_attributes, 573 | ), 574 | 'feturbulence' => array_merge( 575 | [ 576 | 'basefrequency' => true, 577 | 'numoctaves' => true, 578 | 'seed' => true, 579 | 'stitchtiles' => true, 580 | 'type' => true, 581 | ], 582 | $am_core_attributes, 583 | $am_filter_primitive_attributes, 584 | $am_presentation_attributes, 585 | $am_style_attributes, 586 | ), 587 | 'filter' => array_merge( 588 | [ 589 | 'filterres' => true, 590 | 'filterunits' => true, 591 | 'height' => true, 592 | 'primitiveunits' => true, 593 | 'width' => true, 594 | 'x' => true, 595 | 'y' => true, 596 | ], 597 | $am_animation_target_element_attributes, 598 | $am_core_attributes, 599 | $am_presentation_attributes, 600 | $am_style_attributes, 601 | $am_xlink_attributes, 602 | ), 603 | 'foreignobject' => array_merge( 604 | [ 605 | 'height' => true, 606 | 'width' => true, 607 | 'x' => true, 608 | 'y' => true, 609 | ], 610 | $am_aria_attributes, 611 | $am_conditional_processing_attributes, 612 | $am_core_attributes, 613 | $am_presentation_attributes, 614 | $am_style_attributes, 615 | ), 616 | 'g' => array_merge( 617 | $am_aria_attributes, 618 | $am_conditional_processing_attributes, 619 | $am_core_attributes, 620 | $am_presentation_attributes, 621 | $am_style_attributes, 622 | ), 623 | 'hatch' => array_merge( 624 | [ 625 | 'hatchcontentunits' => true, 626 | 'hatchunits' => true, 627 | 'pitch' => true, 628 | 'rotate' => true, 629 | 'transform' => true, 630 | 'x' => true, 631 | 'y' => true, 632 | ], 633 | $am_animation_target_element_attributes, 634 | $am_core_attributes, 635 | $am_presentation_attributes, 636 | $am_style_attributes, 637 | ), 638 | 'hatchpath' => array_merge( 639 | [ 640 | 'd' => true, 641 | 'offset' => true, 642 | ], 643 | $am_core_attributes, 644 | $am_presentation_attributes, 645 | $am_style_attributes, 646 | ), 647 | 'image' => array_merge( 648 | [ 649 | 'crossorigin' => true, 650 | 'height' => true, 651 | 'preserveaspectratio' => true, 652 | 'width' => true, 653 | 'x' => true, 654 | 'y' => true, 655 | ], 656 | $am_animation_target_element_attributes, 657 | $am_conditional_processing_attributes, 658 | $am_core_attributes, 659 | $am_presentation_attributes, 660 | $am_style_attributes, 661 | $am_xlink_attributes, 662 | ), 663 | 'line' => array_merge( 664 | [ 665 | 'pathlength' => true, 666 | 'x1' => true, 667 | 'x2' => true, 668 | 'y1' => true, 669 | 'y2' => true, 670 | ], 671 | $am_aria_attributes, 672 | $am_conditional_processing_attributes, 673 | $am_core_attributes, 674 | $am_presentation_attributes, 675 | $am_style_attributes, 676 | ), 677 | 'lineargradient' => array_merge( 678 | [ 679 | 'gradienttransform' => true, 680 | 'gradientunits' => true, 681 | 'spreadmethod' => true, 682 | 'x1' => true, 683 | 'x2' => true, 684 | 'y1' => true, 685 | 'y2' => true, 686 | ], 687 | $am_animation_target_element_attributes, 688 | $am_core_attributes, 689 | $am_presentation_attributes, 690 | $am_style_attributes, 691 | $am_xlink_attributes, 692 | ), 693 | 'marker' => array_merge( 694 | [ 695 | 'markerheight' => true, 696 | 'markerunits' => true, 697 | 'markerwidth' => true, 698 | 'orient' => true, 699 | 'preserveaspectratio' => true, 700 | 'refx' => true, 701 | 'refy' => true, 702 | 'viewbox' => true, 703 | ], 704 | $am_aria_attributes, 705 | $am_conditional_processing_attributes, 706 | $am_core_attributes, 707 | $am_presentation_attributes, 708 | $am_style_attributes, 709 | ), 710 | 'mask' => array_merge( 711 | [ 712 | 'height' => true, 713 | 'maskcontentunits' => true, 714 | 'maskunits' => true, 715 | 'width' => true, 716 | 'x' => true, 717 | 'y' => true, 718 | ], 719 | $am_conditional_processing_attributes, 720 | $am_core_attributes, 721 | $am_presentation_attributes, 722 | $am_style_attributes, 723 | ), 724 | 'meshgradient' => array_merge( 725 | [ 726 | 'gradientunits' => true, 727 | 'transform' => true, 728 | 'type' => true, 729 | 'x' => true, 730 | 'y' => true, 731 | ], 732 | $am_animation_target_element_attributes, 733 | $am_core_attributes, 734 | $am_presentation_attributes, 735 | $am_style_attributes, 736 | ), 737 | 'meshpatch' => array_merge( 738 | $am_core_attributes, 739 | $am_presentation_attributes, 740 | $am_style_attributes, 741 | ), 742 | 'meshrow' => array_merge( 743 | $am_core_attributes, 744 | $am_presentation_attributes, 745 | $am_style_attributes, 746 | ), 747 | 'metadata' => $am_core_attributes, 748 | 'mpath' => array_merge( 749 | $am_animation_target_element_attributes, 750 | $am_core_attributes, 751 | $am_xlink_attributes, 752 | ), 753 | 'path' => array_merge( 754 | [ 755 | 'd' => true, 756 | 'pathlength' => true, 757 | ], 758 | $am_aria_attributes, 759 | $am_conditional_processing_attributes, 760 | $am_core_attributes, 761 | $am_presentation_attributes, 762 | $am_style_attributes, 763 | ), 764 | 'pattern' => array_merge( 765 | [ 766 | 'height' => true, 767 | 'patterncontentunits' => true, 768 | 'patterntransform' => true, 769 | 'patternunits' => true, 770 | 'preserveaspectratio' => true, 771 | 'viewbox' => true, 772 | 'width' => true, 773 | 'x' => true, 774 | 'y' => true, 775 | ], 776 | $am_animation_target_element_attributes, 777 | $am_conditional_processing_attributes, 778 | $am_core_attributes, 779 | $am_presentation_attributes, 780 | $am_style_attributes, 781 | $am_xlink_attributes, 782 | ), 783 | 'polygon' => array_merge( 784 | [ 785 | 'pathlength' => true, 786 | 'points' => true, 787 | ], 788 | $am_aria_attributes, 789 | $am_conditional_processing_attributes, 790 | $am_core_attributes, 791 | $am_presentation_attributes, 792 | $am_style_attributes, 793 | ), 794 | 'polyline' => array_merge( 795 | [ 796 | 'pathlength' => true, 797 | 'points' => true, 798 | ], 799 | $am_aria_attributes, 800 | $am_conditional_processing_attributes, 801 | $am_core_attributes, 802 | $am_presentation_attributes, 803 | $am_style_attributes, 804 | ), 805 | 'radialgradient' => array_merge( 806 | [ 807 | 'cx' => true, 808 | 'cy' => true, 809 | 'fr' => true, 810 | 'fx' => true, 811 | 'fy' => true, 812 | 'gradienttransform' => true, 813 | 'gradientunits' => true, 814 | 'r' => true, 815 | 'spreadmethod' => true, 816 | ], 817 | $am_animation_target_element_attributes, 818 | $am_core_attributes, 819 | $am_presentation_attributes, 820 | $am_style_attributes, 821 | $am_xlink_attributes, 822 | ), 823 | 'rect' => array_merge( 824 | [ 825 | 'height' => true, 826 | 'pathlength' => true, 827 | 'rx' => true, 828 | 'ry' => true, 829 | 'width' => true, 830 | 'x' => true, 831 | 'y' => true, 832 | ], 833 | $am_aria_attributes, 834 | $am_conditional_processing_attributes, 835 | $am_core_attributes, 836 | $am_presentation_attributes, 837 | $am_style_attributes, 838 | ), 839 | 'set' => array_merge( 840 | [ 841 | 'to' => true, 842 | ], 843 | $am_animation_target_element_attributes, 844 | $am_animation_attribute_target_attributes, 845 | $am_animation_timing_attributes, 846 | $am_core_attributes, 847 | $am_style_attributes, 848 | ), 849 | 'stop' => array_merge( 850 | [ 851 | 'offset' => true, 852 | 'stop-color' => true, 853 | 'stop-opacity' => true, 854 | ], 855 | $am_core_attributes, 856 | $am_presentation_attributes, 857 | $am_style_attributes, 858 | ), 859 | 'style' => array_merge( 860 | [ 861 | 'media' => true, 862 | 'title' => true, 863 | 'type' => true, 864 | ], 865 | $am_core_attributes, 866 | $am_style_attributes, 867 | ), 868 | 'svg' => array_merge( 869 | [ 870 | 'focusable' => true, 871 | 'height' => true, 872 | 'preserveaspectratio' => true, 873 | 'viewbox' => true, 874 | 'width' => true, 875 | 'x' => true, 876 | 'y' => true, 877 | ], 878 | $am_aria_attributes, 879 | $am_conditional_processing_attributes, 880 | $am_core_attributes, 881 | $am_presentation_attributes, 882 | $am_style_attributes, 883 | $am_xmlns_attributes, 884 | ), 885 | 'switch' => array_merge( 886 | $am_conditional_processing_attributes, 887 | $am_core_attributes, 888 | $am_presentation_attributes, 889 | $am_style_attributes, 890 | ), 891 | 'symbol' => array_merge( 892 | [ 893 | 'height' => true, 894 | 'preserveaspectratio' => true, 895 | 'refx' => true, 896 | 'refy' => true, 897 | 'viewbox' => true, 898 | 'width' => true, 899 | 'x' => true, 900 | 'y' => true, 901 | ], 902 | $am_aria_attributes, 903 | $am_core_attributes, 904 | $am_presentation_attributes, 905 | $am_style_attributes, 906 | $am_xmlns_attributes, 907 | ), 908 | 'text' => array_merge( 909 | [ 910 | 'dx' => true, 911 | 'dy' => true, 912 | 'lengthadjust' => true, 913 | 'rotate' => true, 914 | 'textlength' => true, 915 | 'x' => true, 916 | 'y' => true, 917 | ], 918 | $am_aria_attributes, 919 | $am_conditional_processing_attributes, 920 | $am_core_attributes, 921 | $am_presentation_attributes, 922 | $am_style_attributes, 923 | ), 924 | 'textpath' => array_merge( 925 | [ 926 | 'lengthadjust' => true, 927 | 'method' => true, 928 | 'path' => true, 929 | 'side' => true, 930 | 'spacing' => true, 931 | 'startoffset' => true, 932 | 'textlength' => true, 933 | ], 934 | $am_animation_target_element_attributes, 935 | $am_aria_attributes, 936 | $am_conditional_processing_attributes, 937 | $am_core_attributes, 938 | $am_presentation_attributes, 939 | $am_style_attributes, 940 | ), 941 | 'title' => array_merge( 942 | $am_core_attributes, 943 | $am_style_attributes, 944 | ), 945 | 'tspan' => array_merge( 946 | [ 947 | 'dx' => true, 948 | 'dy' => true, 949 | 'lengthadjust' => true, 950 | 'rotate' => true, 951 | 'textlength' => true, 952 | 'x' => true, 953 | 'y' => true, 954 | ], 955 | $am_aria_attributes, 956 | $am_conditional_processing_attributes, 957 | $am_core_attributes, 958 | $am_presentation_attributes, 959 | $am_style_attributes, 960 | ), 961 | 'unknown' => array_merge( 962 | $am_aria_attributes, 963 | $am_conditional_processing_attributes, 964 | $am_core_attributes, 965 | $am_presentation_attributes, 966 | ), 967 | 'use' => array_merge( 968 | [ 969 | 'height' => true, 970 | 'width' => true, 971 | 'x' => true, 972 | 'y' => true, 973 | ], 974 | $am_animation_target_element_attributes, 975 | $am_aria_attributes, 976 | $am_conditional_processing_attributes, 977 | $am_core_attributes, 978 | $am_presentation_attributes, 979 | $am_style_attributes, 980 | ), 981 | 'view' => array_merge( 982 | [ 983 | 'preserveaspectratio' => true, 984 | 'viewbox' => true, 985 | 'viewtarget' => true, 986 | 'zoomandpan' => true, 987 | ], 988 | $am_aria_attributes, 989 | $am_core_attributes, 990 | ), 991 | ]; 992 | -------------------------------------------------------------------------------- /wp-asset-manager.php: -------------------------------------------------------------------------------- 1 |