is_active_for_network = is_plugin_active_for_network(KT_CENTRAL_PALETTE_BASENAME); define('KT_CENTRAL_PALETTE_MULTISITE', $this->is_active_for_network); add_action('plugins_loaded', array($this, 'init_l10n')); add_action('after_setup_theme', array($this, 'init_integrations'), 999); add_filter('plugin_action_links', array($this, 'add_action_link'), 10, 2); add_filter('network_admin_plugin_action_links', array($this, 'add_network_action_link'), 10, 2); add_action($this->is_active_for_network ? 'network_admin_menu' : 'admin_menu', array($this, 'add_settings_page')); $this->update_plugin(); } public function get_option($name, $fallback = null) { return KT_CENTRAL_PALETTE_MULTISITE ? get_site_option($name, $fallback) : get_option($name, $fallback); } public function set_option($name, $value) { return KT_CENTRAL_PALETTE_MULTISITE ? update_site_option($name, $value) : update_option($name, $value); } public function delete_option($name) { return KT_CENTRAL_PALETTE_MULTISITE ? delete_site_option($name) : delete_option($name); } /** * Check for a feature, plugin or theme * @since 1.10 * @param string $key * @return boolean */ protected function supports($key) { switch ($key) { case 'upload': if (function_exists('wp_is_mobile')) { return !(function_exists('_device_can_upload') && !_device_can_upload()); } return true; case 'gutenberg': return $this->version_compare('>=', '5.0'); case 'elementor': return defined('ELEMENTOR_VERSION') && version_compare(ELEMENTOR_VERSION, '3.0', '<'); case 'generatepress': return defined('GENERATE_VERSION') && version_compare(GENERATE_VERSION, '1.3.42', '>='); case 'generatepress-premium': return defined('GP_PREMIUM_VERSION'); case 'oceanwp': return defined('OCEANWP_THEME_VERSION'); case 'beaverbuilder': return defined('FL_BUILDER_VERSION') && version_compare(FL_BUILDER_VERSION, '1.7.6', '>='); case 'astra': return defined('ASTRA_THEME_VERSION') && version_compare(ASTRA_THEME_VERSION, '1.0.23', '>='); case 'page-builder-framework': return defined('WPBF_VERSION'); case 'hestia-theme': return defined('HESTIA_VERSION'); case 'neve-theme': return defined('NEVE_VERSION'); case 'fontpress': return defined('FP_VER') && version_compare(FP_VER, '3.03', '>='); case 'acf': return defined('ACF'); case 'megamaxmenu': return defined('MEGAMENU_VERSION') && version_compare(MEGAMENU_VERSION, '2.7.2', '>='); case 'suki_theme': return defined('SUKI_VERSION') && version_compare(SUKI_VERSION, '1.1.0', '>='); case 'oxygen_vsb': return defined('CT_VERSION'); } return false; } /** * Update procedures * @since 1.6 */ protected function update_plugin() { $version = $this->get_option(self::ACTIVE_VERSION, 0); if ($version == KT_CENTRAL_PALETTE) { return; } while ($version != KT_CENTRAL_PALETTE) { switch ($version) { case 0: $version = 16; $sets = get_option('kt_color_grid_sets', array()); if ($sets) { foreach ($sets as &$set) { $set[0] = str_replace('#', '', $set[0]); } update_option('kt_color_grid_sets', $sets); } break; case 16: case 161: $version = 170; if (get_option('kt_color_grid_custom')) { update_option('kt_color_grid_visual', '1'); } $sets = get_option('kt_color_grid_sets', array()); if ($sets) { update_option('kt_color_grid_palette', $sets); } delete_option('kt_color_grid_custom'); delete_option('kt_color_grid_sets'); break; case 170: case 171: case 172: case 18: case 181: case 19: case 191: case 192: case 193: $version = 1100; $palette = get_option('kt_color_grid_palette', array()); $_palette = array(); foreach ($palette as $set) { $_palette[] = array($set[0], 100, $set[1]); } update_option('kt_color_grid_palette', $_palette); break; case 1100: case 1110: case '1.11': case '1.12': case '1.12.1': case '1.12.2': case '1.12.3': case '1.12.4': case '1.12.5': case '1.12.6': $version = '1.13'; $palette = (array) get_option('kt_color_grid_palette'); $_palette = array(); $index = 1; $status = 1; foreach ($palette as $set) { if (!$set) { continue; } list($color, $alpha, $name) = $set; $color = '#' . ltrim($color, '#'); $_palette[] = compact('color', 'alpha', 'name', 'index', 'status'); $index++; } update_option('kt_color_grid_palette', $_palette); $alpha = 'kt_color_grid_alpha'; if (get_option($alpha)) { update_option('kt_color_grid_oceanwp_alpha', 1); update_option('kt_color_grid_gp_alpha', 1); } delete_option($alpha); # re-render for new data formats $this->render_map(); break; case '1.13': case '1.13.1': case '1.13.2': case '1.13.3': case '1.13.4': case '1.13.5': case '1.13.6': case '1.13.7': case '1.13.8': case '1.13.9': $version = '1.13.10'; # re-render for obligatory "reset color" if (get_option('kt_color_grid_type') == 'palette') { $this->render_map(); } delete_option('kt_color_grid_mce_reset'); break; case '1.13.10': case '1.13.11': $version = '1.14'; $palette = (array) get_option('kt_color_grid_palette'); $_palette = array(); foreach ($palette as $set) { if (!$set) { continue; } $set['hex'] = $set['color']; $set['type'] = 1; // hex $_palette[] = $set; } update_option('kt_color_grid_palette', $_palette); break; case '1.14'; case '1.14.1'; case '1.14.2'; case '1.14.3'; case '1.14.4'; case '1.14.5'; $version = '1.14.6'; update_option('kt_color_grid_astra_theme', 1); if ($this->supports('generatepress')) { update_option('kt_color_grid_gp', 1); } break; case '1.14.7': $version = '1.15'; $palette = (array) get_option('kt_color_grid_palette'); $_palette = array(); foreach ($palette as $set) { if (!$set) { continue; } $set['variable'] = $this->get_css_class_name($set); $_palette[] = $set; } $this->set_option('kt_color_grid_palette', $_palette); break; default: $version = KT_CENTRAL_PALETTE; } } $this->set_option(self::ACTIVE_VERSION, KT_CENTRAL_PALETTE); } /** * Compare against WP version * @global string $wp_version * @param string $operator * @param string $version * @return boolean */ protected function version_compare($operator, $version) { global $wp_version; return version_compare($wp_version, $version, $operator); } protected function get_css_class_name($color) { static $duplicates = array(); $name = sanitize_html_class($color['name'], 'central-palette-' . $color['index']); if (isset($duplicates[$name])) { $i = $duplicates[$name]; $duplicates[$name] += 1; $name .= "-$i"; } else { $duplicates[$name] = 1; } return $name; } /** * Load translation for older WP versions * @since 1.11 */ public function init_l10n() { // load_plugin_textdomain is obsolete since WordPress 4.6 if ($this->version_compare('<', '4.6')) { load_plugin_textdomain('kt-tinymce-color-grid'); } } /** * Init integration * @since 1.11 */ public function init_integrations() { add_filter('tiny_mce_before_init', array($this, 'tinymce_integration'), 10, 2); add_action('after_wp_tiny_mce', array($this, 'print_tinymce_style')); $iris_integration = array($this, 'iris_integration'); $integrate_customizer = $this->get_option(self::CUSTOMIZER); if ($integrate_customizer) { add_action('admin_enqueue_scripts', array($this, 'iris_enqueue_script')); add_action('admin_print_scripts', $iris_integration, 999); add_action('admin_print_footer_scripts', $iris_integration, 999); add_action('customize_controls_print_scripts', $iris_integration, 999); add_action('customize_controls_print_footer_scripts', $iris_integration, 999); } if ($this->supports('acf') && $this->get_option(self::ACF)) { add_action('admin_enqueue_scripts', array($this, 'iris_enqueue_script')); add_action('acf/admin_print_scripts', $iris_integration, 999); } if ($this->supports('suki_theme') && $this->get_option(self::SUKI_THEME)) { add_action('suki/admin/dashboard/content', array($this, 'suki_hide_color_module'), 11); add_action('customize_register', array($this, 'suki_remove_color_section'), 11); for ($i = 1; $i < 9; $i++) { add_filter("suki/customizer/setting_value/color_palette_{$i}", array($this, 'suki_color_palette_overwrite')); } } if ($this->get_option(self::CSS_VARS)) { add_action('wp_print_styles', array($this, 'print_global_css_variables')); } if ($this->get_option(self::CSS_ADMIN_VARS)) { add_action('admin_print_styles', array($this, 'print_global_css_variables')); } if ($this->supports('elementor') && $this->get_option(self::ELEMENTOR)) { add_filter('elementor/editor/localize_settings', array($this, 'elementor_integration'), 100); if (version_compare(ELEMENTOR_VERSION, '2.8', '<')) { add_action('elementor/editor/after_enqueue_scripts', array($this, 'elementor_styles')); add_action('elementor/editor/after_enqueue_scripts', array($this, 'iris_enqueue_script')); add_action('elementor/editor/after_enqueue_styles', array($this, 'print_iris_style')); } } if ($this->supports('generatepress') && $this->get_option(self::GENERATEPRESS)) { add_filter('generate_default_color_palettes', array($this, 'generatepress_integration')); add_action('admin_enqueue_scripts', array($this, 'iris_enqueue_script')); } if ($this->supports('oceanwp') && $this->get_option(self::OCEANWP)) { add_filter('ocean_default_color_palettes', array($this, 'oceanwp_integration')); add_action('admin_enqueue_scripts', array($this, 'iris_enqueue_script')); } if ($this->supports('beaverbuilder') && $this->get_option(self::BEAVERBUILDER)) { add_filter('fl_builder_color_presets', array($this, 'beaver_integration')); add_action('wp_enqueue_scripts', array($this, 'beaver_style')); } if ($this->supports('gutenberg') && $this->get_option(self::GUTENBERG)) { $this->integrate_gutenberg(); } if ($this->supports('hestia-theme') && $integrate_customizer) { add_filter('hestia_accent_color_palette', array($this, 'hestia_integration')); } if ($this->supports('fontpress') && $this->get_option(self::FONTPRESS)) { add_action('admin_enqueue_scripts', array($this, 'fontpress_enqueue_scripts')); } if ($this->supports('megamaxmenu') && $this->get_option(self::MEGAMAXMENU)) { add_filter('megamenu_spectrum_localisation', array($this, 'megamaxmenu_integration')); } if ($this->supports('astra') && $this->get_option(self::ASTRA_THEME)) { if (version_compare(ASTRA_THEME_VERSION, '2.6.0', '<')) { add_filter('astra_color_palettes', array($this, 'astra_legacy_integration')); add_action('admin_enqueue_scripts', array($this, 'iris_enqueue_script')); } else { add_filter('astra_color_palettes', array($this, 'astra_integration')); } } if ($this->supports('oxygen_vsb') && $this->get_option(self::OXYGEN_VSB)) { add_filter('pre_option_oxygen_vsb_global_colors', array($this, 'oxygen_vsb_integration')); add_action('wp_enqueue_scripts', array($this, 'oxygen_vsb_enqueue_scripts')); } } public function oxygen_vsb_enqueue_scripts() { if (defined('SHOW_CT_BUILDER')) { wp_enqueue_style(self::KEY . '-oxygen_vsb', KT_CENTRAL_PALETTE_URL . 'integration/oxygen-vsb/style.css'); } } public function oxygen_vsb_integration() { $oxygen_palette = array( "colorsIncrement" => 0, "setsIncrement" => 1, "colors" => array(), "sets" => array( array( "id" => 0, "name" => __('Central Palette', 'kt-tinymce-color-grid'), ), ), ); $palette = $this->get_palette(array( 'alpha' => true, 'status' => self::COLOR_ACTIVE, )); foreach ($palette as $set) { $rgba = $this->hex2rgba($set['hex'], $set['alpha']); $oxygen_palette['colors'][] = [ 'id' => $set['index'], 'name' => $set['name'], 'value' => $rgba, 'sourceVal' => $rgba, 'set' => 0, ]; } return $oxygen_palette; } public function suki_remove_color_section() { global $wp_customize; #$wp_customize->remove_section('suki_section_color_palette'); } public function suki_color_palette_overwrite($value) { $palette = $this->get_colors(); $i = $this->preg_get('~color_palette_(\d+)$~', current_filter(), 0) - 1; if (!isset($palette[$i])) { return $value; } return $palette[$i]; } public function suki_hide_color_module() { #print ''; } public function print_global_css_variables() { static $printed = false; if ($printed) { return; } $printed = true; $palette = $this->get_palette(array( 'status' => self::COLOR_ACTIVE, )); if (!$palette) { return; } print " '; } public function megamaxmenu_integration($spectrum) { $spectrum['palette'] = $this->get_colors(); return $spectrum; } /** * FontPress scripts * @since 1.13.11 */ public function fontpress_enqueue_scripts() { $screen = get_current_screen(); if (!$screen) { return; } // enqueue scripts for the rule manager and any post/page editor if ($screen->id == 'toplevel_page_fp_settings' || $screen->base == 'post') { wp_enqueue_style(self::KEY . '-colpicker', KT_CENTRAL_PALETTE_URL . 'integration/fontpress/colpicker.css', array('fp-colpick'), KT_CENTRAL_PALETTE); wp_enqueue_script(self::KEY . '-colpicker', KT_CENTRAL_PALETTE_URL . 'integration/fontpress/colpicker.js', array('jquery'), KT_CENTRAL_PALETTE, true); wp_localize_script(self::KEY . '-colpicker', 'kt_fontpress_palette', $this->get_palette(array( 'status' => self::COLOR_ACTIVE, ))); } } /** * Integrate with Hestia Theme * @since 1.13.3 * @param bool|array $palette * @return bool|array */ public function hestia_integration($palette) { return $this->get_colors(array( 'default' => $palette, )); } protected function integrate_gutenberg() { $palette = $this->get_gutenberg_palette(); if ($palette) { if ($this->get_option(self::GUTENBERG_MERGE)) { $existing_palette = get_theme_support('editor-color-palette'); if (is_array($existing_palette)) { $palette = array_merge(reset($existing_palette), $palette); } } // 8 is just after wp_print_styles, 99 just before wp_custom_css_cb $priority = $this->get_option(self::GUTENBERG_FORCE) ? 99 : 8; add_theme_support('editor-color-palette', $palette); add_action('wp_head', array($this, 'print_gutenberg_style'), $priority); add_action('admin_print_styles', array($this, 'print_gutenberg_style'), 21); } } public function print_gutenberg_style() { if (is_admin()) { $screen = get_current_screen(); if (!$screen || !$screen->is_block_editor()) { return; } } $palette = $this->get_gutenberg_palette(); if (!$palette) { return; } $debug = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG; $force = $this->get_option(self::GUTENBERG_FORCE) ? ' !important' : ''; print ' '; } protected function get_gutenberg_palette() { $palette = array(); $_palette = $this->get_palette(array( 'status' => self::COLOR_ACTIVE, )); foreach ($_palette as $set) { $name = $set['name']; if ($name === '') { $name = $set['hex']; } $palette[] = array( 'color' => $set['hex'], 'name' => $name, 'slug' => 'central-palette-' . $set['index'], ); } return $palette; } public function beaver_style() { if (class_exists('FLBuilderModel') && FLBuilderModel::is_builder_active()) { wp_enqueue_style(self::KEY . '-beaver', KT_CENTRAL_PALETTE_URL . 'integration/beaver/beaver.css', null, KT_CENTRAL_PALETTE); } } public function beaver_integration() { return $this->get_colors(array( 'hash' => false, )); } /** * Get palette * @since 1.13 * @param string|array $options * @return array */ public function get_palette($options = '') { $options = wp_parse_args($options, array( '_palette' => null, 'status' => array(self::COLOR_ACTIVE, self::COLOR_INACTIVE), 'chunk' => false, 'pad' => array( 'hex' => '#FFFFFF', 'color' => '#FFFFFF', 'alpha' => 100, 'name' => '', 'index' => '', 'type' => 1, 'status' => self::COLOR_ACTIVE, ), )); $status = false; if ($options['status']) { $status = $options['status']; if (!is_array($status)) { $status = array($status); } } $_palette = $options['_palette']; if (!is_array($_palette)) { $_palette = (array) $this->get_option(self::PALETTE); } $palette = array(); foreach ($_palette as $color) { if (!$color) { continue; } if (!($status === false || in_array($color['status'], $status))) { continue; } $palette[] = $color; } if (count($palette) && $options['chunk'] > 0) { $chunk = (int) $options['chunk']; $palette = array_chunk($palette, $chunk); $last = count($palette) - 1; $palette[$last] = array_pad($palette[$last], $chunk, $options['pad']); } return $palette; } /** * Set the palette. * @since 1.13.3 * @param null|array $new_palette * @param bool|float $merge_threshold */ public function set_palette($new_palette, $merge_threshold = .25) { if (!is_array($new_palette)) { return array(); } $palette = array(); $next_index = 1; $type = 1; // hex $statii = array(self::COLOR_ACTIVE, self::COLOR_INACTIVE); foreach ($new_palette as $data) { if (is_string($data)) { $data = array('color' => $data); } if (!is_array($data) || !isset($data['color'])) { continue; } $color = $this->sanitize_color($data['color']); if ($color === false) { continue; } $hex = $color; $name = isset($data['name']) ? $this->sanitize_textfield($data['name']) : ''; $alpha = isset($data['alpha']) ? $this->sanitize_alpha($data['alpha']) : 100; $index = 0; if ($merge_threshold === false) { if (isset($data['index']) && is_numeric($data['index'])) { $index = intval($data['index']); } else { $index = $next_index++; } } $status = self::COLOR_ACTIVE; if (isset($data['status']) && in_array($data['status'], $statii)) { $status = $data['status']; } $palette[] = compact('color', 'hex', 'name', 'alpha', 'index', 'status', 'type'); } if ($merge_threshold !== false) { $palette = $this->merge_palette($palette, $merge_threshold); } $this->set_option(self::PALETTE, $palette); $this->render_map(); return $palette; } /** * Get the color palette * @since 1.11 * @since 1.13 Introduced $options argument * @param string|array $options * @return array */ public function get_colors($options = '') { $options = wp_parse_args($options, array( 'alpha' => false, 'min' => false, 'pad' => '#FFFFFF', 'hash' => true, 'default' => array(), '_force_alpha' => false, '_name' => false, )); $palette = array(); $_palette = $this->get_palette(array( 'status' => self::COLOR_ACTIVE, )); foreach ($_palette as $set) { $color = $set['hex']; if ($options['_force_alpha'] || $options['alpha']) { $color = $this->hex2rgba($color, $set['alpha'], $options['_force_alpha']); } if (!$options['hash']) { $color = ltrim($color, '#'); } if ($options['_name']) { $color = array( 'color' => $color, 'name' => $set['name'], ); } $palette[] = $color; } if ($options['min'] > 0) { $pad = $options['pad']; if ($options['_name']) { $pad = array('color' => $pad, 'name' => ''); } $palette = array_pad($palette, $options['min'], $pad); } if (!count($palette) && is_array($options['default'])) { $palette = $options['default']; } return $palette; } /** * GeneratePress integration * @since 1.10 * @param array $palette * @return array */ public function generatepress_integration($palette) { if (doing_action('customize_register')) { // for GeneratePress_Alpha_Color_Customize_Control return $this->get_colors(array( 'alpha' => $this->supports('generatepress-premium') && $this->get_option(self::GENERATEPRESS_ALPHA), 'default' => $palette, )); } return $palette = $this->get_colors(array( 'default' => $palette, '_name' => true, )); } /** * OceanWP integration * @since 1.11 * @param array $palette * @return array */ public function oceanwp_integration($palette) { return $this->get_colors(array( 'alpha' => $this->get_option(self::OCEANWP_ALPHA), '_name' => true, 'default' => $palette, )); } /** * Enqueue a stylesheet for Elementor * @since 1.10 */ public function elementor_styles() { #wp_enqueue_style(self::KEY . '-elementor', KT_CENTRAL_PALETTE_URL . 'integration/elementor/elementor.css', null, KT_CENTRAL_PALETTE); } /** * Elementor integration * @since 1.10 * @param array $config * @return array */ public function elementor_integration($config) { if (!isset($config['schemes'])) { $config['schemes'] = array('items' => array()); } if (!isset($config['schemes']['items']['color-picker'])) { $config['schemes']['items']['color-picker'] = array('items' => array()); } $config['schemes']['items']['color-picker']['items'] = $this->elementor_palette(); return $config; } /** * Prepare colors for Elementor * @since 1.10 * @return array */ public function elementor_palette() { $colors = $this->get_colors(array( 'pad' => '#FFF', 'min' => 6, 'alpha' => true, '_force_alpha' => true, '_name' => true, )); $palette_version = false; if (version_compare(ELEMENTOR_VERSION, '2.8', '>=')) { $palette_version = '2.8'; } $palette = array(); for ($i = 1, $n = count($colors); $i <= $n; $i++) { switch ($palette_version) { case '2.8': $palette[$i] = array('value' => $colors[$i - 1]['color']); break; default: $palette[$i] = array('value' => $colors[$i - 1]); } } return $palette; } public function astra_integration($palette) { return $this->get_colors(array( '_force_alpha' => $this->get_option(self::ASTRA_ALPHA), 'default' => $palette, )); } public function astra_legacy_integration($palette) { return $this->get_colors(array( '_force_alpha' => $this->get_option(self::ASTRA_ALPHA), '_name' => true, 'default' => $palette, )); } public function iris_enqueue_script() { wp_enqueue_script(self::KEY . '-iris', KT_CENTRAL_PALETTE_URL . 'js/iris.js', array('iris'), KT_CENTRAL_PALETTE); } /** * wpColorPicker/Iris integration * @since 1.7 */ public function iris_integration() { static $printed = false; if ($printed || !wp_script_is('iris', 'done')) { return; } $printed = true; $colors = $this->get_colors(array( 'pad' => '#FFF', 'min' => 6, '_name' => true, )); if (!$colors) { return; } $this->print_iris_style(); $colors = json_encode($colors); print ' '; } public function print_iris_style() { static $printed = false; if ($printed) { return; } $printed = true; $n = count($this->get_colors()); if (!$n) { return; } $padding_bottom = (ceil($n / 8) * 23); print ' '; } /** * Add dynamic CSS for TinyMCE * @since 1.3 * @since 1.12.3 Change css selector */ public function print_tinymce_style() { if ($this->get_option(self::TYPE, self::DEFAULT_TYPE) == 'default') { return; } $map = $this->get_option(self::MAP); if (!is_array($map) || !$map['rows']) { return; } $rows = $map['rows']; print ""; } /** * Pass color map to TinyMCE * @since 1.3 * @since 1.13 Gutenberg support * @param array $init Wordpress' TinyMCE inits * @param string $id Id of TinyMCE instance * @return array */ public function tinymce_integration($init, $id) { // Gutenberg uses 'editor' as id and wp_localize_script which // does JSON encoding for us, so we return an array. // For any other id we asume a classic editor and return a string. $type = $this->get_option(self::TYPE, self::DEFAULT_TYPE); if ($type == 'default') { return $init; } $mce = $this->get_option(self::MAP); if (is_array($mce) && $mce['rows']) { if ($id != 'editor') { $map = array_map('esc_js', $mce['map']); $mce['map'] = '["' . implode('","', $map) . '"]'; } $init['textcolor_map'] = $mce['map']; $init['textcolor_cols'] = $mce['columns']; $init['textcolor_rows'] = $mce['rows']; } return $init; } /** * Add a link to the plugin listing * @since 1.4 * @param array $links Array holding HTML * @param string $file Current name of plugin file * @return array Modified array */ public function add_action_link($links, $file) { if (!current_user_can($this->capability) || plugin_basename($file) != KT_CENTRAL_PALETTE_BASENAME) { return $links; } $href = $this->is_active_for_network ? 'network/settings.php' : 'options-general.php'; $links[] = ' ' . esc_html__('Color Palette', 'kt-tinymce-color-grid') . ''; return $links; } public function add_network_action_link($links, $file) { if (!$this->is_active_for_network || !current_user_can($this->capability) || plugin_basename($file) != KT_CENTRAL_PALETTE_BASENAME) { return $links; } $links[] = ' ' . esc_html__('Color Palette', 'kt-tinymce-color-grid') . ''; return $links; } /** * Add settings page to WordPress' admin menu * @since 1.3 */ public function add_settings_page() { $name = __('Central Color Palette', 'kt-tinymce-color-grid'); $parent = $this->is_active_for_network ? 'settings.php' : 'options-general.php'; $this->capability = $this->is_active_for_network ? 'manage_network_options' : 'manage_options'; $hook = add_submenu_page($parent, $name, $name, $this->capability, self::KEY, array($this, 'print_settings_page')); add_action("load-$hook", array($this, 'init_settings_page')); } /** * Add removable query arguments for this plugin * @since 1.8 * @param array $args * @return array */ public function add_removeable_args($args) { $args[] = 'kt-import-error'; $args[] = 'kt-export-error'; return $args; } /** * Initialize settings page * @since 1.4.4 */ public function init_settings_page() { $this->native_palette_support = array( 'page-builder-framework' => __('Page Builder Framework', 'kt-tinymce-color-grid'), 'hestia-theme' => __('Hestia Theme', 'kt-tinymce-color-grid'), 'neve-theme' => __('Neve Theme', 'kt-tinymce-color-grid'), ); $this->export_formats = array( 'base64' => array( 'extension' => 'bak', 'name' => __('Backup', 'kt-tinymce-color-grid'), 'form' => 'json', 'export' => 'base64', ), 'json' => array( 'extension' => 'json', 'name' => __('JSON', 'kt-tinymce-color-grid'), 'form' => 'json', 'export' => 'json', ), 'css' => array( 'extension' => 'css', 'name' => __('CSS', 'kt-tinymce-color-grid'), 'form' => 'css', 'export' => 'css', ), 'css-vars' => array( 'extension' => 'css', 'name' => __('CSS Variables', 'kt-tinymce-color-grid'), 'form' => 'css_vars', 'export' => 'css_vars', ), 'scss-partial' => array( 'extension' => 'scss', 'prefix' => '_', 'name' => __('SCSS Partial', 'kt-tinymce-color-grid'), 'form' => 'css', 'export' => 'css', ), 'scss-vars' => array( 'extension' => 'scss', 'prefix' => '_', 'name' => __('SCSS Variables', 'kt-tinymce-color-grid'), 'form' => 'scss', 'export' => 'scss', ), ); add_action('admin_enqueue_scripts', array($this, 'enqueue_settings_scripts')); add_filter('removable_query_args', array($this, 'add_removeable_args')); add_action('kt_add_luma_transformation', array($this, 'default_luma_transformations')); /** * Register luma transformations * @since 1.9 */ do_action('kt_add_luma_transformation'); $this->save_settings(); $this->add_help(); $this->add_metaboxes(); } /** * Enqueue JavaScript and CSS files * @since 1.3 */ public function enqueue_settings_scripts() { /** * Name that Color JavaScript * @author Chirag Mehta * @link http://chir.ag/projects/ntc/ * @license http://creativecommons.org/licenses/by/2.5/ Creative Commons Attribution 2.5 */ wp_enqueue_script(self::KEY . '_ntc', KT_CENTRAL_PALETTE_URL . "js/ntc.js", null, '1.1'); wp_enqueue_script(self::KEY . '_common', KT_CENTRAL_PALETTE_URL . 'js/common.js', null, KT_CENTRAL_PALETTE); wp_enqueue_script(self::KEY . '_color', KT_CENTRAL_PALETTE_URL . 'js/color.js', null, KT_CENTRAL_PALETTE); wp_enqueue_script(self::KEY . '_picker', KT_CENTRAL_PALETTE_URL . 'js/picker.js', array('wp-util', 'jquery-ui-position'), KT_CENTRAL_PALETTE); wp_enqueue_script(self::KEY . '_editor', KT_CENTRAL_PALETTE_URL . 'js/editor.js', array('wp-util', 'postbox', 'jquery-ui-sortable'), KT_CENTRAL_PALETTE); wp_localize_script(self::KEY . '_editor', 'kt_central_palette_entries', $this->get_palette()); wp_enqueue_style(self::KEY, KT_CENTRAL_PALETTE_URL . 'css/admin.css', null, KT_CENTRAL_PALETTE); } /** * Add metaboxes to settings page * @since 1.9 */ protected function add_metaboxes() { $boxes = array( 'palette' => __('Color Palette', 'kt-tinymce-color-grid'), 'grid' => __('Classic Editor: Color Picker', 'kt-tinymce-color-grid'), 'backup' => __('Backup', 'kt-tinymce-color-grid'), ); foreach ($boxes as $key => $title) { add_meta_box("kt_{$key}_metabox", $title, array($this, "print_{$key}_metabox")); } } /** * Sanitize and saves settings * @since 1.7 */ protected function save_settings() { if (!wp_verify_nonce($this->get_request('kt_settings_nonce'), self::NONCE)) { return; } $action = $this->get_request('kt_action', $this->get_request('kt_hidden_action')); $type = $this->get_request('kt_type'); if (!in_array($type, $this->types)) { $type = self::DEFAULT_TYPE; } $visual = $type == 'palette' || $this->get_request('kt_visual') ? '1' : false; $booleans = array( 'kt_customizer' => self::CUSTOMIZER, 'kt_elementor' => self::ELEMENTOR, 'kt_oxygen_vsb' => self::OXYGEN_VSB, 'kt_css_vars' => self::CSS_VARS, 'kt_css_admin_vars' => self::CSS_ADMIN_VARS, 'kt_acf' => self::ACF, 'kt_astra' => self::ASTRA_THEME, 'kt_astra_alpha' => self::ASTRA_ALPHA, 'kt_suki_theme' => self::SUKI_THEME, 'kt_fontpress' => self::FONTPRESS, 'kt_megamaxmenu' => self::MEGAMAXMENU, 'kt_generatepress' => self::GENERATEPRESS, 'kt_generatepress_alpha' => self::GENERATEPRESS_ALPHA, 'kt_oceanwp' => self::OCEANWP, 'kt_oceanwp_alpha' => self::OCEANWP_ALPHA, 'kt_beaverbuilder' => self::BEAVERBUILDER, 'kt_gutenberg' => self::GUTENBERG, 'kt_gutenberg_merge' => self::GUTENBERG_MERGE, 'kt_gutenberg_force' => self::GUTENBERG_FORCE, ); foreach ($booleans as $field => $option) { $this->set_option($option, $this->get_request($field) ? '1' : false); } $palette_saved = $this->save_palette(); if (!$palette_saved && $type == 'palette') { $type = 'default'; $visual = ''; } $this->set_option(self::TYPE, $type); $this->set_option(self::VISUAL, $visual); $lumas = array('linear') + $this->get_luma_transformations('ids'); $this->set('kt_rows', $this->rows, self::ROWS, self::DEFAULT_ROWS); $this->set('kt_cols', $this->columns, self::COLS, self::DEFAULT_COLS); $this->set('kt_luma', $lumas, self::LUMA, self::DEFAULT_LUMA); $this->set('kt_blocks', $this->blocks, self::BLOCKS, self::DEFAULT_BLOCKS); $this->set('kt_block_size', $this->sizes, self::SIZE, self::DEFAULT_SIZE); $this->set('kt_axis', $this->axes, self::AXIS, self::DEFAULT_AXIS); $this->set('kt_spread', $this->spread, self::SPREAD, self::DEFAULT_SPREAD); $this->set('kt_clamp', $this->clamp, self::CLAMP, self::DEFAULT_CLAMP); $clamps = intval($this->get_request('kt_clamps')); if ($clamps < 4 || $clamps > 18) { $clamps = self::DEFAULT_CLAMPS; } $this->set_option(self::CLAMPS, $clamps); list($error, $action) = $this->handle_backup($action); if (!$error || $error == 'ok') { $this->render_map(); } if ($error) { $url = add_query_arg("kt-{$action}-error", $error); wp_redirect($url); exit; } wp_redirect(add_query_arg('updated', $action == 'save' ? '1' : false)); exit; } /** * Save the palette from form data * @since 1.13.3 * @return boolean */ protected function save_palette() { $palette = array(); $next_index = $this->get_option(self::NEXT_INDEX, 1); $data = (array) $this->get_request('kt_palette'); if (isset($data['hex']) && is_array($data['hex'])) { foreach ($data['hex'] as $i => $hex) { $hex = $this->sanitize_color($hex); if (!$hex) { continue; } $color = $this->sanitize_textfield($data['color'][$i]); $alpha = $this->sanitize_alpha($data['alpha'][$i]); $name = $this->sanitize_textfield($data['name'][$i]); $variable = $this->sanitize_textfield($data['variable'][$i]); $index = $data['index'][$i]; if (!is_numeric($index) || $index < 1) { $index = $next_index; $next_index++; } $type = abs(intval($data['type'][$i])); $status = abs(intval($data['status'][$i])); $default_variable = $name === '' ? 'central-palette-' . $index : $name; $variable = sanitize_html_class($variable, $default_variable); $palette[] = compact('color', 'hex', 'name', 'variable', 'alpha', 'index', 'status', 'type'); } } $this->set_option(self::NEXT_INDEX, $next_index); $this->set_option(self::PALETTE, $palette); return count($palette) > 0; } protected function handle_backup($action) { $error = false; switch ($action) { case 'export': $error = $this->download_export(); break; case 'import': $error = $this->import_backup(); break; default: $action = 'save'; } return array($error, $action); } /** * Debug function * @since 1.13 * @ignore * @param mixed $x * @param bool $exit */ public function xmp($x, $exit = true) { print ''; print_r($x); print ''; if ($exit) { exit; } } protected function sanitize_textfield($string) { return sanitize_text_field(stripslashes($string)); } /** * Return all options as an array * @since 1.8 * @since 1.9 Partial options * @param $parts * @return array */ protected function default_options($parts = null) { $options = array( self::ACTIVE_VERSION => KT_CENTRAL_PALETTE ); $settings = array( self::VISUAL => false, self::CUSTOMIZER => false, self::CSS_VARS => false, self::CSS_ADMIN_VARS => false, self::ACF => false, self::ASTRA_THEME => false, self::ASTRA_ALPHA => false, self::SUKI_THEME => false, self::ELEMENTOR => false, self::OXYGEN_VSB => false, self::FONTPRESS => false, self::MEGAMAXMENU => false, self::OCEANWP => false, self::OCEANWP_ALPHA => false, self::GENERATEPRESS => false, self::GENERATEPRESS_ALPHA => false, self::BEAVERBUILDER => false, self::GUTENBERG => false, self::GUTENBERG_MERGE => false, self::GUTENBERG_FORCE => false, self::TYPE => self::DEFAULT_TYPE, self::ROWS => self::DEFAULT_ROWS, self::COLS => self::DEFAULT_COLS, self::LUMA => self::DEFAULT_LUMA, self::BLOCKS => self::DEFAULT_BLOCKS, self::SIZE => self::DEFAULT_SIZE, self::AXIS => self::DEFAULT_AXIS, self::SPREAD => self::DEFAULT_SPREAD, self::CLAMP => self::DEFAULT_CLAMP, self::CLAMPS => self::DEFAULT_CLAMPS ); $palette = array( self::PALETTE => array(), ); if ($parts) { foreach ($parts as $part) { switch ($part) { case 'settings': $options += $settings; break; case 'palette': $options += $palette; break; } } return $options; } return $options + $settings + $palette; } /** * Import settings from file upload * @since 1.8 * @return string */ protected function import_backup() { if (isset($_FILES['kt_upload'])) { $file = $_FILES['kt_upload']; $status = $this->verify_upload($file); if ($status != 'ok') { return $status; } $payload = file_get_contents($file['tmp_name']); } else if (isset($_REQUEST['kt_raw_upload'])) { $payload = $_REQUEST['kt_raw_upload']; } else { return 'no-import'; } $options = null; if ($this->import_is_json($payload)) { $options = json_decode($payload, true); } else if ($this->import_is_base64($payload)) { $payload = base64_decode(substr($payload, 0, -8)); $options = json_decode($payload, true); } else { return 'format'; } if (!is_array($options)) { return 'funny'; } if (!isset($options[self::ACTIVE_VERSION])) { $options[self::ACTIVE_VERSION] = 180; } $this->update_import($options); $options[self::PALETTE] = $this->merge_palette($options[self::PALETTE]); $names = array_keys($this->default_options()); foreach ($names as $name) { if (isset($options[$name])) { $this->set_option($name, $options[$name]); } } return 'ok'; } protected function import_is_json($payload) { return substr($payload, 0, 1) == '{' && substr($payload, -1) == '}'; } protected function import_is_base64($payload) { return dechex(crc32(substr($payload, 0, -8))) == substr($payload, -8); } /** * Tries to reuse existing colors and their indices during an import * @since 1.13 * @since 1.13.3 Added $threshold parameter * @param array $palette * @param bool|float $threshold * @return array */ protected function merge_palette($palette, $threshold = .25) { $next_index = $this->get_option(self::NEXT_INDEX, 1); $current_palette = (array) $this->get_option(self::PALETTE); if ($threshold === true) { $threshold = .25; } if ($threshold === false) { $threshold = 0; } foreach ($palette as $i => $new) { $shortest_distance = $threshold; $reuse = false; $new_color = strtoupper($new['hex']); $new_rgb = $this->hex2rgb($new_color); foreach ($current_palette as $j => $current) { if (!$current) { continue; } // excact match, we're done $current_color = strtoupper($current['hex']); if ($new['alpha'] == $current['alpha'] && $new_color == $current_color) { $reuse = $j; break; } // too different in transparency, skip if (5 < abs($new['alpha'] - $current['alpha'])) { continue; } if (!isset($current['rgb'])) { $current['rgb'] = $this->hex2rgb($current['hex']); } $distance = $this->rgb_distance($new_rgb, $current['rgb']); if ($distance < $shortest_distance) { $shortest_distance = $distance; $reuse = $j; } } if ($reuse !== false) { $palette[$i]['index'] = $current_palette[$reuse]['index']; // make sure we don't change the status of the color or active // colors may disappear or inactive colors reappear inside the block editor $palette[$i]['status'] = $current_palette[$reuse]['status']; $current_palette[$reuse] = null; } else { $palette[$i]['index'] = $next_index; $next_index++; } } // pluck out all remaining inactive colors and add them to the import $inactives = array(); foreach ($current_palette as $current) { if (!$current || $current['status'] != self::COLOR_INACTIVE) { continue; } $inactives[] = $current; } $palette = array_merge($inactives, $palette); $this->set_option(self::NEXT_INDEX, $next_index); return $palette; } /** * Calculate euclidean distance between two RGB vectors * @param array $a * @param array $b * @return float [0..100] */ public function rgb_distance($a, $b) { return (pow($a[0] - $b[0], 2) + pow($a[1] - $b[1], 2) + pow($a[2] - $b[2], 2)) / 1950.75; } /** * Check a file upload * @since 1.9 * @param array $file Element of $_FILES * @return string Status code */ protected function verify_upload($file) { $upload_error = array( UPLOAD_ERR_INI_SIZE => 'size-php', UPLOAD_ERR_FORM_SIZE => 'size', UPLOAD_ERR_PARTIAL => 'partially', UPLOAD_ERR_NO_FILE => 'no-upload', UPLOAD_ERR_NO_TMP_DIR => 'tmp', UPLOAD_ERR_CANT_WRITE => 'fs', UPLOAD_ERR_EXTENSION => 'ext', ); if (!isset($file['error'])) { $file['error'] = UPLOAD_ERR_NO_FILE; } if (isset($upload_error[$file['error']])) { return $upload_error[$file['error']]; } if (!is_uploaded_file($file['tmp_name'])) { return 'no-upload'; } $size = filesize($file['tmp_name']); if (!$size) { return 'empty'; } if ($size > self::MAX_FILE_SIZE) { return 'size'; } return 'ok'; } /** * Update procedures for export/import file * @since 1.9 * @param array $options passed by reference */ protected function update_import(&$options) { $version = $options[self::ACTIVE_VERSION]; unset($options[self::ACTIVE_VERSION]); while ($version != KT_CENTRAL_PALETTE) { switch ($version) { case 19: case 191: case 192: case 193: $version = 1100; $key = 'kt_color_grid_palette'; if (!is_array($options[$key])) { break; } $palette = array(); foreach ($options[$key] as $set) { $palette[] = array($set[0], 100, $set[1]); } $options[$key] = $palette; break; case 1100: case 1110: case '1.11': case '1.12': case '1.12.1': case '1.12.2': case '1.12.3': case '1.12.4': case '1.12.5': case '1.12.6': $version = '1.13'; $index = 0; $key = 'kt_color_grid_palette'; if (!is_array($options[$key])) { break; } $palette = array(); foreach ($options[$key] as $set) { list($color, $alpha, $name) = $set; $color = '#' . ltrim($color, '#'); $index++; $status = self::COLOR_ACTIVE; $palette[] = compact('color', 'alpha', 'name', 'index', 'status'); } $options[$key] = $palette; $alpha = 'kt_color_grid_alpha'; if (isset($options[$alpha]) && $options[$alpha]) { $options['kt_color_grid_oceanwp_alpha'] = 1; $options['kt_color_grid_gp_alpha'] = 1; } unset($options[$alpha]); break; case '1.13': case '1.13.1': case '1.13.2': case '1.13.3': case '1.13.4': case '1.13.5': case '1.13.6': case '1.13.7': case '1.13.8': case '1.13.9': case '1.13.10': case '1.13.11': $version = '1.14'; $key = 'kt_color_grid_palette'; if (!is_array($options[$key])) { break; } for ($i = 0, $l = count($options[$key]); $i < $l; $i++) { $options[$key][$i]['hex'] = $options[$key][$i]['color']; $options[$key][$i]['type'] = 1; // hex; } break; case '1.14': case '1.14.1': case '1.14.2': case '1.14.3': case '1.14.4': case '1.14.5': $version = '1.14.6'; $key = 'kt_color_grid_astra_theme'; $options[$key] = '1'; break; case '1.14.6': case '1.14.7': $version = '1.15'; $key = 'kt_color_grid_palette'; if (!is_array($options[$key])) { break; } for ($i = 0, $l = count($options[$key]); $i < $l; $i++) { if (!$options[$key][$i]) { continue; } $options[$key][$i]['variable'] = $this->get_css_class_name($options[$key][$i]); } break; default: $version = KT_CENTRAL_PALETTE; } } } /** * Export settings and trigger a file download * @since 1.13 * @return string */ protected function download_export() { $id = $this->get_request('kt_export_format'); if (!isset($this->export_formats[$id])) { return 'format'; } $fn = $this->export_formats[$id]['export']; $payload = call_user_func(array($this, "export_$fn")); if (!$payload) { return 'empty'; } if (is_wp_error($payload)) { return $payload->get_error_code(); } $filename = $this->get_export_filename($id); header('Content-Type: plain/text'); header('Content-Disposition: attachment; filename="' . $filename . '"'); print $payload; exit; } /** * Get file name for export file download * @since 1.13 * @return string The filename */ protected function get_export_filename($id) { $format = $this->export_formats[$id]; $ext = $format['extension']; $prefix = isset($format['prefix']) ? $format['prefix'] : ''; $name = get_bloginfo('name'); $suffix = sanitize_file_name(str_replace('"', '', $name)); if ($suffix) { $suffix = "_$suffix"; } return "{$prefix}central-palette{$suffix}.{$ext}"; } public function export_base64() { $json = $this->export_json(); if (is_wp_error($json)) { return $json; } $base64 = base64_encode($json); if (!$base64) { return new WP_Error('base64'); } return $base64 . dechex(crc32($base64)); } public function export_json() { $parts = $this->get_request('kt_export_parts'); if (!is_array($parts) || empty($parts)) { return new WP_Error('no-parts'); } $options = $this->default_options($parts); foreach ($options as $name => $default) { $options[$name] = $this->get_option($name, $default); } $json = json_encode($options); if (!$json) { return new WP_Error('json'); } return $json; } public function export_css() { return $this->get_export('css', array( 'line' => ".%1 {\n color: %2;\n}\n", 'compat' => ".%1 {\n color: %2;\n color: %3;\n}\n", )); } public function export_css_vars() { return $this->get_export('css_vars', array( 'wrap' => "%2 {%1\n}", 'line' => "\n --%1: %2;", 'compat' => "\n --%1: %2;\n --%1: %3;", 'glue' => '', 'compat_glue' => "\n", )); } public function export_scss() { return $this->get_export('scss', array( 'line' => "$%1: %2;\n", 'glue' => '', )); } protected function get_export($format, $args) { $palette = $this->get_palette(); if (!$palette) { return ''; } $args = wp_parse_args($args, array( 'wrap' => '%1', 'line' => '', 'glue' => "\n", 'compat' => '', 'compat_glue' => "\n", )); $lines = array(); $selector = trim($this->get_request("kt_export_{$format}_selector")); if ($selector === '') { $selector = ':root'; } $add_compat = $this->get_request("kt_export_{$format}_color_compat"); $prefix = sanitize_html_class($this->get_request("kt_export_{$format}_prefix")); $suffix = sanitize_html_class($this->get_request("kt_export_{$format}_suffix")); $color_format = $this->get_request("kt_export_{$format}_color_format", self::DEFAULT_EXPORT_COLOR_FORMAT); $compat = $add_compat && $color_format != 'hex' && $args['compat']; $glue = $compat ? $args['compat_glue'] : $args['glue']; foreach ($palette as $set) { $color = $this->get_css_export_color($set, $color_format); $name = $prefix . $set['variable'] . $suffix; if ($compat) { $lines[] = $this->sprintf($args['compat'], $name, $set['hex'], $color); } else { $lines[] = $this->sprintf($args['line'], $name, $color); } } return $this->sprintf($args['wrap'], implode($glue, $lines), $selector); } protected function get_css_export_color($set, $format = 'hex') { if ($format == 'rgb' || $format == 'rgba') { $color = array_map('hexdec', str_split(substr($set['hex'], 1), 2)); if ($format == 'rgba') { $color[] = round($set['alpha'] / 100, 2); return 'rgba(' . implode(', ', $color) . ')'; } return 'rgb(' . implode(', ', $color) . ')'; } switch ($format) { case 'hsl': return $this->hex2hsla($set['hex'], false); case 'hsla': return $this->hex2hsla($set['hex'], $set['alpha']); } return $set['hex']; } /** * Pass a HTTP request value through a filter and store it as option * @since 1.7 * @param string $key * @param array $constrain * @param string $option * @param mixed $default * @return mixed */ protected function set($key, $constrain, $option, $default) { $value = $this->get_request($key, $default); $value = in_array($value, $constrain) ? $value : $default; $this->set_option($option, $value); return $value; } /** * Renders color map * @since 1.7 */ protected function render_map() { switch ($this->get_option(self::TYPE, self::DEFAULT_TYPE)) { case 'palette': $map = $this->render_palette(); break; case 'rainbow': $map = $this->render_rainbow(); break; case 'block': $map = $this->render_blocks(); break; default: return; } $this->set_option(self::MAP, $map); } /** * Chunk palette into columns of constant size * @since 1.7 * @return array [palette, rows, cols] */ protected function chunk_palette($_palette = null) { $palette = array(); list($rows, $cols) = $this->get_map_size(); if ($this->get_option(self::VISUAL)) { $palette = $this->get_palette(array( 'status' => self::COLOR_ACTIVE, 'chunk' => $rows, '_palette' => $_palette, )); } return array($palette, $rows, $cols); } /** * Get palette size depending on its current type * @since 1.9 * @return array [rows, cols] */ protected function get_map_size() { switch ($this->get_option(self::TYPE, self::DEFAULT_TYPE)) { case 'palette': $count = 1 + count($this->get_palette(array( 'status' => self::COLOR_ACTIVE, ))); if ('even' == $this->get_option(self::SPREAD, self::DEFAULT_SPREAD)) { $cols = ceil(sqrt($count)); $rows = ceil($count / $cols); return array($rows, $cols); } $fixed = $this->get_option(self::CLAMPS, self::DEFAULT_CLAMPS); $dynamic = ceil($count / $fixed); if ('cols' == $this->get_option(self::CLAMP, self::DEFAULT_CLAMP)) { return array($dynamic, $fixed); } return array($fixed, $dynamic); case 'rainbow': $rows = $this->get_option(self::ROWS, self::DEFAULT_ROWS); $cols = $this->get_option(self::COLS, self::DEFAULT_COLS); return array($rows, $cols); case 'block': $size = $this->get_option(self::SIZE, self::DEFAULT_SIZE); $blocks = $this->get_option(self::BLOCKS, self::DEFAULT_BLOCKS); return array($size * self::GROUPS, $size * $blocks / self::GROUPS); } return array(self::TINYMCE_ROWS, self::TINYMCE_COLS); } /** * Add a row from the palette to the color map * @since 1.7 * @param array $map passed by reference * @param array $palette passed by reference * @param int $row */ protected function add_row_to_map(&$map, &$palette, $row) { $cols = count($palette); for ($col = 0; $col < $cols; $col++) { if ($palette[$col][$row]['color'] == 'reset') { continue; } $map[] = ltrim($palette[$col][$row]['hex'], '#'); $map[] = $palette[$col][$row]['name']; } } /** * Add a monocrome/grayscale color to the color map * @since 1.7 * @param array $map passed by reference * @param int $row * @param int $rows */ protected function add_monocroma(&$map, $row, $rows) { if ($row == $rows - 1) { return; } $c = $this->float2hex($row / ($rows - 2)); $map[] = "$c$c$c"; $map[] = ""; } /** * Render TinyMCE palette color map * @since 1.8 * @return array */ protected function render_palette() { $_palette = $this->get_palette(array( 'status' => self::COLOR_ACTIVE, )); $_palette[] = array( 'color' => 'reset', 'status' => self::COLOR_ACTIVE, ); list($palette, $rows, $columns) = $this->chunk_palette($_palette); $map = array(); for ($row = 0; $row < $rows; $row++) { $this->add_row_to_map($map, $palette, $row); } return compact('map', 'rows', 'columns'); } /** * Render TinyMCE block color map * @since 1.7 * @return array */ protected function render_blocks() { $blocks = $this->get_option(self::BLOCKS, self::DEFAULT_BLOCKS); $size = $this->get_option(self::SIZE, self::DEFAULT_SIZE); $axis = $this->get_option(self::AXIS, self::DEFAULT_AXIS); $pattern = strtr($axis, array( 'r' => '%1$s', 'g' => '%2$s', 'b' => '%3$s', )); $per_group = $blocks / self::GROUPS; $chunks = $square = array(); for ($i = 0, $step = 1 / ($size - 1); $i < $size; $i++) { $square[] = $this->float2hex($i * $step); } for ($i = 0, $step = 1 / ($blocks - 1); $i < $blocks; $i++) { $chunks[] = $this->float2hex($i * $step); } list($palette, $rows, $columns) = $this->chunk_palette(); $map = array(); for ($row = 0; $row < $rows; $row++) { $this->add_row_to_map($map, $palette, $row); $b = $square[$row % $size]; $shift = floor($row / $size) * $per_group; for ($col = 0; $col < $columns; $col++) { $g = $square[$col % $size]; $r = $chunks[floor($col / $size) + $shift]; $map[] = sprintf($pattern, $r, $g, $b); $map[] = ""; } $this->add_monocroma($map, $row, $rows); } $columns += count($palette) + 1; return compact('map', 'rows', 'columns'); } /** * Render TinyMCE rainbow color map * @since 1.7 * @return array */ protected function render_rainbow() { list($palette, $rows, $columns) = $this->chunk_palette(); $rgb = array(); for ($i = 0; $i < $columns; $i++) { $rgb[] = $this->hue2rgb($i / $columns); } $map = array(); $type = $this->get_option(self::LUMA, self::DEFAULT_LUMA); for ($row = 0; $row < $rows; $row++) { $this->add_row_to_map($map, $palette, $row); $luma = 2 * ($row + 1) / ($rows + 1) - 1; $luma = $this->transform_luma($luma, $type); for ($col = 0; $col < $columns; $col++) { $_rgb = $this->apply_luma($luma, $rgb[$col]); $map[] = $this->rgb2hex($_rgb, true, false); $map[] = ""; } $this->add_monocroma($map, $row, $rows); } $columns += count($palette) + 1; return compact('map', 'rows', 'columns'); } /** * Add help to settings page * @since 1.7 */ protected function add_help() { $screen = get_current_screen(); $link = '%2$s'; $rgb_url = vsprintf($link, array( _x('https://en.wikipedia.org/wiki/RGB_color_model', 'URL to wiki page about RGB', 'kt-tinymce-color-grid'), __('RGB cube', 'kt-tinymce-color-grid'), __('Wikipedia article about RGB color space', 'kt-tinymce-color-grid'), )); $hsl_link = vsprintf($link, array( _x('https://en.wikipedia.org/wiki/HSL_and_HSV', 'URL to wiki page about HSL', 'kt-tinymce-color-grid'), __('HSL space', 'kt-tinymce-color-grid'), __('Wikipedia article about HSL color space', 'kt-tinymce-color-grid'), )); $screen->add_help_tab(array( 'id' => 'palette', 'title' => __('Color Palette', 'kt-tinymce-color-grid'), 'content' => '

' . __('You can create a color palette and include it to the Visual Editor and/or the Theme Customizer.', 'kt-tinymce-color-grid') . '

' . __("Add to Theme Customizer makes the palette available to the color picker of the Theme Customizer. This works by altering WordPress' color picker so every plugin using it receives the palette as well.", 'kt-tinymce-color-grid') . '

' . __('Add to block editor adds the palette to the color picker of the new block editor.', 'kt-tinymce-color-grid') . '

' . __('Add to classic editor adds the palette to the color picker of the classic editor. This only works if you choose a color grid other than Default.', 'kt-tinymce-color-grid') . '

' )); $screen->add_help_tab(array( 'id' => 'grid', 'title' => __('Classic Editor', 'kt-tinymce-color-grid'), 'content' => '

' . __("Default leaves TinyMCE's color picker untouched.", 'kt-tinymce-color-grid') . '

' . __("Palette only takes the colors defined by the Central Palette.") . '

' . sprintf(__("Rainbow takes hue and lightness components from the %s and thus creates a rainbow. The Luma option controls how the lightness for each hue is spread.", 'kt-tinymce-color-grid'), $hsl_link) . '

' . sprintf(__("Blocks takes planes from the %s and places them next to one another. Block Count controls how many planes are taken, and Block Size determines their size.", 'kt-tinymce-color-grid'), $rgb_url) . '

' )); $screen->add_help_tab(array( 'id' => 'backup', 'title' => __('Backup', 'kt-tinymce-color-grid'), 'content' => '

' . __('If you want to export all settings and your palette to a file you can do so by simply clicking Download Backup at the bottom of the editor and you will be prompted with a download.', 'kt-tinymce-color-grid') . '

' . __('Likewise you can import such a file into this or another install of WordPress by clicking Choose Backup. All current settings and your palette will be overwritten, so make sure you made a backup.', 'kt-tinymce-color-grid') . '

' )); $screen->add_help_tab(array( 'id' => 'aria', 'title' => __('Accessibility', 'kt-tinymce-color-grid'), 'content' => '

' . __('The palette editor consists of a toolbar and a list of entries. Every entry has a color picker, two text fields — one holding a hexadecimal representation of the color, and one for the name of the entry — and lastly a button to remove the entry.', 'kt-tinymce-color-grid') . '

' . __('You can reorder an entry by pressing the page keys. To delete an entry press the delete or backspace key. If a color picker has focus use the arrow keys, and plus and minus to change the color.', 'kt-tinymce-color-grid') . '

' )); $plugin_url = esc_url(_x('https://wordpress.org/plugins/kt-tinymce-color-grid', 'URL to plugin site', 'kt-tinymce-color-grid')); $support_url = esc_url(_x('https://wordpress.org/support/plugin/kt-tinymce-color-grid', 'URL to support forums', 'kt-tinymce-color-grid')); $documentation_url = esc_url('https://kungtiger.github.io/central-color-palette'); $screen->set_help_sidebar('

' . esc_html__('For more information:', 'kt-tinymce-color-grid') . '

' . esc_html__('Visit plugin site', 'kt-tinymce-color-grid') . '

' . esc_html__('Support Forums', 'kt-tinymce-color-grid') . '

' . esc_html__('API Documentation', 'kt-tinymce-color-grid') . '

'); } /** * Render settings page * @since 1.3 */ public function print_settings_page() { $head = $this->version_compare('<', '4.3') ? 'h2' : 'h1'; print "
<$head>" . get_admin_page_title() . ""; $this->print_settings_error(); $file = $this->is_active_for_network ? 'settings.php' : 'options-general.php'; print "

" . esc_html__('You need to enable JavaScript to use the palette editor.', 'kt-tinymce-color-grid') . "

" . esc_html__('You need to use a device with a bigger screen to use the palette editor.', 'kt-tinymce-color-grid') . "

"; wp_nonce_field(self::NONCE, 'kt_settings_nonce', false); wp_nonce_field('meta-box-order', 'meta-box-order-nonce', false); wp_nonce_field('closedpostboxes', 'closedpostboxesnonce', false); print "
"; do_meta_boxes(get_current_screen(), 'advanced', $this); $save_key = _x('S', 'accesskey for saving', 'kt-tinymce-color-grid'); $save_label = $this->underline_accesskey(__('Save', 'kt-tinymce-color-grid'), $save_key); print "

"; } /** * Render settings error * @since 1.9 */ public function print_settings_error() { $feedback = $error = ''; if (isset($_GET['kt-import-error'])) { $error = $_GET['kt-import-error']; $import_errors = array( 'ok' => __('Backup successfully imported.', 'kt-tinymce-color-grid'), 'no-import' => __('No data to process.', 'kt-tinymce-color-grid'), 'format' => __('The uploaded file format is not supported.', 'kt-tinymce-color-grid'), 'funny' => __('The uploaded file does not contain any useable data.', 'kt-tinymce-color-grid'), 'no-upload' => __('No file was uploaded.', 'kt-tinymce-color-grid'), 'empty' => __('The uploaded file is empty.', 'kt-tinymce-color-grid'), 'partially' => __('The uploaded file was only partially uploaded.', 'kt-tinymce-color-grid'), 'size-php' => __('The uploaded file exceeds the upload_max_filesize directive in php.ini.', 'kt-tinymce-color-grid'), 'size' => sprintf(__('The uploaded file is too big. It is limited to %s.', 'kt-tinymce-color-grid'), size_format(self::MAX_FILE_SIZE)), 'tmp' => __('Missing a temporary folder.', 'kt-tinymce-color-grid'), 'fs' => __('Failed to write file to disk.', 'kt-tinymce-color-grid'), 'ext' => __('File upload stopped by PHP extension.', 'kt-tinymce-color-grid'), ); $feedback = __('Import failed.', 'kt-tinymce-color-grid'); if (isset($import_errors[$error])) { $feedback = $import_errors[$error]; } } else if (isset($_GET['kt-export-error'])) { $error = $_GET['kt-export-error']; $export_errors = array( 'no-parts' => __('Please select which parts you would like to backup.', 'kt-tinymce-color-grid'), 'json' => __('Could not pack settings into JSON.', 'kt-tinymce-color-grid'), 'base64' => __('Could not convert settings.', 'kt-tinymce-color-grid'), ); $feedback = __('Export failed.', 'kt-tinymce-color-grid'); if (isset($export_errors[$error])) { $feedback = $export_errors[$error]; } } if ($feedback) { $type = $error == 'ok' ? 'updated' : 'error'; print "

$feedback

"; } } /** * Print grid metabox * @since 1.9 */ public function print_grid_metabox() { $_type = $this->get_option(self::TYPE, self::DEFAULT_TYPE); $_cols = $this->get_option(self::COLS, self::DEFAULT_COLS); $_rows = $this->get_option(self::ROWS, self::DEFAULT_ROWS); $_blocks = $this->get_option(self::BLOCKS, self::DEFAULT_BLOCKS); $_size = $this->get_option(self::SIZE, self::DEFAULT_SIZE); $_axis = $this->get_option(self::AXIS, self::DEFAULT_AXIS); $_spread = $this->get_option(self::SPREAD, self::DEFAULT_SPREAD); $_clamp = $this->get_option(self::CLAMP, self::DEFAULT_CLAMP); $_clamps = $this->get_option(self::CLAMPS, self::DEFAULT_CLAMPS); $luma_map = array( 'linear' => __('Linear', 'kt-tinymce-color-grid'), ) + $this->get_luma_transformations('names'); $size = array( 4 => __('small', 'kt-tinymce-color-grid'), 5 => __('medium', 'kt-tinymce-color-grid'), 6 => __('big', 'kt-tinymce-color-grid'), ); $axes = array( 'rgb' => __('Blue-Green', 'kt-tinymce-color-grid'), 'rbg' => __('Green-Blue', 'kt-tinymce-color-grid'), 'grb' => __('Blue-Red', 'kt-tinymce-color-grid'), 'brg' => __('Red-Blue', 'kt-tinymce-color-grid'), 'gbr' => __('Green-Red', 'kt-tinymce-color-grid'), 'bgr' => __('Red-Green', 'kt-tinymce-color-grid'), ); $clamp = array( 'row' => __('row', 'kt-tinymce-color-grid'), 'column' => __('column', 'kt-tinymce-color-grid'), ); $types = array( 'default' => __('Default', 'kt-tinymce-color-grid'), 'palette' => __('Color Palette', 'kt-tinymce-color-grid'), 'rainbow' => __('Rainbow', 'kt-tinymce-color-grid'), 'block' => __('Blocks', 'kt-tinymce-color-grid'), ); $cols = $this->selectbox('kt_cols', $this->columns, $_cols); $rows = $this->selectbox('kt_rows', $this->rows, $_rows); $luma = ''; if (count($luma_map) > 1) { $luma_label = esc_html__('Luma', 'kt-tinymce-color-grid'); $current_luma = $this->get_option(self::LUMA, self::DEFAULT_LUMA); $luma = $this->selectbox('kt_luma', $luma_map, $current_luma); $luma = " $luma"; } $blocks = $this->selectbox('kt_blocks', $this->blocks, $_blocks); $size = $this->selectbox('kt_block_size', $size, $_size); $axes = $this->selectbox('kt_axis', $axes, $_axis); print "

"; print $this->selectbox('kt_type', $types, $_type); print '

'; $clamp = $this->selectbox('kt_clamp', $clamp, $_clamp); $clamps = ""; $spread = array( 'even' => esc_html__('Spread colors evenly', 'kt-tinymce-color-grid'), /* translators: %1$s selectbox for row or column, %2$s input for number */ 'odd' => sprintf(__('Fill each %1$s with %2$s colors', 'kt-tinymce-color-grid'), $clamp, $clamps), ); foreach ($spread as $value => $label) { $id = "kt_spread_$value"; $checked = $_spread == $value ? " checked='checked'" : ''; print "

"; } print "

$rows $cols$luma

$blocks $size $axes

"; } public function sort_interations($a, $b) { return strcasecmp($a[2], $b[2]); } /** * Print editor metabox * @since 1.9 */ public function print_palette_metabox() { $_type = $this->get_option(self::TYPE, self::DEFAULT_TYPE); $_visual = $this->get_option(self::VISUAL); $_customizer = $this->get_option(self::CUSTOMIZER); $_css_vars = $this->get_option(self::CSS_VARS); $_css_admin_vars = $this->get_option(self::CSS_ADMIN_VARS); if ($_type == 'palette') { $_visual = true; } $customizer_checked = $_customizer ? ' checked="checked"' : ''; $visual_checked = $_visual ? ' checked="checked"' : ''; $css_vars_checked = $_css_vars ? ' checked="checked"' : ''; $css_admin_vars_checked = $_css_admin_vars ? ' checked="checked"' : ''; $add_key = _x('A', 'accesskey for adding color', 'kt-tinymce-color-grid'); $_add = __('Add Color', 'kt-tinymce-color-grid'); $add_label = esc_html($_add); $add_title = esc_attr($_add); $add_to_customizer = esc_html__('Add to Theme Customizer', 'kt-tinymce-color-grid'); foreach ($this->native_palette_support as $native => $name) { if ($this->supports($native)) { $add_to_customizer .= " / $name"; } } print "

"; $has_gutenberg = $this->supports('gutenberg'); if ($has_gutenberg) { $_gutenberg = $this->get_option(self::GUTENBERG); $_gutenberg_merge = $this->get_option(self::GUTENBERG_MERGE); $_gutenberg_force = $this->get_option(self::GUTENBERG_FORCE); $gutenberg_checked = $_gutenberg ? ' checked="checked"' : ''; $gutenberg_merge_checked = $_gutenberg_merge ? ' checked="checked"' : ''; $gutenberg_force_checked = $_gutenberg_force ? ' checked="checked"' : ''; $merge_hidden = $_gutenberg ? '' : ' hide-if-js'; $force_hint = esc_attr__("Try this if your colors won't get applied to your theme", 'kt-tinymce-color-grid'); print "


"; } print "

"; $integrations = array( array('acf', self::ACF, __('Advanced Custom Fields', 'kt-tinymce-color-grid'), ''), array('astra', self::ASTRA_THEME, __('Astra Theme', 'kt-tinymce-color-grid'), ''), array('beaverbuilder', self::BEAVERBUILDER, __('Beaver Builder', 'kt-tinymce-color-grid'), ''), array('elementor', self::ELEMENTOR, __('Elementor', 'kt-tinymce-color-grid'), ''), array('fontpress', self::FONTPRESS, __('FontPress', 'kt-tinymce-color-grid'), ''), array('megamaxmenu', self::MEGAMAXMENU, __('Mega Max Menu', 'kt-tinymce-color-grid'), ''), array('generatepress', self::GENERATEPRESS, __('GeneratePress', 'kt-tinymce-color-grid'), ''), array('oceanwp', self::OCEANWP, __('OceanWP', 'kt-tinymce-color-grid'), ''), array('oxygen_vsb', self::OXYGEN_VSB, __('Oxygen', 'kt-tinymce-color-grid'), ''), array('suki_theme', self::SUKI_THEME, __('Suki Theme', 'kt-tinymce-color-grid'), __('Suki only supports up to eight colors.', 'kt-tinymce-color-grid')), ); $supported_integrations = array(); foreach ($integrations as $integration) { if ($this->supports($integration[0])) { $supported_integrations[] = $integration; } } $alpha_channel = array( 'oceanwp' => self::OCEANWP_ALPHA, 'astra' => self::ASTRA_ALPHA, ); if ($this->supports('generatepress-premium')) { $alpha_channel['generatepress'] = self::GENERATEPRESS_ALPHA; } if (count($supported_integrations)) { print '
'; $_add_to = __('Integrate with %s', 'kt-tinymce-color-grid'); $_use_alpha = __('Add transparent colors to %s', 'kt-tinymce-color-grid'); usort($supported_integrations, array($this, 'sort_interations')); foreach ($supported_integrations as $integration) { list($id, $option_name, $label, $hint) = $integration; $is_active = $this->get_option($option_name); $checked = $is_active ? ' checked="checked"' : ''; $has_alpha = isset($alpha_channel[$id]); $has_hint = $hint !== ''; printf('

', $id, sprintf($_add_to, $label), $checked, $has_alpha || $has_hint ? ' data-form="1"' : ''); $hidden = $is_active ? '' : ' hide-if-js'; $alpha_checkbox = ''; if ($has_alpha || $has_hint) { if ($has_alpha) { $alpha = $this->get_option($alpha_channel[$id]); $checked = $alpha ? ' checked="checked"' : ''; $alpha_checkbox = sprintf(' ', $id, $checked, sprintf($_use_alpha, $label)); } if ($has_hint) { $hint = "$hint
"; } printf('

%3$s%4$s

', $id, $hidden, $hint, $alpha_checkbox); } } print '
'; } print '
'; $classes = array('loading', 'show-palette'); $classes = implode(' ', $classes); print "
" . esc_attr__('loading...', 'kt-tinymce-color-grid') . " " . esc_attr__('Palette is empty', 'kt-tinymce-color-grid') . " " . esc_attr__('No inactive colors', 'kt-tinymce-color-grid') . "
"; print ' '; } /** * Print backup metabox * @since 1.9 */ public function print_backup_metabox() { $this->print_export_forms(); $this->print_import_forms(); } public function print_export_forms() { print '

' . esc_html__('Export', 'kt-tinymce-color-grid') . '

'; foreach ($forms as $id => $form) { $label = sprintf(__('Options for %s', 'kt-tinymce-color-grid'), implode(' / ', $form['label'])); $active = $form['active'] ? '' : ' hide-if-js'; print "

$label

"; call_user_func(array($this, "print_{$id}_export_form")); print '
'; } print "


'; } public function print_import_forms() { $_note = '

' . esc_html__('Currently only files in Backup and JSON format are supported.', 'kt-tinymce-color-grid') . '

'; print '

' . esc_html__('Import', 'kt-tinymce-color-grid') . '

'; if ($this->supports('upload')) { print $_note . '

"; } else { print '

' . esc_html__('Your device seems not to support file uploads. Open your import in a simple text editor and paste its content into this textfield.', 'kt-tinymce-color-grid') . "

$_note

'; } } public function print_json_export_form() { $parts = array( 'settings' => __('Settings', 'kt-tinymce-color-grid'), 'palette' => __('Palette', 'kt-tinymce-color-grid'), ); print '

'; foreach ($parts as $key => $label) { $id = "kt_export_{$key}"; $checked = $this->get_cookie($id, 1); $key = esc_attr($key); print " "; } print '

'; } public function print_export_color_format_fields($format, $compat = false) { $color_formats = array( 'hex' => __('Hexadecimal only', 'kt-tinymce-color-grid'), 'rgb' => __('RGB', 'kt-tinymce-color-grid'), 'rgba' => __('RGBA', 'kt-tinymce-color-grid'), 'hsl' => __('HSL', 'kt-tinymce-color-grid'), 'hsla' => __('HSLA', 'kt-tinymce-color-grid'), ); $id = "kt_export_{$format}_color_format"; $cookie = esc_attr($this->get_cookie($id), self::DEFAULT_EXPORT_COLOR_FORMAT); print "

'; if ($compat) { $hidden = $cookie == 'hex' ? ' class="hide-if-js"' : ''; $id = "kt_export_{$format}_color_compat"; $cookie = esc_attr($this->get_cookie($id)); $checked = $cookie ? ' checked="checked"' : ''; print "

"; } } public function print_export_affix_fields($format, $for_names = false) { $affixes = array( 'prefix' => $for_names ? __('Name Prefix', 'kt-tinymce-color-grid') : __('Class Prefix', 'kt-tinymce-color-grid'), 'suffix' => $for_names ? __('Name Suffix', 'kt-tinymce-color-grid') : __('Class Suffix', 'kt-tinymce-color-grid'), ); $_none = __('None', 'kt-tinymce-color-grid'); foreach ($affixes as $affix => $label) { $id = "kt_export_{$format}_{$affix}"; $cookie = esc_attr($this->get_cookie($id)); $label = esc_html($label); print "

"; } } protected function print_export_preview_template($format, $template, $compat = false) { $_key = '{{ data.prefix }}{{ data.key }}{{ data.suffix }}'; $_color = '{{ data.color }}'; $_selector = '{{ data.selector }}'; $template = $this->sprintf($template, $_key, $_color, $_selector); print "
" . esc_html__('Preview', 'kt-tinymce-color-grid') . "

"; if ($compat) { $compat = $this->sprintf($compat, $_key, '{{ data.hex }}', $_color, $_selector); print " "; } } public function print_css_export_form() { $this->print_export_affix_fields('css'); $this->print_export_color_format_fields('css', true); $this->print_export_preview_template('css', ".%1 {\n color: %2;\n}", ".%1 {\n color: %2;\n color: %3;\n}"); } public function print_css_vars_export_form() { $id = 'kt_export_css_vars_selector'; $selector = esc_attr($this->get_cookie($id)); print "

"; $this->print_export_affix_fields('css_vars', true); $this->print_export_color_format_fields('css_vars', true); $this->print_export_preview_template('css_vars', "%3 {\n --%1: %2;\n}", "%4 {\n --%1: %2;\n --%1: %3\n}"); } public function print_scss_export_form() { $this->print_export_affix_fields('scss'); $this->print_export_color_format_fields('scss'); $this->print_export_preview_template('scss', "\$%1: %2;", "\$%1: %2;\n\$%1: %3;"); } public function sprintf() { $values = func_get_args(); $string = array_shift($values); foreach ($values as $i => $value) { $token = '%' . ($i + 1); $string = str_replace($token, $value, $string); } return $string; } /** * Highlight an accesskey inside a translated string * @since 1.4.4 * @param string $string Translated string * @param string $key Accesskey * @return string */ protected function underline_accesskey($string, $key) { $pattern = '~(' . preg_quote($key, '~') . ')~i'; return preg_replace($pattern, '$1', esc_html($string), 1); } /** * Generate HTML markup of a selectbox * @since 1.7 * @param string $name * @param array $data * @param mixed $selected * @param bool $disabled * @return string */ protected function selectbox($name, $data, $selected = null, $disabled = false) { $options = ''; if (key($data) === 0) { $data = array_combine($data, $data); } foreach ($data as $value => $label) { $sel = $value == $selected ? ' selected="selected"' : ''; $value = esc_attr($value); $label = esc_html($label); $options .= " "; } $name = esc_attr($name); $disabled = $disabled ? ' disabled="disable"' : ''; return " "; } /** * Fetch a HTTP request value * @since 1.3 * @param string $key Name of the value to fetch * @param mixed|null $default Default value if $key does not exist * @return mixed The value for $key or $default */ protected function get_request($key, $default = null) { return key_exists($key, $_REQUEST) ? $_REQUEST[$key] : $default; } /** * Get a cookie * @since 1.9 * @param string $name Cookie name * @param mixed $default Default value if cookie is not set * @return string */ protected function get_cookie($name, $default = null) { return isset($_COOKIE[$name]) ? $_COOKIE[$name] : $default; } /** * Perform regular expression match and get first capture group * @since 1.9 * @param string $pattern * @param string $subject * @param string|null $default Default value if pattern does not match * @return string|null */ protected function preg_get($pattern, $subject, $default = null) { $matches = null; if (preg_match($pattern, $subject, $matches)) { return isset($matches[1]) ? $matches[1] : $default; } return $default; } protected $luma_transformations = array(); public function add_luma_transformation($id, $name, $fn) { if ($this->has_luma_transformation($id) || !is_callable($fn)) { return false; } $this->luma_transformations[$id] = array($name, $fn); } public function has_luma_transformation($id) { return $id && isset($this->luma_transformations[$id]); } public function get_luma_transformation($id) { if ($this->has_luma_transformation($id)) { return $this->luma_transformations[$id]; } return false; } public function get_luma_transformations($output = 'all') { switch ($output) { case 'ids': return array_keys($this->luma_transformations); case 'names': $transformations = array(); foreach ($this->luma_transformations as $id => $transformation) { $transformations[$id] = $transformation[0]; } return $transformations; } return $this->luma_transformations; } public function default_luma_transformations() { $this->add_luma_transformation('sine', __('Sine', 'kt-tinymce-color-grid'), array($this, 'sine_luma')); $this->add_luma_transformation('cubic', __('Cubic', 'kt-tinymce-color-grid'), array($this, 'cubic_luma')); $this->add_luma_transformation('natural', __('Natural', 'kt-tinymce-color-grid'), array($this, 'natural_luma')); } /** * Apply a transformation on a linear float * @since 1.7 * @param float $luma [-1..1] * @param string $type * @return float [-1..1] */ public function transform_luma($luma, $type) { if (!$this->has_luma_transformation($type)) { return $luma; } list($name, $fn) = $this->get_luma_transformation($type); return call_user_func($fn, $luma, $name); } /** * Apply a sine transformation on a linear luma value. * @since 1.7 * @param float $luma [-1..1] * @return float [-1..1] */ public function sine_luma($luma) { return $luma < 0 ? sin((1 - $luma) * M_PI_2) - 1 : sin($luma * M_PI_2); } /** * Apply a cubic transformation on a linear luma value. * @since 1.7 * @param float $luma [-1..1] * @return float [-1..1] */ public function cubic_luma($luma) { return $luma < 0 ? pow(($luma + 1), 8 / 11) - 1 : pow($luma, 8 / 13); } /** * Apply a natural transformation on a linear luma value. * @since 1.7 * @param float $luma [-1..1] * @return float [-1..1] */ public function natural_luma($luma) { return $luma < 0 ? $this->sine_luma($luma) : $this->cubic_luma($luma); } /** * Apply a luma transformation on a RGB vector * @since 1.7 * @param float $luma [-1..1] * @param array $rgb RGB vector [red, gree, blue] of [0..1] * @return array */ public function apply_luma($luma, $rgb) { foreach ($rgb as $i => $c) { if ($luma < 0) { $c += $c * $luma; } else if ($luma > 0) { $c = $c == 0 ? $luma : $c + (1 - $c) * $luma; $c = max(0, min($c, 1)); } $rgb[$i] = $c; } return $rgb; } /** * Sanitize a string to #RRGGBB * @since 1.4 * @since 1.13 optionally prepends a # * @param string $string String to be checked * @param boolean $prepend_hash [optional] Prepend resulting string with a hash * @return string|boolean Returns a color of #RRGGBB or false on failure */ public function sanitize_color($string, $prepend_hash = true) { $string = strtoupper($string); $hex = $this->preg_get('~#?([0-9A-F]{6}|[0-9A-F]{3})~', $string); if ($hex === null) { return false; } if (strlen($hex) == 3) { $hex = preg_replace('~([0-9A-F])~', '\1\1', $hex); } return $prepend_hash ? "#$hex" : $hex; } /** * Sanitize an alpha value * @since 1.10 * @param string $string * @return int */ public function sanitize_alpha($string) { return intval($this->preg_get('~(100|[1-9][0-9]?|0)~', $string, 100)); } /** * Convert a float to a HEX string * @since 1.7 * @param float $p [0..1] * @return string */ public function float2hex($p) { return $this->int2hex($p * 255); } /** * Convert a integer to a HEX string * @since 1.9 * @param int|float $i [0..255] * @return string */ public function int2hex($i) { $s = dechex($i); return (strlen($s) == 1 ? '0' : '') . $s; } /** * Return a RGB vector for a hue * @since 1.7 * @param float $hue [0..1] * @return array RGB vector [red, gree, blue] of [0..1] */ public function hue2rgb($hue) { $hue *= 6; if ($hue < 1) { return array(1, $hue, 0); } if (--$hue < 1) { return array(1 - $hue, 1, 0); } if (--$hue < 1) { return array(0, 1, $hue); } if (--$hue < 1) { return array(0, 1 - $hue, 1); } if (--$hue < 1) { return array($hue, 0, 1); } return array(1, 0, 1 - --$hue); } /** * Convert a RGB vector to a HEX string * @since 1.9 * @param array $rgb RGB vector [red, gree, blue] of floats [0..1] * @param bool $floats Return vector of floats * @return string|false Returns false if any of the components is outside [0..1] */ public function rgb2hex($rgb, $floats = false, $prepend_hash = true) { foreach ($rgb as $i => $x) { if ($floats) { $x *= 255; } if ($x < 0 || $x > 255) { return false; } $rgb[$i] = $this->int2hex($x); } return ($prepend_hash ? '#' : '') . implode('', $rgb); } /** * Convert a HEX string to a RGB vector * @since 1.13 * @param string $hex * @return array */ public function hex2rgb($hex, $as_string = false) { $color = (string) $this->sanitize_color($hex, false); $hex = str_split($color, 2); $vector = array_map('hexdec', $hex); if (!$as_string) { return $vector; } return 'rgb(' . implode(', ', $vector) . ')'; } /** * Convert a HEX string and an alpha value into RGBA notation * @since 1.13 * @param string $hex Hexadecimal color * @param int $alpha Alpha value [0..100] * @param bool $force_alpha * @return string */ public function hex2rgba($hex, $alpha, $force_alpha = false) { if (!$force_alpha && $alpha == 100) { return $hex; } list($r, $g, $b) = $this->hex2rgb($hex); $a = round($alpha / 100, 2); return "rgba($r,$g,$b,$a)"; } public function hex2hsla($hex, $alpha = false) { $rgb = $this->hex2rgb($hex); $r = $rgb[0] / 255; $g = $rgb[1] / 255; $b = $rgb[2] / 255; $min = min($rgb) / 255; $max = max($rgb) / 255; $e = $max + $min; $d = $max - $min; $l = $e / 2; $s = 0; if ($l > 0 && $l < 1) { $s = $d / ($l < .5 ? $e : (2 - $e)); } $h = 0; if ($d > 0) { if ($max == $r) { $h = ($g - $b) / $d + ($g < $b ? 6 : 0); } else if ($max == $g) { $h = ($b - $r) / $d + 2; } else if ($max == $b) { $h = ($r - $g) / $d + 4; } } $hsl = array(round($h * 60, 2) . '%', round($s * 100, 2) . '%', round($l * 100, 2) . '%'); if ($alpha === false) { return 'hsl(' . implode(', ', $hsl) . ')'; } $hsl[] = round($alpha / 100, 2); return 'hsla(' . implode(', ', $hsl) . ')'; } } kt_Central_Palette::instance(); }