Background
2 years ago
Blocks
3 years ago
GlobalElements
3 years ago
Layout
4 years ago
License
2 years ago
Separators
4 years ago
StyleManager
2 years ago
Styles
4 years ago
Activation.php
2 years ago
Backup.php
4 years ago
CustomizerImporter.php
3 years ago
Deactivation.php
4 years ago
EditInKubioCustomizerPanel.php
4 years ago
Element.php
2 years ago
ElementBase.php
4 years ago
Importer.php
2 years ago
InnerBlocks.php
4 years ago
LodashBasic.php
2 years ago
Registry.php
3 years ago
Utils.php
2 years ago
Element.php
371 lines
| 1 | <?php |
| 2 | |
| 3 | namespace Kubio\Core; |
| 4 | |
| 5 | use function array_shift; |
| 6 | use function explode; |
| 7 | use function is_array; |
| 8 | use function is_string; |
| 9 | use function strpos; |
| 10 | #[\AllowDynamicProperties] |
| 11 | class Element extends ElementBase { |
| 12 | |
| 13 | const DIV = 'div'; |
| 14 | const SPAN = 'span'; |
| 15 | const IMAGE = 'img'; |
| 16 | const A = 'a'; |
| 17 | const FRAGMENT = '<>'; |
| 18 | const DASHED_ATTRIBUTES = array( 'data', 'aria' ); |
| 19 | const DASHED_ATTRIBUTES_REGEX = '/^(data|aria)-/'; |
| 20 | const ALLOWED_ATTRIBUTES = array( |
| 21 | 'hidden', |
| 22 | 'high', |
| 23 | 'href', |
| 24 | 'hreflang', |
| 25 | 'http-equiv', |
| 26 | 'icon', |
| 27 | 'id', |
| 28 | 'ismap', |
| 29 | 'itemprop', |
| 30 | 'keytype', |
| 31 | 'kind', |
| 32 | 'label', |
| 33 | 'lang', |
| 34 | 'language', |
| 35 | 'list', |
| 36 | 'loop', |
| 37 | 'low', |
| 38 | 'manifest', |
| 39 | 'max', |
| 40 | 'maxlength', |
| 41 | 'media', |
| 42 | 'method', |
| 43 | 'min', |
| 44 | 'multiple', |
| 45 | 'name', |
| 46 | 'novalidate', |
| 47 | 'open', |
| 48 | 'optimum', |
| 49 | 'pattern', |
| 50 | 'ping', |
| 51 | 'placeholder', |
| 52 | 'poster', |
| 53 | 'preload', |
| 54 | 'pubdate', |
| 55 | 'radiogroup', |
| 56 | 'readonly', |
| 57 | 'rel', |
| 58 | 'required', |
| 59 | 'reversed', |
| 60 | 'rows', |
| 61 | 'rowspan', |
| 62 | 'sandbox', |
| 63 | 'spellcheck', |
| 64 | 'scope', |
| 65 | 'scoped', |
| 66 | 'seamless', |
| 67 | 'selected', |
| 68 | 'shape', |
| 69 | 'size', |
| 70 | 'sizes', |
| 71 | 'span', |
| 72 | 'src', |
| 73 | 'srcdoc', |
| 74 | 'srclang', |
| 75 | 'srcset', |
| 76 | 'start', |
| 77 | 'step', |
| 78 | 'style', |
| 79 | 'summary', |
| 80 | 'tabindex', |
| 81 | 'target', |
| 82 | 'title', |
| 83 | 'type', |
| 84 | 'usemap', |
| 85 | 'value', |
| 86 | 'width', |
| 87 | 'wrap', |
| 88 | 'border', |
| 89 | 'buffered', |
| 90 | 'challenge', |
| 91 | 'charset', |
| 92 | 'checked', |
| 93 | 'cite', |
| 94 | 'class', |
| 95 | 'code', |
| 96 | 'codebase', |
| 97 | 'color', |
| 98 | 'cols', |
| 99 | 'colspan', |
| 100 | 'content', |
| 101 | 'contenteditable', |
| 102 | 'contextmenu', |
| 103 | 'controls', |
| 104 | 'coords', |
| 105 | 'data', |
| 106 | 'datetime', |
| 107 | 'default', |
| 108 | 'defer', |
| 109 | 'dir', |
| 110 | 'dirname', |
| 111 | 'disabled', |
| 112 | 'download', |
| 113 | 'draggable', |
| 114 | 'dropzone', |
| 115 | 'enctype', |
| 116 | 'for', |
| 117 | 'form', |
| 118 | 'formaction', |
| 119 | 'headers', |
| 120 | 'height', |
| 121 | 'accept', |
| 122 | 'accept-charset', |
| 123 | 'accesskey', |
| 124 | 'action', |
| 125 | 'align', |
| 126 | 'alt', |
| 127 | 'async', |
| 128 | 'autocomplete', |
| 129 | 'autofocus', |
| 130 | 'autoplay', |
| 131 | 'autosave', |
| 132 | 'bgcolor', |
| 133 | ); |
| 134 | |
| 135 | const SELF_CLOSING_TAGS = array( |
| 136 | 'area', |
| 137 | 'base', |
| 138 | 'br', |
| 139 | 'embed', |
| 140 | 'hr', |
| 141 | // "iframe", - self closing iframe break chrome |
| 142 | 'img', |
| 143 | 'input', |
| 144 | 'link', |
| 145 | 'meta', |
| 146 | 'param', |
| 147 | 'source', |
| 148 | 'track', |
| 149 | |
| 150 | ); |
| 151 | |
| 152 | public static $allowedAttributesByName = true; |
| 153 | public $block; |
| 154 | protected $type; |
| 155 | protected $props; |
| 156 | protected $filters = null; |
| 157 | protected $children = array(); |
| 158 | protected $innerHTML = ''; |
| 159 | protected $shouldRender = true; |
| 160 | |
| 161 | |
| 162 | function __construct( $type, $props = array(), $children = array(), $block = null ) { |
| 163 | self::$allowedAttributesByName = array_fill_keys( self::ALLOWED_ATTRIBUTES, true ); |
| 164 | |
| 165 | $this->type = $type; |
| 166 | |
| 167 | $this->children = $children; |
| 168 | $this->block = $block; |
| 169 | |
| 170 | $this->resolveComputed( $props ); |
| 171 | |
| 172 | if ( isset( $props['innerHTML'] ) ) { |
| 173 | $this->innerHTML = $props['innerHTML']; |
| 174 | unset( $props['innerHTML'] ); |
| 175 | } |
| 176 | |
| 177 | if ( isset( $props['shouldRender'] ) ) { |
| 178 | $this->shouldRender = $props['shouldRender']; |
| 179 | unset( $props['shouldRender'] ); |
| 180 | } |
| 181 | |
| 182 | if ( ! empty( $props['filters'] ) ) { |
| 183 | $this->filters = $props['filters']; |
| 184 | unset( $props['filters'] ); |
| 185 | } |
| 186 | |
| 187 | if ( isset( $props['disableStyleClasses'] ) ) { |
| 188 | $this->disableStyleClasses = $props['disableStyleClasses']; |
| 189 | unset( $props['disableStyleClasses'] ); |
| 190 | } |
| 191 | |
| 192 | $this->props = $props; |
| 193 | } |
| 194 | |
| 195 | function resolveComputed( &$props ) { |
| 196 | foreach ( $props as $name => $value ) { |
| 197 | if ( is_string( $value ) ) { |
| 198 | if ( strpos( $value, 'computed.' ) === 0 ) { |
| 199 | $props[ $name ] = $this->getComputed( $value ); |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | if ( is_array( $value ) ) { |
| 204 | $this->resolveComputed( $value ); |
| 205 | } |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | function getComputed( $path, $defaultValue = null ) { |
| 210 | $paths = explode( '.', $path ); |
| 211 | array_shift( $paths ); |
| 212 | |
| 213 | return LodashBasic::get( $this->block->computed(), $paths, $defaultValue ); |
| 214 | } |
| 215 | |
| 216 | function getClassName() { |
| 217 | return $this->getProp( 'className', array() ); |
| 218 | } |
| 219 | |
| 220 | function getProp( $name, $default = null ) { |
| 221 | return LodashBasic::get( $this->props, $name, $default ); |
| 222 | } |
| 223 | |
| 224 | function extendProps( $extend ) { |
| 225 | $this->props = LodashBasic::merge( $extend, $this->props ); |
| 226 | } |
| 227 | |
| 228 | function setChildren( $children ) { |
| 229 | $this->children = $children; |
| 230 | } |
| 231 | |
| 232 | function mergeProps( ...$arrays ) { |
| 233 | $result = array(); |
| 234 | foreach ( $arrays as $props ) { |
| 235 | if ( $props ) { |
| 236 | foreach ( $props as $prop_name => $prop_value ) { |
| 237 | $result_value = LodashBasic::get( $result, $prop_name, array() ); |
| 238 | if ( isset( $prop_value ) ) { |
| 239 | switch ( $prop_name ) { |
| 240 | case 'className': |
| 241 | $result[ $prop_name ] = LodashBasic::concat( $result_value, $prop_value ); |
| 242 | break; |
| 243 | case 'style': |
| 244 | $result[ $prop_name ] = LodashBasic::merge( $result_value, $prop_value ); |
| 245 | break; |
| 246 | default: |
| 247 | $result[ $prop_name ] = $prop_value; |
| 248 | } |
| 249 | } |
| 250 | } |
| 251 | } |
| 252 | } |
| 253 | $result['className'] = LodashBasic::identity( LodashBasic::uniq( $result['className'] ) ); |
| 254 | |
| 255 | return $result; |
| 256 | } |
| 257 | |
| 258 | function __toString() { |
| 259 | if ( ! $this->shouldRender ) { |
| 260 | return ''; |
| 261 | } |
| 262 | |
| 263 | $output = ''; |
| 264 | $tags = array(); |
| 265 | |
| 266 | $tag_name = tag_escape( $this->tagName() ); |
| 267 | |
| 268 | if ( $this->type !== self::FRAGMENT && in_array( $tag_name, self::SELF_CLOSING_TAGS ) ) { |
| 269 | $output = "<{$tag_name} {$this->getAttributesAsString()} />"; |
| 270 | } else { |
| 271 | if ( $this->type !== self::FRAGMENT ) { |
| 272 | $tags[] = "<{$tag_name} {$this->getAttributesAsString()}>"; |
| 273 | } |
| 274 | |
| 275 | // check for non empty strings that return false on validation like. ex: '0' |
| 276 | $non_empty_string = is_string( $this->innerHTML ) && strlen( $this->innerHTML ); |
| 277 | if ( $this->innerHTML || $non_empty_string ) { |
| 278 | $tags[] = $this->innerHTML; |
| 279 | } else { |
| 280 | foreach ( $this->children as $child ) { |
| 281 | if ( $child ) { |
| 282 | $tags[] = $child; |
| 283 | } |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | if ( $this->type !== self::FRAGMENT ) { |
| 288 | $tags[] = "</{$tag_name}>"; |
| 289 | } |
| 290 | |
| 291 | $output = implode( '', $tags ); |
| 292 | } |
| 293 | |
| 294 | if ( ! empty( $this->filters ) ) { |
| 295 | foreach ( $this->filters as $filter ) { |
| 296 | $output = apply_filters( $filter, $output ); |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | return $output; |
| 301 | } |
| 302 | |
| 303 | function tagName() { |
| 304 | return $this->type; |
| 305 | } |
| 306 | |
| 307 | function getAttributesAsString() { |
| 308 | $attrs = array(); |
| 309 | $props = $this->getProps(); |
| 310 | |
| 311 | foreach ( $props as $prop_name => $prop_value ) { |
| 312 | $attr_name = $prop_name; |
| 313 | $attr_value = $prop_value; |
| 314 | switch ( $prop_name ) { |
| 315 | case 'className': |
| 316 | $attr_name = 'class'; |
| 317 | $attr_value = $this->classAttribute( $prop_value ); |
| 318 | break; |
| 319 | case 'style': |
| 320 | $attr_value = $this->styleAttribute( $prop_value ); |
| 321 | break; |
| 322 | } |
| 323 | if ( is_string( $attr_value ) ) { |
| 324 | if ( isset( self::$allowedAttributesByName[ $attr_name ] ) || preg_match( Element::DASHED_ATTRIBUTES_REGEX, $attr_name ) ) { |
| 325 | $attrs[] = $attr_name . '="' . esc_attr( $attr_value ) . '"'; |
| 326 | } |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | return implode( ' ', $attrs ); |
| 331 | } |
| 332 | |
| 333 | function getProps() { |
| 334 | return $this->props; |
| 335 | } |
| 336 | |
| 337 | function setProps( $props ) { |
| 338 | $this->props = $props; |
| 339 | } |
| 340 | |
| 341 | function classAttribute( $classes ) { |
| 342 | $cls = array(); |
| 343 | if ( is_string( $classes ) ) { |
| 344 | return $classes; |
| 345 | } |
| 346 | if ( is_array( $classes ) ) { |
| 347 | foreach ( $classes as $class_name => $class_value ) { |
| 348 | if ( ! is_numeric( $class_name ) ) { |
| 349 | if ( ! ! $class_value ) { |
| 350 | $cls[] = $class_name; |
| 351 | } |
| 352 | } else { |
| 353 | $cls[] = $class_value; |
| 354 | } |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | return implode( ' ', $cls ); |
| 359 | } |
| 360 | |
| 361 | function styleAttribute( $style ) { |
| 362 | $styles = array(); |
| 363 | foreach ( $style as $s_name => $s_value ) { |
| 364 | $styles[] = LodashBasic::kebabCase( $s_name ) . ':' . $s_value; |
| 365 | } |
| 366 | |
| 367 | return implode( ';', $styles ); |
| 368 | } |
| 369 | } |
| 370 | |
| 371 |