'text', 'spacing' => 'spacing', 'font' => 'font', 'align' => 'align', 'contrast' => 'contrast', 'links' => 'links', 'headings' => 'headings', 'guide' => 'guide', 'images' => 'images', 'tts' => null, 'motion' => 'motion', 'cursor' => 'cursor', 'focus' => 'focus', ); } /** * Posiciones permitidas del botón. * * @return string[] */ public static function positions() { return array( 'bottom-right', 'bottom-left', 'top-right', 'top-left' ); } /** * Tamaños del botón en píxeles. * * @return int[] */ public static function sizes() { return array( 'small' => 44, 'medium' => 52, 'large' => 64, ); } /** * Valores por defecto. * * @return array */ public static function defaults() { return array( 'enabled' => 1, 'admins_only' => 0, 'show_trigger' => 1, 'position' => 'bottom-right', 'offset_x' => 20, 'offset_y' => 20, 'size' => 'medium', 'color' => self::BRAND_COLOR, 'features' => array_fill_keys( array_keys( self::features() ), 1 ), 'remember' => 1, 'respect_motion' => 1, 'statement_url' => '', 'contact_cta' => 0, 'show_credit' => 0, 'contact_url' => '', 'contact_text' => '', ); } /** * Ajustes guardados combinados con los valores por defecto. * * @return array */ public static function get() { $saved = get_option( self::OPTION, array() ); $saved = is_array( $saved ) ? $saved : array(); $defaults = self::defaults(); $settings = array_merge( $defaults, $saved ); $features = ( isset( $saved['features'] ) && is_array( $saved['features'] ) ) ? $saved['features'] : array(); $settings['features'] = array_merge( $defaults['features'], $features ); return $settings; } /** * Sanea el array enviado desde el formulario (o desde update_option). * * @param mixed $input Datos sin sanear. * @return array */ public static function sanitize( $input ) { $defaults = self::defaults(); $input = is_array( $input ) ? $input : array(); $out = array(); foreach ( array( 'enabled', 'admins_only', 'show_trigger', 'remember', 'respect_motion', 'contact_cta', 'show_credit' ) as $key ) { $out[ $key ] = empty( $input[ $key ] ) ? 0 : 1; } $position = isset( $input['position'] ) ? (string) $input['position'] : ''; $out['position'] = in_array( $position, self::positions(), true ) ? $position : $defaults['position']; $size = isset( $input['size'] ) ? (string) $input['size'] : ''; $out['size'] = array_key_exists( $size, self::sizes() ) ? $size : $defaults['size']; foreach ( array( 'offset_x', 'offset_y' ) as $key ) { $value = isset( $input[ $key ] ) ? (int) $input[ $key ] : $defaults[ $key ]; $out[ $key ] = max( 0, min( 200, $value ) ); } $color = isset( $input['color'] ) ? sanitize_hex_color( (string) $input['color'] ) : ''; $out['color'] = $color ? strtolower( $color ) : $defaults['color']; $out['features'] = array(); foreach ( array_keys( self::features() ) as $id ) { $out['features'][ $id ] = empty( $input['features'][ $id ] ) ? 0 : 1; } foreach ( array( 'statement_url', 'contact_url' ) as $key ) { $url = isset( $input[ $key ] ) ? trim( (string) $input[ $key ] ) : ''; $out[ $key ] = '' === $url ? '' : esc_url_raw( $url, array( 'http', 'https', 'mailto' ) ); } $text = isset( $input['contact_text'] ) ? sanitize_text_field( (string) $input['contact_text'] ) : ''; $out['contact_text'] = function_exists( 'mb_substr' ) ? mb_substr( $text, 0, 60 ) : substr( $text, 0, 60 ); return $out; } /** * Color de icono (blanco o negro) con mejor contraste sobre el color de fondo. * * @param string $background Color hexadecimal. * @return string */ public static function icon_color( $background ) { return self::contrast_ratio( '#ffffff', $background ) >= self::contrast_ratio( '#000000', $background ) ? '#ffffff' : '#000000'; } /** * Ratio de contraste WCAG entre dos colores hexadecimales. * * @param string $a Color A. * @param string $b Color B. * @return float */ public static function contrast_ratio( $a, $b ) { $la = self::luminance( $a ); $lb = self::luminance( $b ); return ( max( $la, $lb ) + 0.05 ) / ( min( $la, $lb ) + 0.05 ); } /** * Luminancia relativa de un color hexadecimal. * * @param string $hex Color (#rgb o #rrggbb). * @return float */ private static function luminance( $hex ) { $hex = ltrim( (string) $hex, '#' ); if ( 3 === strlen( $hex ) ) { $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; } if ( 6 !== strlen( $hex ) || ! ctype_xdigit( $hex ) ) { return 0.0; } $channels = array(); foreach ( array( 0, 2, 4 ) as $offset ) { $c = hexdec( substr( $hex, $offset, 2 ) ) / 255; $channels[] = ( $c <= 0.03928 ) ? $c / 12.92 : pow( ( $c + 0.055 ) / 1.055, 2.4 ); } return 0.2126 * $channels[0] + 0.7152 * $channels[1] + 0.0722 * $channels[2]; } /** * Crea los ajustes por defecto al activar el plugin (sin pisar los existentes). * * @return void */ public static function activate() { add_option( self::OPTION, self::defaults() ); } }