mirror of
https://ghproxy.net/https://github.com/AlxMedia/curver.git
synced 2025-08-28 09:25:45 +08:00
Initial commit
This commit is contained in:
commit
ee50200fe7
353 changed files with 78977 additions and 0 deletions
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles the CSS-variables of fields.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.28
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Kirki_Modules_CSS_Vars object.
|
||||
*
|
||||
* @since 3.0.28
|
||||
*/
|
||||
class Kirki_Modules_CSS_Vars {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Fields with variables.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @var array
|
||||
*/
|
||||
private $fields = array();
|
||||
|
||||
/**
|
||||
* CSS-variables array [var=>val].
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.35
|
||||
* @var array
|
||||
*/
|
||||
private $vars = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.28
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'init', array( $this, 'populate_vars' ) );
|
||||
add_action( 'wp_head', array( $this, 'the_style' ), 999 );
|
||||
add_action( 'admin_head', array( $this, 'the_style' ), 999 );
|
||||
add_action( 'customize_preview_init', array( $this, 'postmessage' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the $vars property of this object.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
* @return void
|
||||
*/
|
||||
public function populate_vars() {
|
||||
|
||||
// Get an array of all fields.
|
||||
$fields = Kirki::$fields;
|
||||
foreach ( $fields as $id => $args ) {
|
||||
if ( ! isset( $args['css_vars'] ) || empty( $args['css_vars'] ) ) {
|
||||
continue;
|
||||
}
|
||||
$val = Kirki_Values::get_value( $args['kirki_config'], $id );
|
||||
foreach ( $args['css_vars'] as $css_var ) {
|
||||
if ( isset( $css_var[2] ) && is_array( $val ) && isset( $val[ $css_var[2] ] ) ) {
|
||||
$this->vars[ $css_var[0] ] = str_replace( '$', $val[ $css_var[2] ], $css_var[1] );
|
||||
} else {
|
||||
$this->vars[ $css_var[0] ] = str_replace( '$', $val, $css_var[1] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add styles in <head>.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @return void
|
||||
*/
|
||||
public function the_style() {
|
||||
if ( empty( $this->vars ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<style id="kirki-css-vars">';
|
||||
echo ':root{';
|
||||
foreach ( $this->vars as $var => $val ) {
|
||||
echo esc_html( $var ) . ':' . esc_html( $val ) . ';';
|
||||
}
|
||||
echo '}';
|
||||
echo '</style>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of all the variables.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
* @return array
|
||||
*/
|
||||
public function get_vars() {
|
||||
return $this->vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the script that handles postMessage
|
||||
* and adds variables to it using the wp_localize_script function.
|
||||
* The rest is handled via JS.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @return void
|
||||
*/
|
||||
public function postmessage() {
|
||||
wp_enqueue_script( 'kirki_auto_css_vars', trailingslashit( Kirki::$url ) . 'modules/css-vars/script.js', array( 'jquery', 'customize-preview' ), KIRKI_VERSION, true );
|
||||
$fields = Kirki::$fields;
|
||||
$data = array();
|
||||
foreach ( $fields as $field ) {
|
||||
if ( isset( $field['transport'] ) && 'postMessage' === $field['transport'] && isset( $field['css_vars'] ) && ! empty( $field['css_vars'] ) ) {
|
||||
$data[] = $field;
|
||||
}
|
||||
}
|
||||
wp_localize_script( 'kirki_auto_css_vars', 'kirkiCssVarFields', $data );
|
||||
}
|
||||
}
|
82
functions/kirki/modules/css-vars/script.js
Normal file
82
functions/kirki/modules/css-vars/script.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
/* global kirkiCssVarFields */
|
||||
var kirkiCssVars = {
|
||||
|
||||
/**
|
||||
* Get styles.
|
||||
*
|
||||
* @since 3.0.28
|
||||
* @returns {Object}
|
||||
*/
|
||||
getStyles: function() {
|
||||
var style = jQuery( '#kirki-css-vars' ),
|
||||
styles = style.html().replace( ':root{', '' ).replace( '}', '' ).split( ';' ),
|
||||
stylesObj = {};
|
||||
|
||||
// Format styles as a object we can then tweak.
|
||||
_.each( styles, function( style ) {
|
||||
style = style.split( ':' );
|
||||
if ( style[0] && style[1] ) {
|
||||
stylesObj[ style[0] ] = style[1];
|
||||
}
|
||||
} );
|
||||
return stylesObj;
|
||||
},
|
||||
|
||||
/**
|
||||
* Builds the styles from an object.
|
||||
*
|
||||
* @since 3.0.28
|
||||
* @param {Object} vars - The vars.
|
||||
* @returns {string}
|
||||
*/
|
||||
buildStyle: function( vars ) {
|
||||
var style = '';
|
||||
|
||||
_.each( vars, function( val, name ) {
|
||||
style += name + ':' + val + ';';
|
||||
} );
|
||||
return ':root{' + style + '}';
|
||||
}
|
||||
};
|
||||
|
||||
jQuery( document ).ready( function() {
|
||||
_.each( kirkiCssVarFields, function( field ) {
|
||||
wp.customize( field.settings, function( value ) {
|
||||
value.bind( function( newVal ) {
|
||||
var styles = kirkiCssVars.getStyles();
|
||||
|
||||
_.each( field.css_vars, function( cssVar ) {
|
||||
if ( 'object' === typeof newVal ) {
|
||||
if ( cssVar[2] && newVal[ cssVar[2] ] ) {
|
||||
styles[ cssVar[0] ] = cssVar[1].replace( '$', newVal[ cssVar[2] ] );
|
||||
}
|
||||
} else {
|
||||
styles[ cssVar[0] ] = cssVar[1].replace( '$', newVal );
|
||||
}
|
||||
} );
|
||||
jQuery( '#kirki-css-vars' ).html( kirkiCssVars.buildStyle( styles ) );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
wp.customize.bind( 'preview-ready', function() {
|
||||
wp.customize.preview.bind( 'active', function() {
|
||||
_.each( kirkiCssVarFields, function( field ) {
|
||||
wp.customize( field.settings, function( value ) {
|
||||
var styles = kirkiCssVars.getStyles(),
|
||||
newVal = window.parent.wp.customize( value.id ).get();
|
||||
_.each( field.css_vars, function( cssVar ) {
|
||||
if ( 'object' === typeof newVal ) {
|
||||
if ( cssVar[2] && newVal[ cssVar[2] ] ) {
|
||||
styles[ cssVar[0] ] = cssVar[1].replace( '$', newVal[ cssVar[2] ] );
|
||||
}
|
||||
} else {
|
||||
styles[ cssVar[0] ] = cssVar[1].replace( '$', newVal );
|
||||
}
|
||||
} );
|
||||
jQuery( '#kirki-css-vars' ).html( kirkiCssVars.buildStyle( styles ) );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
143
functions/kirki/modules/css/class-kirki-css-to-file.php
Normal file
143
functions/kirki/modules/css/class-kirki-css-to-file.php
Normal file
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
/**
|
||||
* Writes compiled CSS to a file.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage CSS Module
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handles writing CSS to a file.
|
||||
*/
|
||||
class Kirki_CSS_To_File {
|
||||
|
||||
/**
|
||||
* Fallback to inline CSS?
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @var bool
|
||||
*/
|
||||
protected $fallback = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
// If the file doesn't exist, create it.
|
||||
if ( ! file_exists( $this->get_path( 'file' ) ) ) {
|
||||
|
||||
// If the file-write fails, fallback to inline
|
||||
// and cache the failure so we don't try again immediately.
|
||||
$this->write_file();
|
||||
}
|
||||
add_action( 'customize_save_after', array( $this, 'write_file' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path of the CSS file and folder in the filesystem.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @param string $context Can be "file" or "folder". If empty, returns both as array.
|
||||
* @return string|array
|
||||
*/
|
||||
protected function get_path( $context = '' ) {
|
||||
$upload_dir = wp_upload_dir();
|
||||
$paths = array(
|
||||
'file' => wp_normalize_path( $upload_dir['basedir'] . '/kirki-css/styles.css' ),
|
||||
'folder' => wp_normalize_path( $upload_dir['basedir'] . '/kirki-css' ),
|
||||
);
|
||||
|
||||
if ( 'file' === $context ) {
|
||||
return $paths['file'];
|
||||
}
|
||||
if ( 'folder' === $context ) {
|
||||
return $paths['folder'];
|
||||
}
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL of the CSS file in the filesystem.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return string
|
||||
*/
|
||||
public function get_url() {
|
||||
$upload_dir = wp_upload_dir();
|
||||
return esc_url_raw( $upload_dir['baseurl'] . '/kirki-css/styles.css' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timestamp of the file.
|
||||
* This will be used as "version" for cache-busting purposes.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return integer|false
|
||||
*/
|
||||
public function get_timestamp() {
|
||||
if ( file_exists( $this->get_path( 'file' ) ) ) {
|
||||
return filemtime( $this->get_path( 'file' ) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the file to disk.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return bool
|
||||
*/
|
||||
public function write_file() {
|
||||
$css = array();
|
||||
$configs = Kirki::$config;
|
||||
foreach ( $configs as $config_id => $args ) {
|
||||
// Get the CSS we want to write.
|
||||
$css[ $config_id ] = apply_filters( "kirki_{$config_id}_dynamic_css", Kirki_Modules_CSS::loop_controls( $config_id ) );
|
||||
}
|
||||
$css = implode( $css, '' );
|
||||
|
||||
// If the folder doesn't exist, create it.
|
||||
if ( ! file_exists( $this->get_path( 'folder' ) ) ) {
|
||||
wp_mkdir_p( $this->get_path( 'folder' ) );
|
||||
}
|
||||
|
||||
$filesystem = $this->get_filesystem();
|
||||
$write_file = (bool) $filesystem->put_contents( $this->get_path( 'file' ), $css );
|
||||
if ( ! $write_file ) {
|
||||
$this->fallback = true;
|
||||
set_transient( 'kirki_css_write_to_file_failed', true, HOUR_IN_SECONDS );
|
||||
}
|
||||
return $write_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the WP_Filesystem object.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
protected function get_filesystem() {
|
||||
|
||||
// The WordPress filesystem.
|
||||
global $wp_filesystem;
|
||||
|
||||
if ( empty( $wp_filesystem ) ) {
|
||||
require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/file.php' );
|
||||
WP_Filesystem();
|
||||
}
|
||||
return $wp_filesystem;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
<?php
|
||||
/**
|
||||
* Generates the styles for the frontend.
|
||||
* Handles the 'output' argument of fields
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Core
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 1.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles CSS output.
|
||||
*/
|
||||
final class Kirki_Modules_CSS_Generator {
|
||||
|
||||
/**
|
||||
* The instance of this class (singleton pattern).
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var null|object
|
||||
*/
|
||||
public static $instance = null;
|
||||
|
||||
/**
|
||||
* Settings.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var null|string|array
|
||||
*/
|
||||
public static $settings = null;
|
||||
|
||||
/**
|
||||
* Output.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var array
|
||||
*/
|
||||
public static $output = array();
|
||||
|
||||
/**
|
||||
* Callback.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var null|string|array
|
||||
*/
|
||||
public static $callback = null;
|
||||
|
||||
/**
|
||||
* Option Name.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var null|string
|
||||
*/
|
||||
public static $option_name = null;
|
||||
|
||||
/**
|
||||
* Field Type.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var string
|
||||
*/
|
||||
public static $field_type = null;
|
||||
|
||||
/**
|
||||
* Google Fonts
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var array
|
||||
*/
|
||||
public static $google_fonts = null;
|
||||
|
||||
/**
|
||||
* Standard Fonts
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var array
|
||||
*/
|
||||
public static $backup_fonts = null;
|
||||
|
||||
/**
|
||||
* CSS
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var string
|
||||
*/
|
||||
public static $css;
|
||||
|
||||
/**
|
||||
* Value
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var mixed
|
||||
*/
|
||||
public static $value = null;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*/
|
||||
private function __construct() {
|
||||
if ( is_null( self::$google_fonts ) ) {
|
||||
self::$google_fonts = Kirki_Fonts::get_google_fonts();
|
||||
}
|
||||
if ( is_null( self::$backup_fonts ) ) {
|
||||
self::$backup_fonts = Kirki_Fonts::get_backup_fonts();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single instance of this class
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS for a field.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param array $field The field.
|
||||
* @return array
|
||||
*/
|
||||
public static function css( $field ) {
|
||||
|
||||
// Set class vars.
|
||||
self::$settings = $field['settings'];
|
||||
self::$callback = $field['sanitize_callback'];
|
||||
self::$field_type = $field['type'];
|
||||
self::$output = $field['output'];
|
||||
if ( ! is_array( self::$output ) ) {
|
||||
self::$output = array(
|
||||
array(
|
||||
'element' => self::$output,
|
||||
'sanitize_callback' => null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Get the value of this field.
|
||||
self::$value = Kirki_Values::get_sanitized_field_value( $field );
|
||||
|
||||
// Find the class that will handle the outpout for this field.
|
||||
$classname = 'Kirki_Output';
|
||||
$field_output_classes = apply_filters(
|
||||
"kirki_{$field['kirki_config']}_output_control_classnames", array(
|
||||
'kirki-background' => 'Kirki_Output_Field_Background',
|
||||
'kirki-dimensions' => 'Kirki_Output_Field_Dimensions',
|
||||
'kirki-image' => 'Kirki_Output_Field_Image',
|
||||
'kirki-typography' => 'Kirki_Output_Field_Typography',
|
||||
'kirki-multicolor' => 'Kirki_Output_Field_Multicolor',
|
||||
)
|
||||
);
|
||||
if ( array_key_exists( self::$field_type, $field_output_classes ) ) {
|
||||
$classname = $field_output_classes[ self::$field_type ];
|
||||
}
|
||||
$obj = new $classname( $field['kirki_config'], self::$output, self::$value, $field );
|
||||
return $obj->get_styles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of generated styles and creates the minimized, inline CSS.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param array $css The CSS definitions array.
|
||||
* @return string The generated CSS.
|
||||
*/
|
||||
public static function styles_parse( $css = array() ) {
|
||||
|
||||
// Pass our styles from the kirki_styles_array filter.
|
||||
$css = apply_filters( 'kirki_styles_array', $css );
|
||||
|
||||
// Process the array of CSS properties and produce the final CSS.
|
||||
$final_css = '';
|
||||
if ( ! is_array( $css ) || empty( $css ) ) {
|
||||
return '';
|
||||
}
|
||||
foreach ( $css as $media_query => $styles ) {
|
||||
$final_css .= ( 'global' !== $media_query ) ? $media_query . '{' : '';
|
||||
foreach ( $styles as $style => $style_array ) {
|
||||
$css_for_style = '';
|
||||
|
||||
foreach ( $style_array as $property => $value ) {
|
||||
if ( is_string( $value ) && '' !== $value ) {
|
||||
$css_for_style .= $property . ':' . $value . ';';
|
||||
} elseif ( is_array( $value ) ) {
|
||||
foreach ( $value as $subvalue ) {
|
||||
if ( is_string( $subvalue ) && '' !== $subvalue ) {
|
||||
$css_for_style .= $property . ':' . $subvalue . ';';
|
||||
}
|
||||
}
|
||||
}
|
||||
$value = ( is_string( $value ) ) ? $value : '';
|
||||
}
|
||||
if ( '' !== $css_for_style ) {
|
||||
$final_css .= $style . '{' . $css_for_style . '}';
|
||||
}
|
||||
}
|
||||
$final_css .= ( 'global' !== $media_query ) ? '}' : '';
|
||||
}
|
||||
return $final_css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add prefixes if necessary.
|
||||
*
|
||||
* @param array $css The CSS definitions array.
|
||||
* @return array
|
||||
*/
|
||||
public static function add_prefixes( $css ) {
|
||||
if ( is_array( $css ) ) {
|
||||
foreach ( $css as $media_query => $elements ) {
|
||||
foreach ( $elements as $element => $style_array ) {
|
||||
foreach ( $style_array as $property => $value ) {
|
||||
|
||||
// Add -webkit-* and -moz-*.
|
||||
if ( is_string( $property ) && in_array(
|
||||
$property, array(
|
||||
'border-radius',
|
||||
'box-shadow',
|
||||
'box-sizing',
|
||||
'text-shadow',
|
||||
'transform',
|
||||
'background-size',
|
||||
'transition',
|
||||
'transition-property',
|
||||
), true
|
||||
) ) {
|
||||
unset( $css[ $media_query ][ $element ][ $property ] );
|
||||
$css[ $media_query ][ $element ][ '-webkit-' . $property ] = $value;
|
||||
$css[ $media_query ][ $element ][ '-moz-' . $property ] = $value;
|
||||
$css[ $media_query ][ $element ][ $property ] = $value;
|
||||
}
|
||||
|
||||
// Add -ms-* and -o-*.
|
||||
if ( is_string( $property ) && in_array(
|
||||
$property, array(
|
||||
'transform',
|
||||
'background-size',
|
||||
'transition',
|
||||
'transition-property',
|
||||
), true
|
||||
) ) {
|
||||
unset( $css[ $media_query ][ $element ][ $property ] );
|
||||
$css[ $media_query ][ $element ][ '-ms-' . $property ] = $value;
|
||||
$css[ $media_query ][ $element ][ '-o-' . $property ] = $value;
|
||||
$css[ $media_query ][ $element ][ $property ] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $css;
|
||||
}
|
||||
}
|
334
functions/kirki/modules/css/class-kirki-modules-css.php
Normal file
334
functions/kirki/modules/css/class-kirki-modules-css.php
Normal file
|
@ -0,0 +1,334 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles the CSS Output of fields.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Kirki_Modules_CSS object.
|
||||
*/
|
||||
class Kirki_Modules_CSS {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Whether we've already processed this or not.
|
||||
*
|
||||
* @access public
|
||||
* @var bool
|
||||
*/
|
||||
public $processed = false;
|
||||
|
||||
/**
|
||||
* The CSS array
|
||||
*
|
||||
* @access public
|
||||
* @var array
|
||||
*/
|
||||
public static $css_array = array();
|
||||
|
||||
/**
|
||||
* Set to true if you want to use the AJAX method.
|
||||
*
|
||||
* @access public
|
||||
* @var bool
|
||||
*/
|
||||
public static $ajax = false;
|
||||
|
||||
/**
|
||||
* The Kirki_CSS_To_File object.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
protected $css_to_file;
|
||||
|
||||
/**
|
||||
* Should we enqueue font-awesome?
|
||||
*
|
||||
* @static
|
||||
* @access protected
|
||||
* @since 3.0.26
|
||||
* @var bool
|
||||
*/
|
||||
protected static $enqueue_fa = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function __construct() {
|
||||
$class_files = array(
|
||||
'Kirki_CSS_To_File' => '/class-kirki-css-to-file.php',
|
||||
'Kirki_Modules_CSS_Generator' => '/class-kirki-modules-css-generator.php',
|
||||
'Kirki_Output' => '/class-kirki-output.php',
|
||||
'Kirki_Output_Field_Background' => '/field/class-kirki-output-field-background.php',
|
||||
'Kirki_Output_Field_Image' => '/field/class-kirki-output-field-image.php',
|
||||
'Kirki_Output_Field_Multicolor' => '/field/class-kirki-output-field-multicolor.php',
|
||||
'Kirki_Output_Field_Dimensions' => '/field/class-kirki-output-field-dimensions.php',
|
||||
'Kirki_Output_Field_Typography' => '/field/class-kirki-output-field-typography.php',
|
||||
'Kirki_Output_Property' => '/property/class-kirki-output-property.php',
|
||||
'Kirki_Output_Property_Background_Image' => '/property/class-kirki-output-property-background-image.php',
|
||||
'Kirki_Output_Property_Background_Position' => '/property/class-kirki-output-property-background-position.php',
|
||||
'Kirki_Output_Property_Font_Family' => '/property/class-kirki-output-property-font-family.php',
|
||||
);
|
||||
|
||||
foreach ( $class_files as $class_name => $file ) {
|
||||
if ( ! class_exists( $class_name ) ) {
|
||||
include_once wp_normalize_path( dirname( __FILE__ ) . $file );
|
||||
}
|
||||
}
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function init() {
|
||||
global $wp_customize;
|
||||
|
||||
Kirki_Modules_Webfonts::get_instance();
|
||||
|
||||
$config = apply_filters( 'kirki_config', array() );
|
||||
$priority = 999;
|
||||
if ( isset( $config['styles_priority'] ) ) {
|
||||
$priority = absint( $config['styles_priority'] );
|
||||
}
|
||||
|
||||
// Allow completely disabling Kirki CSS output.
|
||||
if ( ( defined( 'KIRKI_NO_OUTPUT' ) && true === KIRKI_NO_OUTPUT ) || ( isset( $config['disable_output'] ) && true === $config['disable_output'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$method = apply_filters( 'kirki_dynamic_css_method', 'inline' );
|
||||
if ( $wp_customize ) {
|
||||
|
||||
// If we're in the customizer, load inline no matter what.
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'inline_dynamic_css' ), $priority );
|
||||
add_action( 'enqueue_block_editor_assets', array( $this, 'inline_dynamic_css' ), $priority );
|
||||
|
||||
// If we're using file method, on save write the new styles.
|
||||
if ( 'file' === $method ) {
|
||||
$this->css_to_file = new Kirki_CSS_To_File();
|
||||
add_action( 'customize_save_after', array( $this->css_to_file, 'write_file' ) );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'file' === $method ) {
|
||||
|
||||
// Attempt to write the CSS to file.
|
||||
$this->css_to_file = new Kirki_CSS_To_File();
|
||||
|
||||
// If we succesd, load this file.
|
||||
$failed = get_transient( 'kirki_css_write_to_file_failed' );
|
||||
|
||||
// If writing CSS to file hasn't failed, just enqueue this file.
|
||||
if ( ! $failed ) {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_compiled_file' ), $priority );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we are in the customizer, load CSS using inline-styles.
|
||||
// If we are in the frontend AND self::$ajax is true, then load dynamic CSS using AJAX.
|
||||
if ( ( true === self::$ajax ) || ( isset( $config['inline_css'] ) && false === $config['inline_css'] ) ) {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'frontend_styles' ), $priority );
|
||||
add_action( 'wp_ajax_kirki_dynamic_css', array( $this, 'ajax_dynamic_css' ) );
|
||||
add_action( 'wp_ajax_nopriv_kirki_dynamic_css', array( $this, 'ajax_dynamic_css' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// If we got this far then add styles inline.
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'inline_dynamic_css' ), $priority );
|
||||
|
||||
// Admin styles, adds Gutenberg compatibility.
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'inline_dynamic_css' ), $priority );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues compiled CSS file.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function enqueue_compiled_file() {
|
||||
wp_enqueue_style( 'kirki-styles', $this->css_to_file->get_url(), array(), $this->css_to_file->get_timestamp() );
|
||||
}
|
||||
/**
|
||||
* Adds inline styles.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function inline_dynamic_css() {
|
||||
$configs = Kirki::$config;
|
||||
if ( ! $this->processed ) {
|
||||
foreach ( $configs as $config_id => $args ) {
|
||||
if ( isset( $args['disable_output'] ) && true === $args['disable_output'] ) {
|
||||
continue;
|
||||
}
|
||||
$styles = self::loop_controls( $config_id );
|
||||
$styles = apply_filters( "kirki_{$config_id}_dynamic_css", $styles );
|
||||
if ( ! empty( $styles ) ) {
|
||||
$stylesheet = apply_filters( "kirki_{$config_id}_stylesheet", false );
|
||||
if ( $stylesheet ) {
|
||||
wp_add_inline_style( $stylesheet, $styles );
|
||||
continue;
|
||||
}
|
||||
wp_enqueue_style( 'kirki-styles-' . $config_id, trailingslashit( Kirki::$url ) . 'assets/css/kirki-styles.css', array(), KIRKI_VERSION );
|
||||
wp_add_inline_style( 'kirki-styles-' . $config_id, $styles );
|
||||
}
|
||||
}
|
||||
$this->processed = true;
|
||||
}
|
||||
|
||||
if ( self::$enqueue_fa && apply_filters( 'kirki_load_fontawesome', true ) ) {
|
||||
wp_enqueue_script( 'kirki-fontawesome-font', 'https://use.fontawesome.com/30858dc40a.js', array(), '4.0.7', true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dynamic-css.php file
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function ajax_dynamic_css() {
|
||||
require wp_normalize_path( Kirki::$path . '/modules/css/dynamic-css.php' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the ajax stylesheet.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function frontend_styles() {
|
||||
wp_enqueue_style( 'kirki-styles-php', admin_url( 'admin-ajax.php' ) . '?action=kirki_dynamic_css', array(), KIRKI_VERSION );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop through all fields and create an array of style definitions.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $config_id The configuration ID.
|
||||
*/
|
||||
public static function loop_controls( $config_id ) {
|
||||
|
||||
// Get an instance of the Kirki_Modules_CSS_Generator class.
|
||||
// This will make sure google fonts and backup fonts are loaded.
|
||||
Kirki_Modules_CSS_Generator::get_instance();
|
||||
|
||||
$fields = Kirki::$fields;
|
||||
$css = array();
|
||||
|
||||
// Early exit if no fields are found.
|
||||
if ( empty( $fields ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $fields as $field ) {
|
||||
|
||||
// Only process fields that belong to $config_id.
|
||||
if ( $config_id !== $field['kirki_config'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( true === apply_filters( "kirki_{$config_id}_css_skip_hidden", true ) ) {
|
||||
|
||||
// Only continue if field dependencies are met.
|
||||
if ( ! empty( $field['required'] ) ) {
|
||||
$valid = true;
|
||||
|
||||
foreach ( $field['required'] as $requirement ) {
|
||||
if ( isset( $requirement['setting'] ) && isset( $requirement['value'] ) && isset( $requirement['operator'] ) ) {
|
||||
$controller_value = Kirki_Values::get_value( $config_id, $requirement['setting'] );
|
||||
if ( ! Kirki_Helper::compare_values( $controller_value, $requirement['value'], $requirement['operator'] ) ) {
|
||||
$valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $valid ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only continue if $field['output'] is set.
|
||||
if ( isset( $field['output'] ) && ! empty( $field['output'] ) ) {
|
||||
$css = Kirki_Helper::array_replace_recursive( $css, Kirki_Modules_CSS_Generator::css( $field ) );
|
||||
|
||||
// Add the globals.
|
||||
if ( isset( self::$css_array[ $config_id ] ) && ! empty( self::$css_array[ $config_id ] ) ) {
|
||||
Kirki_Helper::array_replace_recursive( $css, self::$css_array[ $config_id ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$css = apply_filters( "kirki_{$config_id}_styles", $css );
|
||||
|
||||
if ( is_array( $css ) ) {
|
||||
return Kirki_Modules_CSS_Generator::styles_parse( Kirki_Modules_CSS_Generator::add_prefixes( $css ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs when we're adding a font-awesome field to allow enqueueing the
|
||||
* fontawesome script on the frontend.
|
||||
*
|
||||
* @static
|
||||
* @since 3.0.26
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function add_fontawesome_script() {
|
||||
self::$enqueue_fa = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if FontAwesome should be loaded.
|
||||
*
|
||||
* @static
|
||||
* @since 3.0.35
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function get_enqueue_fa() {
|
||||
return self::$enqueue_fa;
|
||||
}
|
||||
}
|
345
functions/kirki/modules/css/class-kirki-output.php
Normal file
345
functions/kirki/modules/css/class-kirki-output.php
Normal file
|
@ -0,0 +1,345 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles CSS output for fields.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Controls
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handles field CSS output.
|
||||
*/
|
||||
class Kirki_Output {
|
||||
|
||||
/**
|
||||
* The Kirki configuration used in the field.
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $config_id = 'global';
|
||||
|
||||
/**
|
||||
* The field's `output` argument.
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $output = array();
|
||||
|
||||
/**
|
||||
* An array of the generated styles.
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $styles = array();
|
||||
|
||||
/**
|
||||
* The field.
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $field = array();
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*
|
||||
* @access protected
|
||||
* @var string|array
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @access public
|
||||
* @param string $config_id The config ID.
|
||||
* @param array $output The output argument.
|
||||
* @param string|array $value The value.
|
||||
* @param array $field The field.
|
||||
*/
|
||||
public function __construct( $config_id, $output, $value, $field ) {
|
||||
$this->config_id = $config_id;
|
||||
$this->value = $value;
|
||||
$this->output = $output;
|
||||
$this->field = $field;
|
||||
|
||||
$this->parse_output();
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have a sanitize_callback defined, apply it to the value.
|
||||
*
|
||||
* @param array $output The output args.
|
||||
* @param string|array $value The value.
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
protected function apply_sanitize_callback( $output, $value ) {
|
||||
if ( isset( $output['sanitize_callback'] ) && null !== $output['sanitize_callback'] ) {
|
||||
|
||||
// If the sanitize_callback is invalid, return the value.
|
||||
if ( ! is_callable( $output['sanitize_callback'] ) ) {
|
||||
return $value;
|
||||
}
|
||||
return call_user_func( $output['sanitize_callback'], $this->value );
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have a value_pattern defined, apply it to the value.
|
||||
*
|
||||
* @param array $output The output args.
|
||||
* @param string|array $value The value.
|
||||
* @return string|array
|
||||
*/
|
||||
protected function apply_value_pattern( $output, $value ) {
|
||||
if ( isset( $output['value_pattern'] ) && ! empty( $output['value_pattern'] ) && is_string( $output['value_pattern'] ) ) {
|
||||
if ( ! is_array( $value ) ) {
|
||||
$value = str_replace( '$', $value, $output['value_pattern'] );
|
||||
}
|
||||
if ( is_array( $value ) ) {
|
||||
foreach ( array_keys( $value ) as $value_k ) {
|
||||
if ( ! is_string( $value[ $value_k ] ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( isset( $output['choice'] ) ) {
|
||||
if ( $output['choice'] === $value_k ) {
|
||||
$value[ $output['choice'] ] = str_replace( '$', $value[ $output['choice'] ], $output['value_pattern'] );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$value[ $value_k ] = str_replace( '$', $value[ $value_k ], $output['value_pattern'] );
|
||||
}
|
||||
}
|
||||
$value = $this->apply_pattern_replace( $output, $value );
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have a value_pattern defined, apply it to the value.
|
||||
*
|
||||
* @param array $output The output args.
|
||||
* @param string|array $value The value.
|
||||
* @return string|array
|
||||
*/
|
||||
protected function apply_pattern_replace( $output, $value ) {
|
||||
if ( isset( $output['pattern_replace'] ) && is_array( $output['pattern_replace'] ) ) {
|
||||
$option_type = ( '' !== Kirki::get_config_param( $this->config_id, 'option_type' ) ) ? Kirki::get_config_param( $this->config_id, 'option_type' ) : 'theme_mod';
|
||||
$option_name = Kirki::get_config_param( $this->config_id, 'option_name' );
|
||||
$options = array();
|
||||
if ( $option_name ) {
|
||||
$options = ( 'site_option' === $option_type ) ? get_site_option( $option_name ) : get_option( $option_name );
|
||||
}
|
||||
foreach ( $output['pattern_replace'] as $search => $replace ) {
|
||||
$replacement = '';
|
||||
switch ( $option_type ) {
|
||||
case 'option':
|
||||
if ( is_array( $options ) ) {
|
||||
if ( $option_name ) {
|
||||
$subkey = str_replace( array( $option_name, '[', ']' ), '', $replace );
|
||||
$replacement = ( isset( $options[ $subkey ] ) ) ? $options[ $subkey ] : '';
|
||||
break;
|
||||
}
|
||||
$replacement = ( isset( $options[ $replace ] ) ) ? $options[ $replace ] : '';
|
||||
break;
|
||||
}
|
||||
$replacement = get_option( $replace );
|
||||
break;
|
||||
case 'site_option':
|
||||
$replacement = ( is_array( $options ) && isset( $options[ $replace ] ) ) ? $options[ $replace ] : get_site_option( $replace );
|
||||
break;
|
||||
case 'user_meta':
|
||||
$user_id = get_current_user_id();
|
||||
if ( $user_id ) {
|
||||
$replacement = get_user_meta( $user_id, $replace, true );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$replacement = get_theme_mod( $replace );
|
||||
}
|
||||
$replacement = ( false === $replacement ) ? '' : $replacement;
|
||||
if ( is_array( $value ) ) {
|
||||
foreach ( $value as $k => $v ) {
|
||||
$_val = ( isset( $value[ $v ] ) ) ? $value[ $v ] : $v;
|
||||
$value[ $k ] = str_replace( $search, $replacement, $_val );
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
$value = str_replace( $search, $replacement, $value );
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the output arguments.
|
||||
* Calls the process_output method for each of them.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function parse_output() {
|
||||
foreach ( $this->output as $output ) {
|
||||
$skip = false;
|
||||
|
||||
// Apply any sanitization callbacks defined.
|
||||
$value = $this->apply_sanitize_callback( $output, $this->value );
|
||||
|
||||
// Skip if value is empty.
|
||||
if ( '' === $this->value ) {
|
||||
$skip = true;
|
||||
}
|
||||
|
||||
// No need to proceed this if the current value is the same as in the "exclude" value.
|
||||
if ( isset( $output['exclude'] ) && is_array( $output['exclude'] ) ) {
|
||||
foreach ( $output['exclude'] as $exclude ) {
|
||||
if ( is_array( $value ) ) {
|
||||
if ( is_array( $exclude ) ) {
|
||||
$diff1 = array_diff( $value, $exclude );
|
||||
$diff2 = array_diff( $exclude, $value );
|
||||
|
||||
if ( empty( $diff1 ) && empty( $diff2 ) ) {
|
||||
$skip = true;
|
||||
}
|
||||
}
|
||||
// If 'choice' is defined check for sub-values too.
|
||||
// Fixes https://github.com/aristath/kirki/issues/1416.
|
||||
if ( isset( $output['choice'] ) && isset( $value[ $output['choice'] ] ) && $exclude == $value[ $output['choice'] ] ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
|
||||
$skip = true;
|
||||
}
|
||||
}
|
||||
if ( $skip ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if value is defined as excluded.
|
||||
if ( $exclude === $value || ( '' === $exclude && empty( $value ) ) ) {
|
||||
$skip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $skip ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply any value patterns defined.
|
||||
$value = $this->apply_value_pattern( $output, $value );
|
||||
|
||||
if ( isset( $output['element'] ) && is_array( $output['element'] ) ) {
|
||||
$output['element'] = array_unique( $output['element'] );
|
||||
sort( $output['element'] );
|
||||
$output['element'] = implode( ',', $output['element'] );
|
||||
}
|
||||
|
||||
$value = $this->process_value( $value, $output );
|
||||
|
||||
if ( is_admin() && ! is_customize_preview() ) {
|
||||
|
||||
// Check if this is an admin style.
|
||||
if ( ! isset( $output['context'] ) || ! in_array( 'editor', $output['context'] ) ) {
|
||||
continue;
|
||||
}
|
||||
} elseif ( isset( $output['context'] ) && ! in_array( 'front', $output['context'] ) ) {
|
||||
|
||||
// Check if this is a frontend style.
|
||||
continue;
|
||||
}
|
||||
$this->process_output( $output, $value );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an output and creates the styles array for it.
|
||||
*
|
||||
* @access protected
|
||||
* @param array $output The field output.
|
||||
* @param string|array $value The value.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function process_output( $output, $value ) {
|
||||
if ( ! isset( $output['element'] ) || ! isset( $output['property'] ) ) {
|
||||
return;
|
||||
}
|
||||
$output['media_query'] = ( isset( $output['media_query'] ) ) ? $output['media_query'] : 'global';
|
||||
$output['prefix'] = ( isset( $output['prefix'] ) ) ? $output['prefix'] : '';
|
||||
$output['units'] = ( isset( $output['units'] ) ) ? $output['units'] : '';
|
||||
$output['suffix'] = ( isset( $output['suffix'] ) ) ? $output['suffix'] : '';
|
||||
|
||||
// Properties that can accept multiple values.
|
||||
// Useful for example for gradients where all browsers use the "background-image" property
|
||||
// and the browser prefixes go in the value_pattern arg.
|
||||
$accepts_multiple = array(
|
||||
'background-image',
|
||||
'background',
|
||||
);
|
||||
if ( in_array( $output['property'], $accepts_multiple, true ) ) {
|
||||
if ( isset( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) && ! is_array( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) ) {
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = (array) $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ];
|
||||
}
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ][] = $output['prefix'] . $value . $output['units'] . $output['suffix'];
|
||||
return;
|
||||
}
|
||||
if ( is_string( $value ) || is_numeric( $value ) ) {
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value ) . $output['units'] . $output['suffix'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some CSS properties are unique.
|
||||
* We need to tweak the value to make everything works as expected.
|
||||
*
|
||||
* @access protected
|
||||
* @param string $property The CSS property.
|
||||
* @param string|array $value The value.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function process_property_value( $property, $value ) {
|
||||
$properties = apply_filters(
|
||||
"kirki_{$this->config_id}_output_property_classnames", array(
|
||||
'font-family' => 'Kirki_Output_Property_Font_Family',
|
||||
'background-image' => 'Kirki_Output_Property_Background_Image',
|
||||
'background-position' => 'Kirki_Output_Property_Background_Position',
|
||||
)
|
||||
);
|
||||
if ( array_key_exists( $property, $properties ) ) {
|
||||
$classname = $properties[ $property ];
|
||||
$obj = new $classname( $property, $value );
|
||||
return $obj->get_value();
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value.
|
||||
*
|
||||
* @access protected
|
||||
* @param string|array $value The value.
|
||||
* @param array $output The field "output".
|
||||
* @return string|array
|
||||
*/
|
||||
protected function process_value( $value, $output ) {
|
||||
if ( isset( $output['property'] ) ) {
|
||||
return $this->process_property_value( $output['property'], $value );
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exploses the private $styles property to the world
|
||||
*
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
public function get_styles() {
|
||||
return $this->styles;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles CSS output for background fields.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Controls
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Output overrides.
|
||||
*/
|
||||
class Kirki_Output_Field_Background extends Kirki_Output {
|
||||
|
||||
/**
|
||||
* Processes a single item from the `output` array.
|
||||
*
|
||||
* @access protected
|
||||
* @param array $output The `output` item.
|
||||
* @param array $value The field's value.
|
||||
*/
|
||||
protected function process_output( $output, $value ) {
|
||||
$output = wp_parse_args(
|
||||
$output,
|
||||
array(
|
||||
'media_query' => 'global',
|
||||
'element' => 'body',
|
||||
'prefix' => '',
|
||||
'suffix' => '',
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( array( 'background-image', 'background-color', 'background-repeat', 'background-position', 'background-size', 'background-attachment' ) as $property ) {
|
||||
|
||||
// See https://github.com/aristath/kirki/issues/1808.
|
||||
if ( 'background-color' === $property && isset( $value['background-color'] ) && $value['background-color'] && ( ! isset( $value['background-image'] ) || empty( $value['background-image'] ) ) ) {
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ]['background'] = $output['prefix'] . $this->process_property_value( $property, $value[ $property ] ) . $output['suffix'];
|
||||
}
|
||||
|
||||
if ( isset( $value[ $property ] ) && ! empty( $value[ $property ] ) ) {
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ][ $property ] = $output['prefix'] . $this->process_property_value( $property, $value[ $property ] ) . $output['suffix'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles CSS output for dimensions fields.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Controls
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Output overrides.
|
||||
*/
|
||||
class Kirki_Output_Field_Dimensions extends Kirki_Output {
|
||||
|
||||
/**
|
||||
* Processes a single item from the `output` array.
|
||||
*
|
||||
* @access protected
|
||||
* @param array $output The `output` item.
|
||||
* @param array $value The field's value.
|
||||
*/
|
||||
protected function process_output( $output, $value ) {
|
||||
$output = wp_parse_args(
|
||||
$output, array(
|
||||
'element' => '',
|
||||
'property' => '',
|
||||
'media_query' => 'global',
|
||||
'prefix' => '',
|
||||
'suffix' => '',
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! is_array( $value ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( array_keys( $value ) as $key ) {
|
||||
|
||||
$property = ( empty( $output['property'] ) ) ? $key : $output['property'] . '-' . $key;
|
||||
if ( isset( $output['choice'] ) && $output['property'] ) {
|
||||
if ( $key === $output['choice'] ) {
|
||||
$property = $output['property'];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( false !== strpos( $output['property'], '%%' ) ) {
|
||||
$property = str_replace( '%%', $key, $output['property'] );
|
||||
}
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ][ $property ] = $output['prefix'] . $this->process_property_value( $property, $value[ $key ] ) . $output['suffix'];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles CSS output for image fields.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Controls
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.10
|
||||
*/
|
||||
|
||||
/**
|
||||
* Output overrides.
|
||||
*/
|
||||
class Kirki_Output_Field_Image extends Kirki_Output {
|
||||
|
||||
/**
|
||||
* Processes a single item from the `output` array.
|
||||
*
|
||||
* @access protected
|
||||
* @param array $output The `output` item.
|
||||
* @param array $value The field's value.
|
||||
*/
|
||||
protected function process_output( $output, $value ) {
|
||||
if ( ! isset( $output['element'] ) || ! isset( $output['property'] ) ) {
|
||||
return;
|
||||
}
|
||||
$output = wp_parse_args(
|
||||
$output, array(
|
||||
'media_query' => 'global',
|
||||
'prefix' => '',
|
||||
'units' => '',
|
||||
'suffix' => '',
|
||||
)
|
||||
);
|
||||
if ( is_array( $value ) ) {
|
||||
if ( isset( $output['choice'] ) && $output['choice'] ) {
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value[ $output['choice'] ] ) . $output['units'] . $output['suffix'];
|
||||
return;
|
||||
}
|
||||
if ( isset( $value['url'] ) ) {
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value['url'] ) . $output['units'] . $output['suffix'];
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value ) . $output['units'] . $output['suffix'];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles CSS output for multicolor fields.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Controls
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Output overrides.
|
||||
*/
|
||||
class Kirki_Output_Field_Multicolor extends Kirki_Output {
|
||||
|
||||
/**
|
||||
* Processes a single item from the `output` array.
|
||||
*
|
||||
* @access protected
|
||||
* @param array $output The `output` item.
|
||||
* @param array $value The field's value.
|
||||
*/
|
||||
protected function process_output( $output, $value ) {
|
||||
foreach ( $value as $key => $sub_value ) {
|
||||
|
||||
// If "element" is not defined, there's no reason to continue.
|
||||
if ( ! isset( $output['element'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the "choice" is not the same as the $key in our loop, there's no reason to proceed.
|
||||
if ( isset( $output['choice'] ) && $key !== $output['choice'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If "property" is not defined, fallback to the $key.
|
||||
$property = ( ! isset( $output['property'] ) || empty( $output['property'] ) ) ? $key : $output['property'];
|
||||
|
||||
// If "media_query" is not defined, use "global".
|
||||
if ( ! isset( $output['media_query'] ) || empty( $output['media_query'] ) ) {
|
||||
$output['media_query'] = 'global';
|
||||
}
|
||||
|
||||
// If "suffix" is defined, add it to the value.
|
||||
$output['suffix'] = ( isset( $output['suffix'] ) ) ? $output['suffix'] : '';
|
||||
|
||||
// Create the styles.
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ][ $property ] = $sub_value . $output['suffix'];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles CSS output for typography fields.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Controls
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Output overrides.
|
||||
*/
|
||||
class Kirki_Output_Field_Typography extends Kirki_Output {
|
||||
|
||||
/**
|
||||
* Processes a single item from the `output` array.
|
||||
*
|
||||
* @access protected
|
||||
* @param array $output The `output` item.
|
||||
* @param array $value The field's value.
|
||||
*/
|
||||
protected function process_output( $output, $value ) {
|
||||
$output['media_query'] = ( isset( $output['media_query'] ) ) ? $output['media_query'] : 'global';
|
||||
$output['element'] = ( isset( $output['element'] ) ) ? $output['element'] : 'body';
|
||||
$output['prefix'] = ( isset( $output['prefix'] ) ) ? $output['prefix'] : '';
|
||||
$output['suffix'] = ( isset( $output['suffix'] ) ) ? $output['suffix'] : '';
|
||||
|
||||
$value = Kirki_Field_Typography::sanitize( $value );
|
||||
|
||||
$properties = array(
|
||||
'font-family',
|
||||
'font-size',
|
||||
'variant',
|
||||
'font-weight',
|
||||
'font-style',
|
||||
'letter-spacing',
|
||||
'word-spacing',
|
||||
'line-height',
|
||||
'text-align',
|
||||
'text-transform',
|
||||
'text-decoration',
|
||||
'color',
|
||||
);
|
||||
|
||||
foreach ( $properties as $property ) {
|
||||
|
||||
// Early exit if the value is not in the defaults.
|
||||
if ( ! isset( $this->field['default'][ $property ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Early exit if the value is not saved in the values.
|
||||
if ( ! isset( $value[ $property ] ) || ! $value[ $property ] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Early exit if we use "choice" but not for this property.
|
||||
if ( isset( $output['choice'] ) && $output['choice'] !== $property ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Take care of variants.
|
||||
if ( 'variant' === $property && isset( $value['variant'] ) && ! empty( $value['variant'] ) ) {
|
||||
|
||||
// Get the font_weight.
|
||||
$font_weight = str_replace( 'italic', '', $value['variant'] );
|
||||
$font_weight = ( in_array( $font_weight, array( '', 'regular' ), true ) ) ? '400' : $font_weight;
|
||||
|
||||
// Is this italic?
|
||||
$is_italic = ( false !== strpos( $value['variant'], 'italic' ) );
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ]['font-weight'] = $font_weight;
|
||||
if ( $is_italic ) {
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ]['font-style'] = 'italic';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$property_value = $this->process_property_value( $property, $value[ $property ] );
|
||||
if ( 'font-family' === $property ) {
|
||||
$value['font-backup'] = ( isset( $value['font-backup'] ) ) ? $value['font-backup'] : '';
|
||||
$property_value = $this->process_property_value(
|
||||
$property, array(
|
||||
$value['font-family'],
|
||||
$value['font-backup'],
|
||||
)
|
||||
);
|
||||
}
|
||||
$property = ( isset( $output['choice'] ) && isset( $output['property'] ) ) ? $output['property'] : $property;
|
||||
$property_value = ( is_array( $property_value ) && isset( $property_value[0] ) ) ? $property_value[0] : $property_value;
|
||||
$this->styles[ $output['media_query'] ][ $output['element'] ][ $property ] = $output['prefix'] . $property_value . $output['suffix'];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles CSS output for background-image.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Controls
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Output overrides.
|
||||
*/
|
||||
class Kirki_Output_Property_Background_Image extends Kirki_Output_Property {
|
||||
|
||||
/**
|
||||
* Modifies the value.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function process_value() {
|
||||
if ( is_array( $this->value ) && isset( $this->value['url'] ) ) {
|
||||
$this->value = $this->value['url'];
|
||||
}
|
||||
if ( false === strpos( $this->value, 'gradient' ) && false === strpos( $this->value, 'url(' ) ) {
|
||||
if ( empty( $this->value ) ) {
|
||||
return;
|
||||
}
|
||||
if ( preg_match( '/^\d+$/', $this->value ) ) {
|
||||
$this->value = 'url("' . set_url_scheme( wp_get_attachment_url( $this->value ) ) . '")';
|
||||
} else {
|
||||
$this->value = 'url("' . set_url_scheme( $this->value ) . '")';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles CSS output for background-position.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Controls
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Output overrides.
|
||||
*/
|
||||
class Kirki_Output_Property_Background_Position extends Kirki_Output_Property {
|
||||
|
||||
/**
|
||||
* Modifies the value.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function process_value() {
|
||||
$this->value = trim( $this->value );
|
||||
|
||||
// If you use calc() there, I suppose you know what you're doing.
|
||||
// No need to process this any further, just exit.
|
||||
if ( false !== strpos( $this->value, 'calc' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the value is initial or inherit, we don't need to do anything.
|
||||
// Just exit.
|
||||
if ( 'initial' === $this->value || 'inherit' === $this->value ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$x_dimensions = array( 'left', 'center', 'right' );
|
||||
$y_dimensions = array( 'top', 'center', 'bottom' );
|
||||
|
||||
// If there's a space, we have an X and a Y value.
|
||||
if ( false !== strpos( $this->value, ' ' ) ) {
|
||||
$xy = explode( ' ', $this->value );
|
||||
|
||||
$x = trim( $xy[0] );
|
||||
$y = trim( $xy[1] );
|
||||
|
||||
// If x is not left/center/right, we need to sanitize it.
|
||||
if ( ! in_array( $x, $x_dimensions, true ) ) {
|
||||
$x = sanitize_text_field( $x );
|
||||
}
|
||||
if ( ! in_array( $y, $y_dimensions, true ) ) {
|
||||
$y = sanitize_text_field( $y );
|
||||
}
|
||||
$this->value = $x . ' ' . $y;
|
||||
return;
|
||||
}
|
||||
$x = 'center';
|
||||
foreach ( $x_dimensions as $x_dimension ) {
|
||||
if ( false !== strpos( $this->value, $x_dimension ) ) {
|
||||
$x = $x_dimension;
|
||||
}
|
||||
}
|
||||
$y = 'center';
|
||||
foreach ( $y_dimensions as $y_dimension ) {
|
||||
if ( false !== strpos( $this->value, $y_dimension ) ) {
|
||||
$y = $y_dimension;
|
||||
}
|
||||
}
|
||||
$this->value = $x . ' ' . $y;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles CSS output for font-family.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Controls
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Output overrides.
|
||||
*/
|
||||
class Kirki_Output_Property_Font_Family extends Kirki_Output_Property {
|
||||
|
||||
/**
|
||||
* Modifies the value.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function process_value() {
|
||||
$google_fonts_array = Kirki_Fonts::get_google_fonts();
|
||||
$backup_fonts = Kirki_Fonts::get_backup_fonts();
|
||||
|
||||
$family = $this->value;
|
||||
$backup = '';
|
||||
if ( is_array( $this->value ) && isset( $this->value[0] ) && isset( $this->value[1] ) ) {
|
||||
$family = $this->value[0];
|
||||
$backup = $this->value[1];
|
||||
}
|
||||
|
||||
// Make sure the value is a string.
|
||||
// If not, then early exit.
|
||||
if ( ! is_string( $family ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hack for standard fonts.
|
||||
$family = str_replace( '"', '"', $family );
|
||||
|
||||
// Add backup font.
|
||||
if ( Kirki_Fonts::is_google_font( $family ) ) {
|
||||
|
||||
if ( '' === $backup && isset( $google_fonts_array[ $family ] ) && isset( $backup_fonts[ $google_fonts_array[ $family ]['category'] ] ) ) {
|
||||
$backup = $backup_fonts[ $google_fonts_array[ $family ]['category'] ];
|
||||
}
|
||||
|
||||
// Add double quotes if needed.
|
||||
if ( false !== strpos( $family, ' ' ) && false === strpos( $family, '"' ) ) {
|
||||
$this->value = '"' . $family . '", ' . $backup;
|
||||
return;
|
||||
}
|
||||
$this->value = $family . ', ' . $backup;
|
||||
return;
|
||||
}
|
||||
|
||||
// Add double quotes if needed.
|
||||
if ( false !== strpos( $family, ' ' ) && false === strpos( $family, '"' ) ) {
|
||||
$this->value = '"' . $family . '"';
|
||||
}
|
||||
$this->value = html_entity_decode( $family, ENT_QUOTES );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles CSS properties.
|
||||
* Extend this class in order to handle exceptions.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Controls
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Output for CSS properties.
|
||||
*/
|
||||
class Kirki_Output_Property {
|
||||
|
||||
/**
|
||||
* The property we're modifying.
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $property;
|
||||
|
||||
/**
|
||||
* The value
|
||||
*
|
||||
* @access protected
|
||||
* @var string|array
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access public
|
||||
* @param string $property The CSS property we're modifying.
|
||||
* @param mixed $value The value.
|
||||
*/
|
||||
public function __construct( $property, $value ) {
|
||||
$this->property = $property;
|
||||
$this->value = $value;
|
||||
$this->process_value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the value.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function process_value() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
public function get_value() {
|
||||
return $this->value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
/**
|
||||
* Custom Sections.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @subpackage Custom Sections Module
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds styles to the customizer.
|
||||
*/
|
||||
class Kirki_Modules_Custom_Sections {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function __construct() {
|
||||
|
||||
// Register the new section types.
|
||||
add_filter( 'kirki_section_types', array( $this, 'set_section_types' ) );
|
||||
|
||||
// Register the new panel types.
|
||||
add_filter( 'kirki_panel_types', array( $this, 'set_panel_types' ) );
|
||||
|
||||
// Include the section-type files.
|
||||
add_action( 'customize_register', array( $this, 'include_sections_and_panels' ) );
|
||||
|
||||
// Enqueue styles & scripts.
|
||||
add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_scrips' ), 999 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the custom section types.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @param array $section_types The registered section-types.
|
||||
* @return array
|
||||
*/
|
||||
public function set_section_types( $section_types ) {
|
||||
$new_types = array(
|
||||
'kirki-default' => 'Kirki_Sections_Default_Section',
|
||||
'kirki-expanded' => 'Kirki_Sections_Expanded_Section',
|
||||
'kirki-nested' => 'Kirki_Sections_Nested_Section',
|
||||
);
|
||||
return array_merge( $section_types, $new_types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the custom panel types.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @param array $panel_types The registered section-types.
|
||||
* @return array
|
||||
*/
|
||||
public function set_panel_types( $panel_types ) {
|
||||
$new_types = array(
|
||||
'kirki-nested' => 'Kirki_Panels_Nested_Panel',
|
||||
);
|
||||
return array_merge( $panel_types, $new_types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the custom-section classes.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function include_sections_and_panels() {
|
||||
|
||||
// Sections.
|
||||
$folder_path = dirname( __FILE__ ) . '/sections/';
|
||||
$section_types = apply_filters( 'kirki_section_types', array() );
|
||||
|
||||
foreach ( $section_types as $id => $class ) {
|
||||
if ( ! class_exists( $class ) ) {
|
||||
$path = wp_normalize_path( $folder_path . 'class-kirki-sections-' . $id . '-section.php' );
|
||||
if ( file_exists( $path ) ) {
|
||||
include_once $path;
|
||||
continue;
|
||||
}
|
||||
$path = str_replace( 'class-kirki-sections-kirki-', 'class-kirki-sections-', $path );
|
||||
if ( file_exists( $path ) ) {
|
||||
include_once $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Panels.
|
||||
$folder_path = dirname( __FILE__ ) . '/panels/';
|
||||
$panel_types = apply_filters( 'kirki_panel_types', array() );
|
||||
|
||||
foreach ( $panel_types as $id => $class ) {
|
||||
if ( ! class_exists( $class ) ) {
|
||||
$path = wp_normalize_path( $folder_path . 'class-kirki-panels-' . $id . '-panel.php' );
|
||||
if ( file_exists( $path ) ) {
|
||||
include_once $path;
|
||||
continue;
|
||||
}
|
||||
$path = str_replace( 'class-kirki-panels-kirki-', 'class-kirki-panels-', $path );
|
||||
if ( file_exists( $path ) ) {
|
||||
include_once $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues any necessary scripts and styles.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function enqueue_scrips() {
|
||||
wp_enqueue_style( 'kirki-custom-sections', trailingslashit( Kirki::$url ) . 'modules/custom-sections/sections.css', array(), KIRKI_VERSION );
|
||||
wp_enqueue_script( 'kirki-custom-sections', trailingslashit( Kirki::$url ) . 'modules/custom-sections/sections.js', array( 'jquery', 'customize-base', 'customize-controls' ), KIRKI_VERSION, false );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
/**
|
||||
* Nested section.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Custom Sections Module
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Nested panel.
|
||||
*/
|
||||
class Kirki_Panels_Nested_Panel extends WP_Customize_Panel {
|
||||
|
||||
/**
|
||||
* The parent panel.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @var string
|
||||
*/
|
||||
public $panel;
|
||||
|
||||
/**
|
||||
* Type of this panel.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @var string
|
||||
*/
|
||||
public $type = 'kirki-nested';
|
||||
|
||||
/**
|
||||
* Gather the parameters passed to client JavaScript via JSON.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return array The array to be exported to the client as JSON.
|
||||
*/
|
||||
public function json() {
|
||||
$array = wp_array_slice_assoc(
|
||||
(array) $this, array(
|
||||
'id',
|
||||
'description',
|
||||
'priority',
|
||||
'type',
|
||||
'panel',
|
||||
)
|
||||
);
|
||||
|
||||
$array['title'] = html_entity_decode( $this->title, ENT_QUOTES, get_bloginfo( 'charset' ) );
|
||||
$array['content'] = $this->get_content();
|
||||
$array['active'] = $this->active();
|
||||
$array['instanceNumber'] = $this->instance_number;
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
26
functions/kirki/modules/custom-sections/sections.css
Normal file
26
functions/kirki/modules/custom-sections/sections.css
Normal file
|
@ -0,0 +1,26 @@
|
|||
#customize-theme-controls .control-section-kirki-expanded .accordion-section-title {
|
||||
display: none;
|
||||
}
|
||||
#customize-theme-controls .control-section-kirki-expanded .customize-section-back {
|
||||
display: none;
|
||||
}
|
||||
#customize-theme-controls .customize-pane-child.control-section-kirki-expanded {
|
||||
position: relative;
|
||||
visibility: visible;
|
||||
height: auto;
|
||||
margin-left: -100%;
|
||||
}
|
||||
#customize-theme-controls .customize-pane-child.control-section-kirki-expanded h3 .customize-action {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#customize-theme-controls .customize-pane-child.current-section-parent,
|
||||
.in-sub-panel #customize-theme-controls .customize-pane-child.current-panel-parent {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.control-section-kirki-nested {
|
||||
margin: 0 -12px;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=sections.css.map */
|
256
functions/kirki/modules/custom-sections/sections.js
Normal file
256
functions/kirki/modules/custom-sections/sections.js
Normal file
|
@ -0,0 +1,256 @@
|
|||
jQuery( document ).ready( function() {
|
||||
|
||||
wp.customize.section.each( function( section ) {
|
||||
|
||||
// Get the pane element.
|
||||
var pane = jQuery( '#sub-accordion-section-' + section.id ),
|
||||
sectionLi = jQuery( '#accordion-section-' + section.id );
|
||||
|
||||
// Check if the section is expanded.
|
||||
if ( sectionLi.hasClass( 'control-section-kirki-expanded' ) ) {
|
||||
|
||||
// Move element.
|
||||
pane.appendTo( sectionLi );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
/**
|
||||
* @see https://wordpress.stackexchange.com/a/256103/17078
|
||||
*/
|
||||
( function() {
|
||||
|
||||
var _panelEmbed,
|
||||
_panelIsContextuallyActive,
|
||||
_panelAttachEvents,
|
||||
_sectionEmbed,
|
||||
_sectionIsContextuallyActive,
|
||||
_sectionAttachEvents;
|
||||
|
||||
wp.customize.bind( 'pane-contents-reflowed', function() {
|
||||
|
||||
var panels = [],
|
||||
sections = [];
|
||||
|
||||
// Reflow Sections.
|
||||
wp.customize.section.each( function( section ) {
|
||||
|
||||
if ( 'kirki-nested' !== section.params.type || _.isUndefined( section.params.section ) ) {
|
||||
return;
|
||||
}
|
||||
sections.push( section );
|
||||
} );
|
||||
|
||||
sections.sort( wp.customize.utils.prioritySort ).reverse();
|
||||
|
||||
jQuery.each( sections, function( i, section ) {
|
||||
var parentContainer = jQuery( '#sub-accordion-section-' + section.params.section );
|
||||
|
||||
parentContainer.children( '.section-meta' ).after( section.headContainer );
|
||||
} );
|
||||
|
||||
// Reflow Panels.
|
||||
wp.customize.panel.each( function( panel ) {
|
||||
if ( 'kirki-nested' !== panel.params.type || _.isUndefined( panel.params.panel ) ) {
|
||||
return;
|
||||
}
|
||||
panels.push( panel );
|
||||
} );
|
||||
|
||||
panels.sort( wp.customize.utils.prioritySort ).reverse();
|
||||
|
||||
jQuery.each( panels, function( i, panel ) {
|
||||
var parentContainer = jQuery( '#sub-accordion-panel-' + panel.params.panel );
|
||||
|
||||
parentContainer.children( '.panel-meta' ).after( panel.headContainer );
|
||||
} );
|
||||
} );
|
||||
|
||||
// Extend Panel.
|
||||
_panelEmbed = wp.customize.Panel.prototype.embed;
|
||||
_panelIsContextuallyActive = wp.customize.Panel.prototype.isContextuallyActive;
|
||||
_panelAttachEvents = wp.customize.Panel.prototype.attachEvents;
|
||||
|
||||
wp.customize.Panel = wp.customize.Panel.extend( {
|
||||
attachEvents: function() {
|
||||
var panel;
|
||||
|
||||
if ( 'kirki-nested' !== this.params.type || _.isUndefined( this.params.panel ) ) {
|
||||
_panelAttachEvents.call( this );
|
||||
return;
|
||||
}
|
||||
|
||||
_panelAttachEvents.call( this );
|
||||
|
||||
panel = this;
|
||||
|
||||
panel.expanded.bind( function( expanded ) {
|
||||
var parent = wp.customize.panel( panel.params.panel );
|
||||
|
||||
if ( expanded ) {
|
||||
parent.contentContainer.addClass( 'current-panel-parent' );
|
||||
} else {
|
||||
parent.contentContainer.removeClass( 'current-panel-parent' );
|
||||
}
|
||||
} );
|
||||
|
||||
panel.container.find( '.customize-panel-back' ).off( 'click keydown' ).on( 'click keydown', function( event ) {
|
||||
if ( wp.customize.utils.isKeydownButNotEnterEvent( event ) ) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault(); // Keep this AFTER the key filter above
|
||||
|
||||
if ( panel.expanded() ) {
|
||||
wp.customize.panel( panel.params.panel ).expand();
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
embed: function() {
|
||||
|
||||
var panel = this,
|
||||
parentContainer;
|
||||
if ( 'kirki-nested' !== this.params.type || _.isUndefined( this.params.panel ) ) {
|
||||
_panelEmbed.call( this );
|
||||
return;
|
||||
}
|
||||
|
||||
_panelEmbed.call( this );
|
||||
|
||||
parentContainer = jQuery( '#sub-accordion-panel-' + this.params.panel );
|
||||
|
||||
parentContainer.append( panel.headContainer );
|
||||
},
|
||||
|
||||
isContextuallyActive: function() {
|
||||
|
||||
var panel = this,
|
||||
children,
|
||||
activeCount = 0;
|
||||
|
||||
if ( 'kirki-nested' !== this.params.type ) {
|
||||
return _panelIsContextuallyActive.call( this );
|
||||
}
|
||||
|
||||
children = this._children( 'panel', 'section' );
|
||||
|
||||
wp.customize.panel.each( function( child ) {
|
||||
if ( ! child.params.panel ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( child.params.panel !== panel.id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
children.push( child );
|
||||
} );
|
||||
|
||||
children.sort( wp.customize.utils.prioritySort );
|
||||
|
||||
_( children ).each( function( child ) {
|
||||
if ( child.active() && child.isContextuallyActive() ) {
|
||||
activeCount += 1;
|
||||
}
|
||||
} );
|
||||
return ( 0 !== activeCount );
|
||||
}
|
||||
} );
|
||||
|
||||
// Extend Section.
|
||||
_sectionEmbed = wp.customize.Section.prototype.embed;
|
||||
_sectionIsContextuallyActive = wp.customize.Section.prototype.isContextuallyActive;
|
||||
_sectionAttachEvents = wp.customize.Section.prototype.attachEvents;
|
||||
|
||||
wp.customize.Section = wp.customize.Section.extend( {
|
||||
attachEvents: function() {
|
||||
|
||||
var section = this;
|
||||
|
||||
if ( 'kirki-nested' !== this.params.type || _.isUndefined( this.params.section ) ) {
|
||||
_sectionAttachEvents.call( section );
|
||||
return;
|
||||
}
|
||||
|
||||
_sectionAttachEvents.call( section );
|
||||
|
||||
section.expanded.bind( function( expanded ) {
|
||||
var parent = wp.customize.section( section.params.section );
|
||||
|
||||
if ( expanded ) {
|
||||
parent.contentContainer.addClass( 'current-section-parent' );
|
||||
} else {
|
||||
parent.contentContainer.removeClass( 'current-section-parent' );
|
||||
}
|
||||
} );
|
||||
|
||||
section.container.find( '.customize-section-back' ).off( 'click keydown' ).on( 'click keydown', function( event ) {
|
||||
if ( wp.customize.utils.isKeydownButNotEnterEvent( event ) ) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault(); // Keep this AFTER the key filter above
|
||||
if ( section.expanded() ) {
|
||||
wp.customize.section( section.params.section ).expand();
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
embed: function() {
|
||||
|
||||
var section = this,
|
||||
parentContainer;
|
||||
|
||||
if ( 'kirki-nested' !== this.params.type || _.isUndefined( this.params.section ) ) {
|
||||
_sectionEmbed.call( section );
|
||||
return;
|
||||
}
|
||||
|
||||
_sectionEmbed.call( section );
|
||||
|
||||
parentContainer = jQuery( '#sub-accordion-section-' + this.params.section );
|
||||
|
||||
parentContainer.append( section.headContainer );
|
||||
},
|
||||
|
||||
isContextuallyActive: function() {
|
||||
var section = this,
|
||||
children,
|
||||
activeCount = 0;
|
||||
if ( 'kirki-nested' !== this.params.type ) {
|
||||
return _sectionIsContextuallyActive.call( this );
|
||||
}
|
||||
|
||||
children = this._children( 'section', 'control' );
|
||||
|
||||
wp.customize.section.each( function( child ) {
|
||||
if ( ! child.params.section ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( child.params.section !== section.id ) {
|
||||
return;
|
||||
}
|
||||
children.push( child );
|
||||
} );
|
||||
|
||||
children.sort( wp.customize.utils.prioritySort );
|
||||
|
||||
_( children ).each( function( child ) {
|
||||
if ( 'undefined' !== typeof child.isContextuallyActive ) {
|
||||
if ( child.active() && child.isContextuallyActive() ) {
|
||||
activeCount += 1;
|
||||
}
|
||||
} else {
|
||||
if ( child.active() ) {
|
||||
activeCount += 1;
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
return ( 0 !== activeCount );
|
||||
}
|
||||
} );
|
||||
}( jQuery ) );
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
/**
|
||||
* The default section.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Custom Sections Module
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default Section.
|
||||
*/
|
||||
class Kirki_Sections_Default_Section extends WP_Customize_Section {
|
||||
|
||||
/**
|
||||
* The section type.
|
||||
*
|
||||
* @access public
|
||||
* @var string
|
||||
*/
|
||||
public $type = 'kirki-default';
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/**
|
||||
* An expanded section.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Custom Sections Module
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Expanded Section.
|
||||
*/
|
||||
class Kirki_Sections_Expanded_Section extends WP_Customize_Section {
|
||||
|
||||
/**
|
||||
* The section type.
|
||||
*
|
||||
* @access public
|
||||
* @var string
|
||||
*/
|
||||
public $type = 'kirki-expanded';
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
/**
|
||||
* Nested section.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Custom Sections Module
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Nested section.
|
||||
*/
|
||||
class Kirki_Sections_Nested_Section extends WP_Customize_Section {
|
||||
|
||||
/**
|
||||
* The parent section.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @var string
|
||||
*/
|
||||
public $section;
|
||||
|
||||
/**
|
||||
* The section type.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @var string
|
||||
*/
|
||||
public $type = 'kirki-nested';
|
||||
|
||||
/**
|
||||
* Gather the parameters passed to client JavaScript via JSON.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return array The array to be exported to the client as JSON.
|
||||
*/
|
||||
public function json() {
|
||||
$array = wp_array_slice_assoc(
|
||||
(array) $this, array(
|
||||
'id',
|
||||
'description',
|
||||
'priority',
|
||||
'panel',
|
||||
'type',
|
||||
'description_hidden',
|
||||
'section',
|
||||
)
|
||||
);
|
||||
|
||||
$array['title'] = html_entity_decode( $this->title, ENT_QUOTES, get_bloginfo( 'charset' ) );
|
||||
$array['content'] = $this->get_content();
|
||||
$array['active'] = $this->active();
|
||||
$array['instanceNumber'] = $this->instance_number;
|
||||
|
||||
$array['customizeAction'] = esc_html__( 'Customizing', 'kirki' );
|
||||
if ( $this->panel ) {
|
||||
/* translators: The title. */
|
||||
$array['customizeAction'] = sprintf( esc_html__( 'Customizing ▸ %s', 'kirki' ), esc_html( $this->manager->get_panel( $this->panel )->title ) );
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
}
|
14
functions/kirki/modules/customizer-branding/branding.js
Normal file
14
functions/kirki/modules/customizer-branding/branding.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* global kirkiBranding */
|
||||
jQuery( document ).ready( function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
if ( '' !== kirkiBranding.logoImage ) {
|
||||
jQuery( 'div#customize-info .preview-notice' ).replaceWith( '<img src="' + kirkiBranding.logoImage + '">' );
|
||||
}
|
||||
|
||||
if ( '' !== kirkiBranding.description ) {
|
||||
jQuery( 'div#customize-info > .customize-panel-description' ).replaceWith( '<div class="customize-panel-description">' + kirkiBranding.description + '</div>' );
|
||||
}
|
||||
|
||||
} );
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
/**
|
||||
* Changes the customizer's branding.
|
||||
* For documentation please see
|
||||
* https://github.com/aristath/kirki/wiki/Styling-the-Customizer
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds styles to the customizer.
|
||||
*/
|
||||
class Kirki_Modules_Customizer_Branding {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'customize_controls_print_scripts', array( $this, 'customize_controls_print_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the script responsible for branding the customizer
|
||||
* and also adds variables to it using the wp_localize_script function.
|
||||
* The actual branding is handled via JS.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function customize_controls_print_scripts() {
|
||||
$config = apply_filters( 'kirki_config', array() );
|
||||
$vars = array(
|
||||
'logoImage' => '',
|
||||
'description' => '',
|
||||
);
|
||||
if ( isset( $config['logo_image'] ) && '' !== $config['logo_image'] ) {
|
||||
$vars['logoImage'] = esc_url_raw( $config['logo_image'] );
|
||||
}
|
||||
if ( isset( $config['description'] ) && '' !== $config['description'] ) {
|
||||
$vars['description'] = esc_textarea( $config['description'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $vars['logoImage'] ) || ! empty( $vars['description'] ) ) {
|
||||
wp_register_script( 'kirki-branding', Kirki::$url . '/modules/customizer-branding/branding.js', array(), KIRKI_VERSION, false );
|
||||
wp_localize_script( 'kirki-branding', 'kirkiBranding', $vars );
|
||||
wp_enqueue_script( 'kirki-branding' );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,422 @@
|
|||
<?php
|
||||
/**
|
||||
* Changes the styling of the customizer
|
||||
* based on the settings set using the kirki_config filter.
|
||||
* For documentation please see
|
||||
* https://github.com/aristath/kirki/wiki/Styling-the-Customizer
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds styles to the customizer.
|
||||
*/
|
||||
class Kirki_Modules_Customizer_Styling {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'customize_controls_print_styles', array( $this, 'custom_css' ), 99 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom CSS rules to the head, applying our custom styles.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function custom_css() {
|
||||
$config = apply_filters( 'kirki_config', array() );
|
||||
if ( ! isset( $config['color_accent'] ) && ! isset( $config['color_back'] ) ) {
|
||||
return;
|
||||
}
|
||||
$back = isset( $config['color_back'] ) ? $config['color_back'] : false;
|
||||
|
||||
$text_on_back = '';
|
||||
$border_on_back = '';
|
||||
$back_on_back = '';
|
||||
$hover_on_back = '';
|
||||
$arrows_on_back = '';
|
||||
$text_on_accent = '';
|
||||
$border_on_accent = '';
|
||||
$accent_disabled_obj = '';
|
||||
$accent_disabled = '';
|
||||
$text_on_accent_disabled = '';
|
||||
$border_on_accent_disabled = '';
|
||||
|
||||
if ( $back ) {
|
||||
$back_obj = ariColor::newColor( $back );
|
||||
$text_on_back = ( 60 > $back_obj->lightness ) ? $back_obj->getNew( 'lightness', $back_obj->lightness + 60 )->toCSS( $back_obj->mode ) : $back_obj->getNew( 'lightness', $back_obj->lightness - 60 )->toCSS( $back_obj->mode );
|
||||
$border_on_back = ( 80 < $back_obj->lightness ) ? $back_obj->getNew( 'lightness', $back_obj->lightness - 13 )->toCSS( $back_obj->mode ) : $back_obj->getNew( 'lightness', $back_obj->lightness + 13 )->toCSS( $back_obj->mode );
|
||||
$back_on_back = ( 90 < $back_obj->lightness ) ? $back_obj->getNew( 'lightness', $back_obj->lightness - 6 )->toCSS( $back_obj->mode ) : $back_obj->getNew( 'lightness', $back_obj->lightness + 11 )->toCSS( $back_obj->mode );
|
||||
$hover_on_back = ( 90 < $back_obj->lightness ) ? $back_obj->getNew( 'lightness', $back_obj->lightness - 3 )->toCSS( $back_obj->mode ) : $back_obj->getNew( 'lightness', $back_obj->lightness + 3 )->toCSS( $back_obj->mode );
|
||||
$arrows_on_back = ( 50 > $back_obj->lightness ) ? $back_obj->getNew( 'lightness', $back_obj->lightness + 30 )->toCSS( $back_obj->mode ) : $back_obj->getNew( 'lightness', $back_obj->lightness - 30 )->toCSS( $back_obj->mode );
|
||||
}
|
||||
$accent = ( isset( $config['color_accent'] ) ) ? $config['color_accent'] : false;
|
||||
if ( $accent ) {
|
||||
$accent_obj = ariColor::newColor( $accent );
|
||||
$text_on_accent = ( 60 > $accent_obj->lightness ) ? $accent_obj->getNew( 'lightness', $accent_obj->lightness + 60 )->toCSS( $accent_obj->mode ) : $accent_obj->getNew( 'lightness', $accent_obj->lightness - 60 )->toCSS( $accent_obj->mode );
|
||||
$border_on_accent = ( 50 < $accent_obj->lightness ) ? $accent_obj->getNew( 'lightness', $accent_obj->lightness - 4 )->toCSS( $accent_obj->mode ) : $accent_obj->getNew( 'lightness', $accent_obj->lightness + 4 )->toCSS( $accent_obj->mode );
|
||||
$accent_disabled_obj = ( 35 < $accent_obj->lightness ) ? $accent_obj->getNew( 'lightness', $accent_obj->lightness - 30 ) : $accent_obj->getNew( 'lightness', $accent_obj->lightness + 30 );
|
||||
$accent_disabled = $accent_disabled_obj->toCSS( $accent_disabled_obj->mode );
|
||||
$text_on_accent_disabled = ( 60 > $accent_disabled_obj->lightness ) ? $accent_disabled_obj->getNew( 'lightness', $accent_disabled_obj->lightness + 60 )->toCSS( $accent_disabled_obj->mode ) : $accent_disabled_obj->getNew( 'lightness', $accent_disabled_obj->lightness - 60 )->toCSS( $accent_disabled_obj->mode );
|
||||
$border_on_accent_disabled = ( 50 < $accent_disabled_obj->lightness ) ? $accent_disabled_obj->getNew( 'lightness', $accent_disabled_obj->lightness - 4 )->toCSS( $accent_disabled_obj->mode ) : $accent_disabled_obj->getNew( 'lightness', $accent_disabled_obj->lightness + 4 )->toCSS( $accent_disabled_obj->mode );
|
||||
}
|
||||
?>
|
||||
<style>
|
||||
.wp-full-overlay-sidebar,
|
||||
#customize-controls .customize-info .accordion-section-title,
|
||||
#customize-controls .panel-meta.customize-info .accordion-section-title:hover,
|
||||
#customize-theme-controls .accordion-section-title,
|
||||
.customize-section-title,
|
||||
#customize-theme-controls .control-section-themes .accordion-section-title,
|
||||
#customize-theme-controls .control-section-themes .accordion-section-title,
|
||||
#customize-theme-controls .control-section-themes .accordion-section-title:hover,
|
||||
.outer-section-open #customize-controls .wp-full-overlay-sidebar-content,
|
||||
#customize-sidebar-outer-content,
|
||||
#customize-control-changeset_status .customize-inside-control-row,
|
||||
#customize-control-changeset_preview_link input,
|
||||
#customize-control-changeset_scheduled_date,
|
||||
.wp-core-ui .wp-full-overlay .collapse-sidebar {
|
||||
background: <?php echo esc_html( $back ); ?>;
|
||||
background-color: <?php echo esc_html( $back ); ?>;
|
||||
color: <?php echo esc_html( $text_on_back ); ?>;
|
||||
}
|
||||
|
||||
<?php if ( $back ) : ?>
|
||||
.media-widget-preview.media_image, .media-widget-preview.media_audio, .attachment-media-view {
|
||||
background: none;
|
||||
}
|
||||
.wp-core-ui .button-link-delete {
|
||||
color: <?php echo ( 90 > $back_obj->lightness ) ? '#FF8A80' : '#a00'; ?>;
|
||||
}
|
||||
.button.wp-color-result {
|
||||
text-shadow: none !important;
|
||||
}
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
#customize-sidebar-outer-content {
|
||||
border-left-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
border-right-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
}
|
||||
|
||||
#customize-controls .customize-info .panel-title,
|
||||
#customize-controls .customize-pane-child .customize-section-title h3,
|
||||
#customize-controls .customize-pane-child h3.customize-section-title,
|
||||
.customize-control,
|
||||
#customize-controls .description {
|
||||
color: <?php echo esc_html( $text_on_back ); ?>;
|
||||
}
|
||||
|
||||
#customize-controls .customize-info,
|
||||
#customize-header-actions,
|
||||
.customize-section-title {
|
||||
border-bottom-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
}
|
||||
|
||||
.wp-full-overlay-sidebar .wp-full-overlay-header,
|
||||
.customize-controls-close,
|
||||
.expanded .wp-full-overlay-footer {
|
||||
color: <?php echo esc_html( $text_on_back ); ?>;
|
||||
background-color: <?php echo esc_html( $back_on_back ); ?>;
|
||||
border-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
}
|
||||
|
||||
.accordion-section,
|
||||
#customize-theme-controls .customize-pane-child.accordion-section-content {
|
||||
background: <?php echo esc_html( $back_on_back ); ?>;
|
||||
}
|
||||
|
||||
#accordion-section-themes+.control-section,
|
||||
#customize-theme-controls .control-section:last-of-type.open,
|
||||
#customize-theme-controls .control-section:last-of-type > .accordion-section-title,
|
||||
#customize-theme-controls .control-section.open {
|
||||
border-bottom-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
border-top-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
}
|
||||
|
||||
#customize-theme-controls .accordion-section-title {
|
||||
border-bottom-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
border-left-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
}
|
||||
|
||||
#customize-theme-controls .control-section-themes .accordion-section-title,
|
||||
#customize-theme-controls .control-section-themes .accordion-section-title:hover {
|
||||
border-bottom-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
border-top-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
border-bottom-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
}
|
||||
|
||||
#customize-theme-controls .accordion-section-title:after {
|
||||
color: <?php echo esc_html( $arrows_on_back ); ?>;
|
||||
}
|
||||
|
||||
.wp-core-ui .button,
|
||||
.wp-core-ui .button-secondary {
|
||||
background-color: <?php echo esc_html( $back ); ?>;
|
||||
border-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
box-shadow: 0 1px 0 <?php echo esc_html( $border_on_back ); ?>;
|
||||
-webkit-box-shadow: 0 1px 0 <?php echo esc_html( $border_on_back ); ?>;
|
||||
text-shadow: 0 -1px 1px <?php echo esc_html( $border_on_back ); ?>, 1px 0 1px <?php echo esc_html( $border_on_back ); ?>, 0 1px 1px <?php echo esc_html( $border_on_back ); ?>, -1px 0 1px <?php echo esc_html( $border_on_back ); ?>;
|
||||
color: <?php echo esc_html( $text_on_back ); ?>;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.customize-controls-preview-toggle{
|
||||
background-color: <?php echo esc_html( $back ); ?>;
|
||||
border-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
box-shadow:0 1px 0 <?php echo esc_html( $border_on_back ); ?>;
|
||||
-webkit-box-shadow:0 1px 0 <?php echo esc_html( $border_on_back ); ?>;
|
||||
text-shadow:0 -1px 1px <?php echo esc_html( $border_on_back ); ?>, 1px 0 1px <?php echo esc_html( $border_on_back ); ?>, 0 1px 1px <?php echo esc_html( $border_on_back ); ?>, -1px 0 1px <?php echo esc_html( $border_on_back ); ?>;
|
||||
color: <?php echo esc_html( $text_on_back ); ?>;
|
||||
}
|
||||
}
|
||||
|
||||
.wp-core-ui .button.focus,
|
||||
.wp-core-ui .button.hover,
|
||||
.wp-core-ui .button:focus,
|
||||
.wp-core-ui .button:hover,
|
||||
.wp-core-ui .button-secondary.focus,
|
||||
.wp-core-ui .button-secondary.hover,
|
||||
.wp-core-ui .button-secondary:focus,
|
||||
.wp-core-ui .button-secondary:hover,
|
||||
.customize-panel-back,
|
||||
.customize-section-back {
|
||||
background-color: <?php echo esc_html( $back_on_back ); ?>;
|
||||
border-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
box-shadow: 0 1px 0 <?php echo esc_html( $border_on_back ); ?>;
|
||||
-webkit-box-shadow: 0 1px 0 <?php echo esc_html( $border_on_back ); ?>;
|
||||
text-shadow: 0 -1px 1px <?php echo esc_html( $border_on_back ); ?>, 1px 0 1px <?php echo esc_html( $border_on_back ); ?>, 0 1px 1px <?php echo esc_html( $border_on_back ); ?>, -1px 0 1px <?php echo esc_html( $border_on_back ); ?>;
|
||||
color: <?php echo esc_html( $text_on_back ); ?>;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.customize-controls-preview-toggle.focus,
|
||||
.customize-controls-preview-toggle.hover,
|
||||
.customize-controls-preview-toggle:focus,
|
||||
.customize-controls-preview-toggle:hover {
|
||||
background-color: <?php echo esc_html( $back_on_back ); ?>;
|
||||
border-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
box-shadow: 0 1px 0 <?php echo esc_html( $border_on_back ); ?>;
|
||||
-webkit-box-shadow: 0 1px 0 <?php echo esc_html( $border_on_back ); ?>;
|
||||
text-shadow: 0 -1px 1px <?php echo esc_html( $border_on_back ); ?>, 1px 0 1px <?php echo esc_html( $border_on_back ); ?>, 0 1px 1px <?php echo esc_html( $border_on_back ); ?>, -1px 0 1px <?php echo esc_html( $border_on_back ); ?>;
|
||||
color:<?php echo esc_html( $text_on_back ); ?>;
|
||||
}
|
||||
}
|
||||
|
||||
.customize-control-kirki-background .background-attachment .buttonset .switch-label,
|
||||
.customize-control-kirki-background .background-size .buttonset .switch-label,
|
||||
.customize-control-kirki-radio-buttonset .buttonset .switch-label {
|
||||
color: <?php echo esc_html( $text_on_back ); ?>;
|
||||
}
|
||||
|
||||
.wp-color-result {
|
||||
border-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
-webkit-box-shadow: 0 1px 0 <?php echo esc_html( $border_on_back ); ?>;
|
||||
box-shadow: 0 1px 0 <?php echo esc_html( $border_on_back ); ?>;
|
||||
}
|
||||
|
||||
.wp-color-result:focus,
|
||||
.wp-color-result:hover {
|
||||
border-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
background: <?php echo esc_html( $back_on_back ); ?>;
|
||||
}
|
||||
|
||||
.wp-color-result:after {
|
||||
border-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
background: <?php echo esc_html( $back ); ?>;
|
||||
color: <?php echo esc_html( $text_on_back ); ?>;
|
||||
}
|
||||
|
||||
.wp-color-result:focus:after,
|
||||
.wp-color-result:hover:after {
|
||||
color: <?php echo esc_html( $text_on_back ); ?>;
|
||||
}
|
||||
|
||||
.customize-control input[type=tel],
|
||||
.customize-control input[type=url],
|
||||
.customize-control input[type=text],
|
||||
.customize-control input[type=password],
|
||||
.customize-control input[type=email],
|
||||
.customize-control input[type=number],
|
||||
.customize-control input[type=search],
|
||||
.customize-control input[type=radio],
|
||||
.customize-control input[type=checkbox],
|
||||
.customize-control select,
|
||||
.select2-container--default .select2-selection--single,
|
||||
.select2-container--default .select2-selection--multiple {
|
||||
background: <?php echo esc_html( $back ); ?>;
|
||||
border-color: <?php echo esc_html( $border_on_back ); ?>;
|
||||
color: <?php echo esc_html( $text_on_back ); ?>;
|
||||
}
|
||||
|
||||
.customize-control-kirki-slider input[type=range]::-webkit-slider-thumb {
|
||||
background-color:<?php echo esc_html( $accent ); ?>;
|
||||
}
|
||||
|
||||
.customize-control-kirki-slider input[type=range]::-moz-range-thumb {
|
||||
background-color: <?php echo esc_html( $accent ); ?>;
|
||||
}
|
||||
|
||||
.customize-control-kirki-slider input[type=range]::-ms-thumb {
|
||||
background-color: <?php echo esc_html( $accent ); ?>;
|
||||
}
|
||||
|
||||
.customize-control-kirki-slider input[type=range] {
|
||||
background: <?php echo esc_html( $border_on_back ); ?>;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||
color:<?php echo esc_html( $text_on_back ); ?>;
|
||||
}
|
||||
|
||||
.wp-full-overlay-footer .devices {
|
||||
background: none;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
}
|
||||
|
||||
.kirki-reset-section .dashicons {
|
||||
color: <?php echo esc_html( $back_on_back ); ?>;
|
||||
}
|
||||
|
||||
#customize-controls .control-section .accordion-section-title:focus,
|
||||
#customize-controls .control-section .accordion-section-title:hover,
|
||||
#customize-controls .control-section.open .accordion-section-title,
|
||||
#customize-controls .control-section:hover > .accordion-section-title,
|
||||
.customize-panel-back:focus,
|
||||
.customize-panel-back:hover,
|
||||
.customize-section-back:focus,
|
||||
.customize-section-back:hover {
|
||||
background: <?php echo esc_html( $hover_on_back ); ?>;
|
||||
color: <?php echo esc_html( $accent ); ?>;
|
||||
border-left-color: <?php echo esc_html( $accent ); ?>;
|
||||
}
|
||||
|
||||
.customize-controls-close:hover {
|
||||
background-color: <?php echo esc_html( $back ); ?>;
|
||||
color: <?php echo esc_html( $accent ); ?>;
|
||||
border-color: <?php echo esc_html( $accent ); ?>;
|
||||
}
|
||||
|
||||
#customize-theme-controls .control-section .accordion-section-title:focus:after,
|
||||
#customize-theme-controls .control-section .accordion-section-title:hover:after,
|
||||
#customize-theme-controls .control-section.open .accordion-section-title:after,
|
||||
#customize-theme-controls .control-section:hover>.accordion-section-title:after {
|
||||
color: <?php echo esc_html( $accent ); ?>;
|
||||
}
|
||||
|
||||
.wp-core-ui .button.button-primary {
|
||||
background-color: <?php echo esc_html( $accent ); ?>;
|
||||
border-color: <?php echo esc_html( $border_on_accent ); ?>;
|
||||
box-shadow: 0 1px 0 <?php echo esc_html( $border_on_accent ); ?>;
|
||||
-webkit-box-shadow: 0 1px 0 <?php echo esc_html( $border_on_accent ); ?>;
|
||||
text-shadow: 0 -1px 1px <?php echo esc_html( $border_on_accent ); ?>, 1px 0 1px <?php echo esc_html( $border_on_accent ); ?>, 0 1px 1px <?php echo esc_html( $border_on_accent ); ?>, -1px 0 1px <?php echo esc_html( $border_on_accent ); ?>;
|
||||
color: <?php echo esc_html( $text_on_accent ); ?>;
|
||||
}
|
||||
|
||||
.wp-core-ui .button.button-primary.focus,
|
||||
.wp-core-ui .button.button-primary.hover,
|
||||
.wp-core-ui .button.button-primary:focus,
|
||||
.wp-core-ui .button.button-primary:hover {
|
||||
background-color: <?php echo esc_html( $accent ); ?>;
|
||||
border-color: <?php echo esc_html( $border_on_accent ); ?>;
|
||||
box-shadow: 0 1px 0 <?php echo esc_html( $border_on_accent ); ?>;
|
||||
-webkit-box-shadow: 0 1px 0 <?php echo esc_html( $border_on_accent ); ?>;
|
||||
text-shadow: 0 -1px 1px <?php echo esc_html( $border_on_accent ); ?>, 1px 0 1px <?php echo esc_html( $border_on_accent ); ?>, 0 1px 1px <?php echo esc_html( $border_on_accent ); ?>, -1px 0 1px <?php echo esc_html( $border_on_accent ); ?>;
|
||||
color: <?php echo esc_html( $text_on_accent ); ?>;
|
||||
}
|
||||
|
||||
.wp-core-ui .button.button-primary-disabled,
|
||||
.wp-core-ui .button.button-primary.disabled,
|
||||
.wp-core-ui .button.button-primary:disabled,
|
||||
.wp-core-ui .button.button-primary[disabled] {
|
||||
background-color: <?php echo esc_html( $accent_disabled ); ?> !important;
|
||||
border-color: <?php echo esc_html( $border_on_accent_disabled ); ?> !important;
|
||||
box-shadow: 0 1px 0 <?php echo esc_html( $border_on_accent_disabled ); ?> !important;
|
||||
-webkit-box-shadow: 0 1px 0 <?php echo esc_html( $border_on_accent_disabled ); ?> !important;
|
||||
text-shadow: 0 -1px 1px <?php echo esc_html( $border_on_accent_disabled ); ?>, 1px 0 1px <?php echo esc_html( $border_on_accent_disabled ); ?>, 0 1px 1px <?php echo esc_html( $border_on_accent_disabled ); ?>, -1px 0 1px <?php echo esc_html( $border_on_accent_disabled ); ?> !important;
|
||||
color: <?php echo esc_html( $text_on_accent_disabled ); ?> !important;
|
||||
}
|
||||
|
||||
input[type=checkbox]:checked:before {
|
||||
color: <?php echo esc_html( $accent ); ?>;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
||||
background-color: <?php echo esc_html( $accent ); ?>;
|
||||
color: <?php echo esc_html( $text_on_accent ); ?>;
|
||||
}
|
||||
|
||||
.customize-control-kirki-radio-buttonset .buttonset .switch-input:checked + .switch-label,
|
||||
.customize-control-kirki-background .background-attachment .buttonset .switch-input:checked + .switch-label,
|
||||
.customize-control-kirki-background .background-size .buttonset .switch-input:checked + .switch-label {
|
||||
background-color: <?php echo esc_html( $accent ); ?>;
|
||||
border-color: <?php echo esc_html( $border_on_accent ); ?>;
|
||||
color: <?php echo esc_html( $text_on_accent ); ?>;
|
||||
}
|
||||
|
||||
.notice,
|
||||
div.updated,
|
||||
div.error {
|
||||
color: #444 !important;
|
||||
}
|
||||
|
||||
<?php if ( isset( $config['width'] ) ) : ?>
|
||||
.wp-full-overlay-sidebar {
|
||||
width: <?php echo esc_html( $config['width'] ); ?>;
|
||||
}
|
||||
.expanded .wp-full-overlay-footer {
|
||||
width: <?php echo esc_html( $config['width'] ); ?>;
|
||||
}
|
||||
.wp-full-overlay.expanded {
|
||||
margin-left: <?php echo esc_html( $config['width'] ); ?>;
|
||||
}
|
||||
.wp-full-overlay.collapsed .wp-full-overlay-sidebar {
|
||||
margin-left: -<?php echo esc_html( $config['width'] ); ?>;
|
||||
}
|
||||
<?php endif; ?>
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
/**
|
||||
* Automatic field-dependencies scripts calculation for Kirki controls.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds styles to the customizer.
|
||||
*/
|
||||
class Kirki_Modules_Field_Dependencies {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'customize_controls_enqueue_scripts', array( $this, 'field_dependencies' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the field-dependencies script
|
||||
* and adds variables to it using the wp_localize_script function.
|
||||
* The rest is handled via JS.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function field_dependencies() {
|
||||
wp_enqueue_script( 'kirki_field_dependencies', trailingslashit( Kirki::$url ) . 'modules/field-dependencies/field-dependencies.js', array( 'jquery', 'customize-base', 'customize-controls' ), KIRKI_VERSION, true );
|
||||
}
|
||||
}
|
210
functions/kirki/modules/field-dependencies/field-dependencies.js
Normal file
210
functions/kirki/modules/field-dependencies/field-dependencies.js
Normal file
|
@ -0,0 +1,210 @@
|
|||
var kirkiDependencies = {
|
||||
|
||||
listenTo: {},
|
||||
|
||||
init: function() {
|
||||
var self = this;
|
||||
|
||||
wp.customize.control.each( function( control ) {
|
||||
self.showKirkiControl( control );
|
||||
} );
|
||||
|
||||
_.each( self.listenTo, function( slaves, master ) {
|
||||
_.each( slaves, function( slave ) {
|
||||
wp.customize( master, function( setting ) {
|
||||
var setupControl = function( control ) {
|
||||
var setActiveState,
|
||||
isDisplayed;
|
||||
|
||||
isDisplayed = function() {
|
||||
return self.showKirkiControl( wp.customize.control( slave ) );
|
||||
};
|
||||
setActiveState = function() {
|
||||
control.active.set( isDisplayed() );
|
||||
};
|
||||
|
||||
setActiveState();
|
||||
setting.bind( setActiveState );
|
||||
control.active.validate = isDisplayed;
|
||||
};
|
||||
wp.customize.control( slave, setupControl );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Should we show the control?
|
||||
*
|
||||
* @since 3.0.17
|
||||
* @param {string|object} control - The control-id or the control object.
|
||||
* @returns {bool}
|
||||
*/
|
||||
showKirkiControl: function( control ) {
|
||||
var self = this,
|
||||
show = true,
|
||||
isOption = (
|
||||
control.params && // Check if control.params exists.
|
||||
control.params.kirkiOptionType && // Check if option_type exists.
|
||||
'option' === control.params.kirkiOptionType && // We're using options.
|
||||
control.params.kirkiOptionName && // Check if option_name exists.
|
||||
! _.isEmpty( control.params.kirkiOptionName ) // Check if option_name is not empty.
|
||||
),
|
||||
i;
|
||||
|
||||
|
||||
if ( _.isString( control ) ) {
|
||||
control = wp.customize.control( control );
|
||||
}
|
||||
|
||||
// Exit early if control not found or if "required" argument is not defined.
|
||||
if ( 'undefined' === typeof control || ( control.params && _.isEmpty( control.params.required ) ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Loop control requirements.
|
||||
for ( i = 0; i < control.params.required.length; i++ ) {
|
||||
if ( ! self.checkCondition( control.params.required[ i ], control, isOption, 'AND' ) ) {
|
||||
show = false;
|
||||
}
|
||||
}
|
||||
return show;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check a condition.
|
||||
*
|
||||
* @param {Object} requirement - The requirement, inherited from showKirkiControl.
|
||||
* @param {Object} control - The control object.
|
||||
* @param {bool} isOption - Whether it's an option or not.
|
||||
* @param {string} relation - Can be one of 'AND' or 'OR'.
|
||||
*/
|
||||
checkCondition: function( requirement, control, isOption, relation ) {
|
||||
var self = this,
|
||||
childRelation = ( 'AND' === relation ) ? 'OR' : 'AND',
|
||||
nestedItems,
|
||||
i;
|
||||
|
||||
// Tweak for using active callbacks with serialized options instead of theme_mods.
|
||||
if ( isOption && requirement.setting ) {
|
||||
|
||||
// Make sure we don't already have the option_name in there.
|
||||
if ( -1 === requirement.setting.indexOf( control.params.kirkiOptionName + '[' ) ) {
|
||||
requirement.setting = control.params.kirkiOptionName + '[' + requirement.setting + ']';
|
||||
}
|
||||
}
|
||||
|
||||
// If an array of other requirements nested, we need to process them separately.
|
||||
if ( 'undefined' !== typeof requirement[0] && 'undefined' === typeof requirement.setting ) {
|
||||
nestedItems = [];
|
||||
|
||||
// Loop sub-requirements.
|
||||
for ( i = 0; i < requirement.length; i++ ) {
|
||||
nestedItems.push( self.checkCondition( requirement[ i ], control, isOption, childRelation ) );
|
||||
}
|
||||
|
||||
// OR relation. Check that true is part of the array.
|
||||
if ( 'OR' === childRelation ) {
|
||||
return ( -1 !== nestedItems.indexOf( true ) );
|
||||
}
|
||||
|
||||
// AND relation. Check that false is not part of the array.
|
||||
return ( -1 === nestedItems.indexOf( false ) );
|
||||
}
|
||||
|
||||
// Early exit if setting is not defined.
|
||||
if ( 'undefined' === typeof wp.customize.control( requirement.setting ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.listenTo[ requirement.setting ] = self.listenTo[ requirement.setting ] || [];
|
||||
if ( -1 === self.listenTo[ requirement.setting ].indexOf( control.id ) ) {
|
||||
self.listenTo[ requirement.setting ].push( control.id );
|
||||
}
|
||||
|
||||
return self.evaluate(
|
||||
requirement.value,
|
||||
wp.customize.control( requirement.setting ).setting._value,
|
||||
requirement.operator
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Figure out if the 2 values have the relation we want.
|
||||
*
|
||||
* @since 3.0.17
|
||||
* @param {mixed} value1 - The 1st value.
|
||||
* @param {mixed} value2 - The 2nd value.
|
||||
* @param {string} operator - The comparison to use.
|
||||
* @returns {bool}
|
||||
*/
|
||||
evaluate: function( value1, value2, operator ) {
|
||||
var found = false;
|
||||
|
||||
if ( '===' === operator ) {
|
||||
return value1 === value2;
|
||||
}
|
||||
if ( '==' === operator || '=' === operator || 'equals' === operator || 'equal' === operator ) {
|
||||
return value1 == value2;
|
||||
}
|
||||
if ( '!==' === operator ) {
|
||||
return value1 !== value2;
|
||||
}
|
||||
if ( '!=' === operator || 'not equal' === operator ) {
|
||||
return value1 != value2;
|
||||
}
|
||||
if ( '>=' === operator || 'greater or equal' === operator || 'equal or greater' === operator ) {
|
||||
return value2 >= value1;
|
||||
}
|
||||
if ( '<=' === operator || 'smaller or equal' === operator || 'equal or smaller' === operator ) {
|
||||
return value2 <= value1;
|
||||
}
|
||||
if ( '>' === operator || 'greater' === operator ) {
|
||||
return value2 > value1;
|
||||
}
|
||||
if ( '<' === operator || 'smaller' === operator ) {
|
||||
return value2 < value1;
|
||||
}
|
||||
if ( 'contains' === operator || 'in' === operator ) {
|
||||
if ( _.isArray( value1 ) && _.isArray( value2 ) ) {
|
||||
_.each( value2, function( value ) {
|
||||
if ( value1.includes( value ) ) {
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
} );
|
||||
return found;
|
||||
}
|
||||
if ( _.isArray( value2 ) ) {
|
||||
_.each( value2, function( value ) {
|
||||
if ( value == value1 ) { // jshint ignore:line
|
||||
found = true;
|
||||
}
|
||||
} );
|
||||
return found;
|
||||
}
|
||||
if ( _.isObject( value2 ) ) {
|
||||
if ( ! _.isUndefined( value2[ value1 ] ) ) {
|
||||
found = true;
|
||||
}
|
||||
_.each( value2, function( subValue ) {
|
||||
if ( value1 === subValue ) {
|
||||
found = true;
|
||||
}
|
||||
} );
|
||||
return found;
|
||||
}
|
||||
if ( _.isString( value2 ) ) {
|
||||
if ( _.isString( value1 ) ) {
|
||||
return ( -1 < value1.indexOf( value2 ) && -1 < value2.indexOf( value1 ) );
|
||||
}
|
||||
return -1 < value1.indexOf( value2 );
|
||||
}
|
||||
}
|
||||
return value1 == value2;
|
||||
}
|
||||
};
|
||||
|
||||
jQuery( document ).ready( function() {
|
||||
kirkiDependencies.init();
|
||||
} );
|
|
@ -0,0 +1,355 @@
|
|||
<?php
|
||||
/**
|
||||
* Gutenberg integration for Kirki.
|
||||
*
|
||||
* This class contains methods for integrating Kirki with
|
||||
* the new WordPress core editor, Gutenberg. It provides
|
||||
* fonts and styles to be output by the theme.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Core
|
||||
* @author Tim Elsass
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.35
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wrapper class for static methods.
|
||||
*
|
||||
* @since 3.0.35
|
||||
*/
|
||||
class Kirki_Modules_Gutenberg {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.35
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Configuration reference.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
* @var object $configs
|
||||
*/
|
||||
private $configs;
|
||||
|
||||
/**
|
||||
* Whether feature is enabled.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
* @var bool $enabled
|
||||
*/
|
||||
public $enabled;
|
||||
|
||||
/**
|
||||
* CSS Module reference.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
* @var object $modules_css
|
||||
*/
|
||||
private $modules_css;
|
||||
|
||||
/**
|
||||
* Webfonts Module reference.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
* @var object $modules_webfonts
|
||||
*/
|
||||
private $modules_webfonts;
|
||||
|
||||
/**
|
||||
* Google Fonts reference.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
* @var object $google_fonts
|
||||
*/
|
||||
private $google_fonts;
|
||||
|
||||
/**
|
||||
* Webfonts Loader Module reference.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
* @var object $modules_webfonts
|
||||
*/
|
||||
private $modules_webfont_loader;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'admin_init', array( $this, 'init' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Module.
|
||||
*
|
||||
* Sets class properties and add necessary hooks.
|
||||
*
|
||||
* @since 3.0.35
|
||||
*/
|
||||
public function init() {
|
||||
$this->set_configs();
|
||||
$this->set_enabled();
|
||||
$this->set_modules_css();
|
||||
$this->set_google_fonts();
|
||||
$this->set_modules_webfonts();
|
||||
$this->set_modules_webfont_loader();
|
||||
$this->add_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add hooks for Gutenberg editor integration.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.35
|
||||
*/
|
||||
protected function add_hooks() {
|
||||
if ( ! $this->is_disabled() ) {
|
||||
add_action( 'after_setup_theme', array( $this, 'add_theme_support' ), 999 );
|
||||
add_action( 'enqueue_block_editor_assets', array( $this, 'load_fonts' ) );
|
||||
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_fontawesome' ) );
|
||||
add_filter( 'block_editor_settings', array( $this, 'enqueue' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add theme support for editor styles.
|
||||
*
|
||||
* This checks if theme has declared editor-styles support
|
||||
* already, and if not present, declares it. Hooked late.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
*/
|
||||
public function add_theme_support() {
|
||||
if ( true !== get_theme_support( 'editor-styles' ) ) {
|
||||
add_theme_support( 'editor-styles' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue styles to Gutenberg Editor.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
*/
|
||||
public function enqueue( $settings ) {
|
||||
$styles = $this->get_styles();
|
||||
|
||||
if ( ! empty( $styles ) ) {
|
||||
$settings['styles'][] = array( 'css' => $styles );
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the styles to add to Gutenberg Editor.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
*
|
||||
* @return string $styles String containing inline styles to add to Gutenberg.
|
||||
*/
|
||||
public function get_styles() {
|
||||
|
||||
$styles = null;
|
||||
|
||||
foreach ( $this->configs as $config_id => $args ) {
|
||||
|
||||
if ( true === $this->is_disabled( $args ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$modules_css = $this->modules_css;
|
||||
$styles = $modules_css::loop_controls( $config_id );
|
||||
$styles = apply_filters( "kirki_gutenberg_{$config_id}_dynamic_css", $styles );
|
||||
|
||||
if ( empty( $styles ) ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to check if feature is disabled.
|
||||
*
|
||||
* Feature can be disabled by KIRKI_NO_OUTPUT constant,
|
||||
* gutenbeg_support argument, and disabled output argument.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
*
|
||||
* @return bool $disabled Is gutenberg integration feature disabled?
|
||||
*/
|
||||
private function is_disabled( $args = array() ) {
|
||||
if ( defined( 'KIRKI_NO_OUTPUT' ) && true === KIRKI_NO_OUTPUT ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! empty( $args ) ) {
|
||||
if ( isset( $args['disable_output'] ) && true === $args['disable_output'] ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! isset( $args['gutenberg_support'] ) || true !== $args['gutenberg_support'] ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Fonts in Gutenberg Editor.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
*/
|
||||
public function load_fonts() {
|
||||
|
||||
$modules_webfonts = $this->modules_webfonts;
|
||||
$google_fonts = $this->google_fonts;
|
||||
foreach ( $this->configs as $config_id => $args ) {
|
||||
|
||||
if ( $this->is_disabled( $args ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->modules_webfont_loader->set_load( true );
|
||||
$this->modules_webfont_loader->enqueue_scripts();
|
||||
|
||||
$async = new Kirki_Modules_Webfonts_Async(
|
||||
$config_id,
|
||||
$modules_webfonts::get_instance(),
|
||||
$google_fonts::get_instance()
|
||||
);
|
||||
|
||||
$async->webfont_loader();
|
||||
$async->webfont_loader_script();
|
||||
|
||||
$local_fonts = new Kirki_Modules_Webfonts_Local(
|
||||
$modules_webfonts::get_instance(),
|
||||
$google_fonts::get_instance()
|
||||
);
|
||||
|
||||
$local_fonts->add_styles();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue fontawesome in Gutenberg Editor.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
*/
|
||||
public function enqueue_fontawesome() {
|
||||
foreach ( $this->configs as $config_id => $args ) {
|
||||
|
||||
if ( $this->is_disabled( $args ) ) {
|
||||
continue;
|
||||
}
|
||||
$modules_css = $this->modules_css;
|
||||
if ( $modules_css::get_enqueue_fa() && apply_filters( 'kirki_load_fontawesome', true ) ) {
|
||||
wp_enqueue_script( 'kirki-fontawesome-font', 'https://use.fontawesome.com/30858dc40a.js', array(), '4.0.7', true );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class property for $configs.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.35
|
||||
*/
|
||||
private function set_configs() {
|
||||
return $this->configs = Kirki::$config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class property for $enabled.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.35
|
||||
*/
|
||||
private function set_enabled() {
|
||||
$this->enabled = ! $this->is_disabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class property for $modules_css.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.35
|
||||
*/
|
||||
private function set_modules_css() {
|
||||
$this->modules_css = Kirki_Modules_CSS::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class property for $google_fonts.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.35
|
||||
*/
|
||||
private function set_google_fonts() {
|
||||
$this->google_fonts = Kirki_Fonts_Google::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class property for $modules_webfonts.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.35
|
||||
*/
|
||||
private function set_modules_webfonts() {
|
||||
$this->modules_webfonts = Kirki_Modules_Webfonts::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class property for $modules_webfont_loader.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.35
|
||||
*/
|
||||
private function set_modules_webfont_loader() {
|
||||
$this->modules_webfont_loader = Kirki_Modules_Webfont_Loader::get_instance();
|
||||
}
|
||||
}
|
104
functions/kirki/modules/icons/class-kirki-modules-icons.php
Normal file
104
functions/kirki/modules/icons/class-kirki-modules-icons.php
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
/**
|
||||
* Try to automatically generate the script necessary for adding icons to panels & section
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Core
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds scripts for icons in sections & panels.
|
||||
*/
|
||||
class Kirki_Modules_Icons {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* An array of panels and sections with icons.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private static $icons = array();
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'customize_controls_enqueue_scripts', array( $this, 'customize_controls_enqueue_scripts' ), 99 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds icon for a section/panel.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @param string $id The panel or section ID.
|
||||
* @param string $icon The icon to add.
|
||||
* @param string $context Lowercase 'section' or 'panel'.
|
||||
*/
|
||||
public function add_icon( $id, $icon, $context = 'section' ) {
|
||||
self::$icons[ $context ][ $id ] = trim( $icon );
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the script in a way that will be compatible with WordPress.
|
||||
*/
|
||||
public function customize_controls_enqueue_scripts() {
|
||||
$sections = Kirki::$sections;
|
||||
$panels = Kirki::$panels;
|
||||
|
||||
// Parse sections and find ones with icons.
|
||||
foreach ( $sections as $section ) {
|
||||
if ( isset( $section['icon'] ) ) {
|
||||
$this->add_icon( $section['id'], $section['icon'], 'section' );
|
||||
}
|
||||
}
|
||||
|
||||
// Parse panels and find ones with icons.
|
||||
foreach ( $panels as $panel ) {
|
||||
if ( isset( $panel['icon'] ) ) {
|
||||
$this->add_icon( $panel['id'], $panel['icon'], 'panel' );
|
||||
}
|
||||
}
|
||||
|
||||
wp_enqueue_script( 'kirki_panel_and_section_icons', trailingslashit( Kirki::$url ) . 'modules/icons/icons.js', array( 'jquery', 'customize-base', 'customize-controls' ), KIRKI_VERSION, true );
|
||||
wp_localize_script( 'kirki_panel_and_section_icons', 'kirkiIcons', self::$icons );
|
||||
}
|
||||
}
|
30
functions/kirki/modules/icons/icons.js
Normal file
30
functions/kirki/modules/icons/icons.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
/* global kirkiIcons */
|
||||
jQuery( document ).ready( function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
if ( ! _.isUndefined( kirkiIcons.section ) ) {
|
||||
|
||||
// Parse sections and add icons.
|
||||
_.each( kirkiIcons.section, function( icon, sectionID ) {
|
||||
|
||||
// Add icons in list.
|
||||
jQuery( '#accordion-section-' + sectionID + ' > h3' ).addClass( 'dashicons-before ' + icon );
|
||||
|
||||
// Add icons on titles when a section is open.
|
||||
jQuery( '#sub-accordion-section-' + sectionID + ' .customize-section-title > h3' ).append( '<span class="dashicons ' + icon + '" style="float:left;padding-right:.1em;padding-top:2px;"></span>' );
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
if ( ! _.isUndefined( kirkiIcons.panel ) ) {
|
||||
|
||||
_.each( kirkiIcons.panel, function( icon, panelID ) {
|
||||
|
||||
// Add icons in lists & headers.
|
||||
jQuery( '#accordion-panel-' + panelID + ' > h3, #sub-accordion-panel-' + panelID + ' .panel-title' ).addClass( 'dashicons-before ' + icon );
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
} );
|
167
functions/kirki/modules/loading/class-kirki-modules-loading.php
Normal file
167
functions/kirki/modules/loading/class-kirki-modules-loading.php
Normal file
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
/**
|
||||
* Adds a custom loading icon when the previewer refreshes.
|
||||
*
|
||||
* @package Kirki
|
||||
* @subpackage Modules
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Modifies the loading overlay.
|
||||
*/
|
||||
class Kirki_Modules_Loading {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on init.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function init() {
|
||||
global $wp_customize;
|
||||
if ( ! $wp_customize ) {
|
||||
return;
|
||||
}
|
||||
// Allow disabling the custom loader using the kirki_config filter.
|
||||
$config = apply_filters( 'kirki_config', array() );
|
||||
if ( isset( $config['disable_loader'] ) && true === $config['disable_loader'] ) {
|
||||
return;
|
||||
}
|
||||
// Add the "loading" icon.
|
||||
add_action( 'wp_footer', array( $this, 'add_loader_to_footer' ) );
|
||||
add_action( 'wp_head', array( $this, 'add_loader_styles_to_header' ), 99 );
|
||||
$this->remove_default_loading_styles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom "loading" div $ its styles when changes are made to the customizer.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function add_loader_to_footer() {
|
||||
?>
|
||||
<div class="kirki-customizer-loading-wrapper">
|
||||
<span class="kirki-customizer-loading"></span>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the loader CSS to our `<head>`.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function add_loader_styles_to_header() {
|
||||
?>
|
||||
<style>
|
||||
body.wp-customizer-unloading {
|
||||
opacity: 1;
|
||||
cursor: progress !important;
|
||||
-webkit-transition: none;
|
||||
transition: none;
|
||||
}
|
||||
body.wp-customizer-unloading * {
|
||||
pointer-events: none !important;
|
||||
}
|
||||
.kirki-customizer-loading-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: rgba(255,255,255,0.83);
|
||||
z-index: 999999;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.5s;
|
||||
transition: opacity 0.5s;
|
||||
background-image: url("<?php echo esc_url_raw( Kirki::$url ); ?>/assets/images/kirki-logo.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
body.wp-customizer-unloading .kirki-customizer-loading-wrapper {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
.kirki-customizer-loading-wrapper .kirki-customizer-loading {
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -30px;
|
||||
background-color: rgba(0,0,0,.83);
|
||||
border-radius: 100%;
|
||||
-webkit-animation: sk-scaleout 1.0s infinite ease-in-out;
|
||||
animation: sk-scaleout 1.0s infinite ease-in-out;
|
||||
}
|
||||
@-webkit-keyframes sk-scaleout {
|
||||
0% { -webkit-transform: scale(0) }
|
||||
100% {
|
||||
-webkit-transform: scale(1.0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes sk-scaleout {
|
||||
0% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: scale(1.0);
|
||||
transform: scale(1.0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the default loader styles from WP Core.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove_default_loading_styles() {
|
||||
global $wp_customize;
|
||||
remove_action( 'wp_head', array( $wp_customize, 'customize_preview_loading_style' ) );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
/**
|
||||
* Customize_Queried_Post_Info class.
|
||||
*
|
||||
* @package CustomizeQueriedPostInfo
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class Customize_Queried_Post_Info.
|
||||
*/
|
||||
class Kirki_Modules_Post_Meta {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) );
|
||||
add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue Customizer control scripts.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function enqueue_control_scripts() {
|
||||
wp_enqueue_script( 'kirki_post_meta_previewed_controls', trailingslashit( Kirki::$url ) . 'modules/post-meta/customize-controls.js', array( 'jquery', 'customize-controls' ), KIRKI_VERSION, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Customizer preview.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function customize_preview_init() {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_preview_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue script for Customizer preview.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function enqueue_preview_scripts() {
|
||||
wp_enqueue_script( 'kirki_post_meta_previewed_preview', trailingslashit( Kirki::$url ) . 'modules/post-meta/customize-preview.js', array( 'jquery', 'customize-preview' ), KIRKI_VERSION, true );
|
||||
|
||||
$wp_scripts = wp_scripts();
|
||||
$queried_post = null;
|
||||
if ( is_singular() && get_queried_object() ) {
|
||||
$queried_post = get_queried_object();
|
||||
$queried_post->meta = get_post_custom( $queried_post->id );
|
||||
}
|
||||
$wp_scripts->add_data( 'kirki_post_meta_previewed_preview', 'data', sprintf( 'var _customizePostPreviewedQueriedObject = %s;', wp_json_encode( $queried_post ) ) );
|
||||
}
|
||||
}
|
23
functions/kirki/modules/post-meta/customize-controls.js
Normal file
23
functions/kirki/modules/post-meta/customize-controls.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
jQuery( document ).ready( function() {
|
||||
|
||||
var self;
|
||||
|
||||
self = {
|
||||
queriedPost: new wp.customize.Value()
|
||||
};
|
||||
|
||||
// Listen for queried-post messages from the preview.
|
||||
wp.customize.bind( 'ready', function() {
|
||||
wp.customize.previewer.bind( 'queried-post', function( queriedPost ) {
|
||||
self.queriedPost.set( queriedPost || false );
|
||||
} );
|
||||
} );
|
||||
|
||||
// Listen for post
|
||||
self.queriedPost.bind( function( newPost, oldPost ) {
|
||||
window.kirkiPost = false;
|
||||
if ( newPost || oldPost ) {
|
||||
window.kirkiPost = ( newPost ) ? newPost : oldPost;
|
||||
}
|
||||
} );
|
||||
} );
|
14
functions/kirki/modules/post-meta/customize-preview.js
Normal file
14
functions/kirki/modules/post-meta/customize-preview.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* global wp, _customizePostPreviewedQueriedObject */
|
||||
jQuery( document ).ready( function() {
|
||||
|
||||
var self = {
|
||||
queriedPost: ( ! _.isUndefined( _customizePostPreviewedQueriedObject ) ) ? _customizePostPreviewedQueriedObject : null
|
||||
};
|
||||
|
||||
// Send the queried post object to the Customizer pane when ready.
|
||||
wp.customize.bind( 'preview-ready', function() {
|
||||
wp.customize.preview.bind( 'active', function() {
|
||||
wp.customize.preview.send( 'queried-post', self.queriedPost );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,515 @@
|
|||
<?php
|
||||
/**
|
||||
* Automatic postMessage scripts calculation for Kirki controls.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds styles to the customizer.
|
||||
*/
|
||||
class Kirki_Modules_PostMessage {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* The script.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @var string
|
||||
*/
|
||||
protected $script = '';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'customize_preview_init', array( $this, 'postmessage' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the postMessage script
|
||||
* and adds variables to it using the wp_localize_script function.
|
||||
* The rest is handled via JS.
|
||||
*/
|
||||
public function postmessage() {
|
||||
wp_enqueue_script( 'kirki_auto_postmessage', trailingslashit( Kirki::$url ) . 'modules/postmessage/postmessage.js', array( 'jquery', 'customize-preview' ), KIRKI_VERSION, true );
|
||||
$fields = Kirki::$fields;
|
||||
foreach ( $fields as $field ) {
|
||||
if ( isset( $field['transport'] ) && 'postMessage' === $field['transport'] && isset( $field['js_vars'] ) && ! empty( $field['js_vars'] ) && is_array( $field['js_vars'] ) && isset( $field['settings'] ) ) {
|
||||
$this->script .= $this->script( $field );
|
||||
}
|
||||
}
|
||||
$this->script = apply_filters( 'kirki_postmessage_script', $this->script );
|
||||
wp_add_inline_script( 'kirki_auto_postmessage', $this->script, 'after' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates script for a single field.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @param array $args The arguments.
|
||||
*/
|
||||
protected function script( $args ) {
|
||||
$script = 'wp.customize(\'' . $args['settings'] . '\',function(value){value.bind(function(newval){';
|
||||
|
||||
$add_css = false;
|
||||
foreach ( $args['js_vars'] as $js_var ) {
|
||||
if ( ! isset( $js_var['function'] ) || 'html' !== $js_var['function'] ) {
|
||||
$add_css = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $add_css ) {
|
||||
|
||||
// append unique style tag if not exist
|
||||
// The style ID.
|
||||
$style_id = 'kirki-postmessage-' . str_replace( array( '[', ']' ), '', $args['settings'] );
|
||||
$script .= 'if(null===document.getElementById(\'' . $style_id . '\')||\'undefined\'===typeof document.getElementById(\'' . $style_id . '\')){jQuery(\'head\').append(\'<style id="' . $style_id . '"></style>\');}';
|
||||
}
|
||||
|
||||
// Add anything we need before the main script.
|
||||
$script .= $this->before_script( $args );
|
||||
|
||||
$field = array(
|
||||
'scripts' => array(),
|
||||
);
|
||||
|
||||
// Loop through the js_vars and generate the script.
|
||||
foreach ( $args['js_vars'] as $key => $js_var ) {
|
||||
|
||||
// Skip styles if "exclude" is defined and value is excluded.
|
||||
if ( isset( $js_var['exclude'] ) ) {
|
||||
$js_var['exclude'] = (array) $js_var['exclude'];
|
||||
$script .= 'exclude=false;';
|
||||
foreach ( $js_var['exclude'] as $exclussion ) {
|
||||
$script .= "if(newval=='{$exclussion}'||(''==='{$exclussion}'&&_.isObject(newval)&&_.isEmpty(newval))){exclude=true;}";
|
||||
}
|
||||
}
|
||||
if ( isset( $js_var['element'] ) ) {
|
||||
|
||||
// Array to string.
|
||||
if ( is_array( $js_var['element'] ) ) {
|
||||
$js_var['element'] = implode( ',', $js_var['element'] );
|
||||
}
|
||||
|
||||
// Replace single quotes with double quotes to avoid issues with the compiled JS.
|
||||
$js_var['element'] = str_replace( '\'', '"', $js_var['element'] );
|
||||
}
|
||||
if ( isset( $js_var['function'] ) && 'html' === $js_var['function'] ) {
|
||||
$script .= $this->script_html_var( $js_var );
|
||||
continue;
|
||||
}
|
||||
$js_var['index_key'] = $key;
|
||||
$callback = $this->get_callback( $args );
|
||||
if ( is_callable( $callback ) ) {
|
||||
$field['scripts'][ $key ] = call_user_func_array( $callback, array( $js_var, $args ) );
|
||||
continue;
|
||||
}
|
||||
$field['scripts'][ $key ] = $this->script_var( $js_var );
|
||||
}
|
||||
$combo_extra_script = '';
|
||||
$combo_css_script = '';
|
||||
foreach ( $field['scripts'] as $script_array ) {
|
||||
$combo_extra_script .= $script_array['script'];
|
||||
$combo_css_script .= ( 'css' !== $combo_css_script ) ? $script_array['css'] : '';
|
||||
}
|
||||
$text = ( 'css' === $combo_css_script ) ? 'css' : '\'' . $combo_css_script . '\'';
|
||||
|
||||
$script .= $combo_extra_script . "var cssContent={$text};";
|
||||
if ( isset( $js_var['exclude'] ) ) {
|
||||
$script .= 'if(true===exclude){cssContent="";}';
|
||||
}
|
||||
if ( $add_css ) {
|
||||
$script .= "jQuery('#{$style_id}').text(cssContent);jQuery('#{$style_id}').appendTo('head');";
|
||||
}
|
||||
$script .= '});});';
|
||||
return $script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates script for a single js_var when using "html" as function.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @param array $args The arguments for this js_var.
|
||||
*/
|
||||
protected function script_html_var( $args ) {
|
||||
$script = ( isset( $args['choice'] ) ) ? "newval=newval['{$args['choice']}'];" : '';
|
||||
|
||||
// Apply the value_pattern.
|
||||
if ( isset( $args['value_pattern'] ) && '' !== $args['value_pattern'] ) {
|
||||
$script .= $this->value_pattern_replacements( 'newval', $args );
|
||||
}
|
||||
|
||||
if ( isset( $args['attr'] ) ) {
|
||||
$script .= "jQuery('{$args['element']}').attr('{$args['attr']}',newval);";
|
||||
return $script;
|
||||
}
|
||||
$script .= "jQuery('{$args['element']}').html(newval);";
|
||||
return $script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates script for a single js_var.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @param array $args The arguments for this js_var.
|
||||
*/
|
||||
protected function script_var( $args ) {
|
||||
$script = '';
|
||||
$property_script = '';
|
||||
|
||||
$value_key = 'newval' . $args['index_key'];
|
||||
$property_script .= $value_key . '=newval;';
|
||||
|
||||
$args = $this->get_args( $args );
|
||||
|
||||
// Apply callback to the value if a callback is defined.
|
||||
if ( ! empty( $args['js_callback'] ) && is_array( $args['js_callback'] ) && isset( $args['js_callback'][0] ) && ! empty( $args['js_callback'][0] ) ) {
|
||||
$script .= $value_key . '=' . $args['js_callback'][0] . '(' . $value_key . ',' . $args['js_callback'][1] . ');';
|
||||
}
|
||||
|
||||
// Apply the value_pattern.
|
||||
if ( '' !== $args['value_pattern'] ) {
|
||||
$script .= $this->value_pattern_replacements( $value_key, $args );
|
||||
}
|
||||
|
||||
// Tweak to add url() for background-images.
|
||||
if ( 'background-image' === $args['property'] && ( ! isset( $args['value_pattern'] ) || false === strpos( $args['value_pattern'], 'gradient' ) ) ) {
|
||||
$script .= 'if(-1===' . $value_key . '.indexOf(\'url(\')){' . $value_key . '=\'url("\'+' . $value_key . '+\'")\';}';
|
||||
}
|
||||
|
||||
// Apply prefix.
|
||||
$value = $value_key;
|
||||
if ( '' !== $args['prefix'] ) {
|
||||
$value = "'" . $args['prefix'] . "'+" . $value_key;
|
||||
}
|
||||
$css = $args['element'] . '{' . $args['property'] . ':\'+' . $value . '+\'' . $args['units'] . $args['suffix'] . ';}';
|
||||
if ( isset( $args['media_query'] ) ) {
|
||||
$css = $args['media_query'] . '{' . $css . '}';
|
||||
}
|
||||
return array(
|
||||
'script' => $property_script . $script,
|
||||
'css' => $css,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes script generation for fields that save an array.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @param array $args The arguments for this js_var.
|
||||
*/
|
||||
protected function script_var_array( $args ) {
|
||||
$script = ( 0 === $args['index_key'] ) ? 'css=\'\';' : '';
|
||||
$property_script = '';
|
||||
|
||||
// Define choice.
|
||||
$choice = ( isset( $args['choice'] ) && '' !== $args['choice'] ) ? $args['choice'] : '';
|
||||
|
||||
$value_key = 'newval' . $args['index_key'];
|
||||
$property_script .= $value_key . '=newval;';
|
||||
|
||||
$args = $this->get_args( $args );
|
||||
|
||||
// Apply callback to the value if a callback is defined.
|
||||
if ( ! empty( $args['js_callback'] ) && is_array( $args['js_callback'] ) && isset( $args['js_callback'][0] ) && ! empty( $args['js_callback'][0] ) ) {
|
||||
$script .= $value_key . '=' . $args['js_callback'][0] . '(' . $value_key . ',' . $args['js_callback'][1] . ');';
|
||||
}
|
||||
$script .= '_.each(' . $value_key . ', function(subValue,subKey){';
|
||||
|
||||
// Apply the value_pattern.
|
||||
if ( '' !== $args['value_pattern'] ) {
|
||||
$script .= $this->value_pattern_replacements( 'subValue', $args );
|
||||
}
|
||||
|
||||
// Tweak to add url() for background-images.
|
||||
if ( '' === $choice || 'background-image' === $choice ) {
|
||||
$script .= 'if(\'background-image\'===\'' . $args['property'] . '\'||\'background-image\'===subKey){if(-1===subValue.indexOf(\'url(\')){subValue=\'url("\'+subValue+\'")\';}}';
|
||||
}
|
||||
|
||||
// Apply prefix.
|
||||
$value = $value_key;
|
||||
if ( '' !== $args['prefix'] ) {
|
||||
$value = '\'' . $args['prefix'] . '\'+subValue';
|
||||
}
|
||||
|
||||
// Mostly used for padding, margin & position properties.
|
||||
$direction_script = 'if(_.contains([\'top\',\'bottom\',\'left\',\'right\'],subKey)){';
|
||||
$direction_script .= 'css+=\'' . $args['element'] . '{' . $args['property'] . '-\'+subKey+\':\'+subValue+\'' . $args['units'] . $args['suffix'] . ';}\';}';
|
||||
|
||||
// Allows us to apply this just for a specific choice in the array of the values.
|
||||
if ( '' !== $choice ) {
|
||||
$choice_is_direction = ( false !== strpos( $choice, 'top' ) || false !== strpos( $choice, 'bottom' ) || false !== strpos( $choice, 'left' ) || false !== strpos( $choice, 'right' ) );
|
||||
|
||||
// The script.
|
||||
$script .= 'if(\'' . $choice . '\'===subKey){';
|
||||
$script .= ( $choice_is_direction ) ? $direction_script . 'else{' : '';
|
||||
$script .= 'css+=\'' . $args['element'] . '{' . $args['property'] . ':\'+subValue+\';}\';';
|
||||
$script .= ( $choice_is_direction ) ? '}' : '';
|
||||
$script .= '}';
|
||||
} else {
|
||||
|
||||
// This is where most object-based fields will go.
|
||||
$script .= $direction_script . 'else{css+=\'' . $args['element'] . '{\'+subKey+\':\'+subValue+\'' . $args['units'] . $args['suffix'] . ';}\';}';
|
||||
}
|
||||
$script .= '});';
|
||||
|
||||
if ( isset( $args['media_query'] ) ) {
|
||||
$script .= 'css=\'' . $args['media_query'] . '{\'+css+\'}\';';
|
||||
}
|
||||
|
||||
return array(
|
||||
'script' => $property_script . $script,
|
||||
'css' => 'css',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes script generation for typography fields.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @param array $args The arguments for this js_var.
|
||||
* @param array $field The field arguments.
|
||||
*/
|
||||
protected function script_var_typography( $args, $field ) {
|
||||
$args = $this->get_args( $args );
|
||||
|
||||
$script = '';
|
||||
$css = '';
|
||||
|
||||
// Load the font using WenFontloader.
|
||||
// This is a bit ugly because wp_add_inline_script doesn't allow adding <script> directly.
|
||||
$webfont_loader = 'sc=\'a\';jQuery(\'head\').append(sc.replace(\'a\',\'<\')+\'script>if(!_.isUndefined(WebFont)&&fontFamily){WebFont.load({google:{families:["\'+fontFamily.replace( /\"/g, \'"\' )+\':\'+variant+\':cyrillic,cyrillic-ext,devanagari,greek,greek-ext,khmer,latin,latin-ext,vietnamese,hebrew,arabic,bengali,gujarati,tamil,telugu,thai"]}});}\'+sc.replace(\'a\',\'<\')+\'/script>\');';
|
||||
|
||||
// Add the css.
|
||||
$css_build_array = array(
|
||||
'font-family' => 'fontFamily',
|
||||
'font-size' => 'fontSize',
|
||||
'line-height' => 'lineHeight',
|
||||
'letter-spacing' => 'letterSpacing',
|
||||
'word-spacing' => 'wordSpacing',
|
||||
'text-align' => 'textAlign',
|
||||
'text-transform' => 'textTransform',
|
||||
'text-decoration' => 'textDecoration',
|
||||
'color' => 'color',
|
||||
'font-weight' => 'fontWeight',
|
||||
'font-style' => 'fontStyle',
|
||||
);
|
||||
$choice_condition = ( isset( $args['choice'] ) && '' !== $args['choice'] && isset( $css_build_array[ $args['choice'] ] ) );
|
||||
$script .= ( ! $choice_condition ) ? $webfont_loader : '';
|
||||
foreach ( $css_build_array as $property => $var ) {
|
||||
if ( $choice_condition && $property !== $args['choice'] ) {
|
||||
continue;
|
||||
}
|
||||
// Fixes https://github.com/aristath/kirki/issues/1436.
|
||||
if ( ! isset( $field['default'] ) || (
|
||||
( 'font-family' === $property && ! isset( $field['default']['font-family'] ) ) ||
|
||||
( 'font-size' === $property && ! isset( $field['default']['font-size'] ) ) ||
|
||||
( 'line-height' === $property && ! isset( $field['default']['line-height'] ) ) ||
|
||||
( 'letter-spacing' === $property && ! isset( $field['default']['letter-spacing'] ) ) ||
|
||||
( 'word-spacing' === $property && ! isset( $field['default']['word-spacing'] ) ) ||
|
||||
( 'text-align' === $property && ! isset( $field['default']['text-align'] ) ) ||
|
||||
( 'text-transform' === $property && ! isset( $field['default']['text-transform'] ) ) ||
|
||||
( 'text-decoration' === $property && ! isset( $field['default']['text-decoration'] ) ) ||
|
||||
( 'color' === $property && ! isset( $field['default']['color'] ) ) ||
|
||||
( 'font-weight' === $property && ! isset( $field['default']['variant'] ) && ! isset( $field['default']['font-weight'] ) ) ||
|
||||
( 'font-style' === $property && ! isset( $field['default']['variant'] ) && ! isset( $field['default']['font-style'] ) )
|
||||
) ) {
|
||||
continue;
|
||||
}
|
||||
$script .= ( $choice_condition && 'font-family' === $args['choice'] ) ? $webfont_loader : '';
|
||||
|
||||
if ( 'font-family' === $property || ( isset( $args['choice'] ) && 'font-family' === $args['choice'] ) ) {
|
||||
$css .= 'fontFamilyCSS=fontFamily;if(0<fontFamily.indexOf(\' \')&&-1===fontFamily.indexOf(\'"\')){fontFamilyCSS=\'"\'+fontFamily+\'"\';}';
|
||||
$var = 'fontFamilyCSS';
|
||||
}
|
||||
$var = ( ( empty( $args['prefix'] ) ) ? '' : '\'' . $args['prefix'] . '\'+' ) . $var . ( ( empty( $args['units'] ) ) ? '' : '+\'' . $args['units'] . '\'' ) . ( ( empty( $args['suffix'] ) ) ? '' : '+\'' . $args['suffix'] . '\'' );
|
||||
$css .= 'css+=(\'\'!==' . $var . ')?\'' . $args['element'] . '\'+\'{' . $property . ':\'+' . $var . '+\';}\':\'\';';
|
||||
}
|
||||
|
||||
$script .= $css;
|
||||
if ( isset( $args['media_query'] ) ) {
|
||||
$script .= 'css=\'' . $args['media_query'] . '{\'+css+\'}\';';
|
||||
}
|
||||
return array(
|
||||
'script' => $script,
|
||||
'css' => 'css',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes script generation for typography fields.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @param array $args The arguments for this js_var.
|
||||
*/
|
||||
protected function script_var_image( $args ) {
|
||||
$return = $this->script_var( $args );
|
||||
return array(
|
||||
'script' => 'newval=(!_.isUndefined(newval.url))?newval.url:newval;' . $return['script'],
|
||||
'css' => $return['css'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds anything we need before the main script.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @param array $args The field args.
|
||||
* @return string
|
||||
*/
|
||||
private function before_script( $args ) {
|
||||
$script = '';
|
||||
if ( isset( $args['type'] ) ) {
|
||||
switch ( $args['type'] ) {
|
||||
case 'kirki-typography':
|
||||
$script .= 'fontFamily=(_.isUndefined(newval[\'font-family\']))?\'\':newval[\'font-family\'];variant=(_.isUndefined(newval.variant))?\'400\':newval.variant;fontSize=(_.isUndefined(newval[\'font-size\']))?\'\':newval[\'font-size\'];lineHeight=(_.isUndefined(newval[\'line-height\']))?\'\':newval[\'line-height\'];letterSpacing=(_.isUndefined(newval[\'letter-spacing\']))?\'\':newval[\'letter-spacing\'];wordSpacing=(_.isUndefined(newval[\'word-spacing\']))?\'\':newval[\'word-spacing\'];textAlign=(_.isUndefined(newval[\'text-align\']))?\'\':newval[\'text-align\'];textTransform=(_.isUndefined(newval[\'text-transform\']))?\'\':newval[\'text-transform\'];textDecoration=(_.isUndefined(newval[\'text-decoration\']))?\'\':newval[\'text-decoration\'];color=(_.isUndefined(newval.color))?\'\':newval.color;fw=(!_.isString(newval.variant))?\'400\':newval.variant.match(/\d/g);fontWeight=(!_.isObject(fw))?400:fw.join(\'\');fontStyle=(variant&&-1!==variant.indexOf(\'italic\'))?\'italic\':\'normal\';css=\'\';';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the arguments and makes sure they are all there.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @param array $args The arguments.
|
||||
* @return array
|
||||
*/
|
||||
private function get_args( $args ) {
|
||||
|
||||
// Make sure everything is defined to avoid "undefined index" errors.
|
||||
$args = wp_parse_args(
|
||||
$args, array(
|
||||
'element' => '',
|
||||
'property' => '',
|
||||
'prefix' => '',
|
||||
'suffix' => '',
|
||||
'units' => '',
|
||||
'js_callback' => array( '', '' ),
|
||||
'value_pattern' => '',
|
||||
)
|
||||
);
|
||||
|
||||
// Element should be a string.
|
||||
if ( is_array( $args['element'] ) ) {
|
||||
$args['element'] = implode( ',', $args['element'] );
|
||||
}
|
||||
|
||||
// Make sure arguments that are passed-on to callbacks are strings.
|
||||
if ( is_array( $args['js_callback'] ) && isset( $args['js_callback'][1] ) && is_array( $args['js_callback'][1] ) ) {
|
||||
$args['js_callback'][1] = wp_json_encode( $args['js_callback'][1] );
|
||||
}
|
||||
|
||||
if ( ! isset( $args['js_callback'][1] ) ) {
|
||||
$args['js_callback'][1] = '';
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns script for value_pattern & replacements.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @param string $value The value placeholder.
|
||||
* @param array $js_vars The js_vars argument.
|
||||
* @return string The script.
|
||||
*/
|
||||
private function value_pattern_replacements( $value, $js_vars ) {
|
||||
$script = '';
|
||||
$alias = $value;
|
||||
if ( ! isset( $js_vars['value_pattern'] ) ) {
|
||||
return $value;
|
||||
}
|
||||
$value = $js_vars['value_pattern'];
|
||||
if ( isset( $js_vars['pattern_replace'] ) ) {
|
||||
$script .= 'settings=window.wp.customize.get();';
|
||||
foreach ( $js_vars['pattern_replace'] as $search => $replace ) {
|
||||
$replace = '\'+settings["' . $replace . '"]+\'';
|
||||
$value = str_replace( $search, $replace, $js_vars['value_pattern'] );
|
||||
$value = trim( $value, '+' );
|
||||
}
|
||||
}
|
||||
$value_compiled = str_replace( '$', '\'+' . $alias . '+\'', $value );
|
||||
$value_compiled = trim( $value_compiled, '+' );
|
||||
|
||||
return $script . $alias . '=\'' . $value_compiled . '\';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the callback function/method we're going to use for this field.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @param array $args The field args.
|
||||
* @return string|array A callable function or method.
|
||||
*/
|
||||
protected function get_callback( $args ) {
|
||||
switch ( $args['type'] ) {
|
||||
case 'kirki-background':
|
||||
case 'kirki-dimensions':
|
||||
case 'kirki-multicolor':
|
||||
case 'kirki-sortable':
|
||||
$callback = array( $this, 'script_var_array' );
|
||||
break;
|
||||
case 'kirki-typography':
|
||||
$callback = array( $this, 'script_var_typography' );
|
||||
break;
|
||||
case 'kirki-image':
|
||||
$callback = array( $this, 'script_var_image' );
|
||||
break;
|
||||
default:
|
||||
$callback = array( $this, 'script_var' );
|
||||
}
|
||||
return $callback;
|
||||
}
|
||||
}
|
4
functions/kirki/modules/postmessage/postmessage.js
Normal file
4
functions/kirki/modules/postmessage/postmessage.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* This file is empty on purpose.
|
||||
* Scripts are added dynamically using wp_add_inline_script() on this.
|
||||
*/
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
/**
|
||||
* Automatic preset scripts calculation for Kirki controls.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.26
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds styles to the customizer.
|
||||
*/
|
||||
class Kirki_Modules_Preset {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.26
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.26
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_controls_print_footer_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.26
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.26
|
||||
*/
|
||||
public function customize_controls_print_footer_scripts() {
|
||||
wp_enqueue_script( 'kirki-preset', trailingslashit( Kirki::$url ) . 'modules/preset/preset.js', array( 'jquery' ), KIRKI_VERSION, false );
|
||||
}
|
||||
}
|
32
functions/kirki/modules/preset/preset.js
Normal file
32
functions/kirki/modules/preset/preset.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* global kirkiSetSettingValue */
|
||||
jQuery( document ).ready( function() {
|
||||
|
||||
// Loop Controls.
|
||||
wp.customize.control.each( function( control ) {
|
||||
|
||||
// Check if we have a preset defined.
|
||||
if ( control.params && control.params.preset && ! _.isEmpty( control.params.preset ) ) {
|
||||
wp.customize( control.id, function( value ) {
|
||||
|
||||
// Listen to value changes.
|
||||
value.bind( function( to ) {
|
||||
|
||||
// Loop preset definitions.
|
||||
_.each( control.params.preset, function( preset, valueToListen ) {
|
||||
|
||||
// Check if the value set want is the same as the one we're looking for.
|
||||
if ( valueToListen === to ) {
|
||||
|
||||
// Loop settings defined inside the preset.
|
||||
_.each( preset.settings, function( controlValue, controlID ) {
|
||||
|
||||
// Set the value.
|
||||
kirkiSetSettingValue.set( controlID, controlValue );
|
||||
} );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles sections created via the Kirki API.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handle selective refreshes introduced in WordPress 4.5.
|
||||
*/
|
||||
class Kirki_Modules_Selective_Refresh {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Adds any necessary actions & filters.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'customize_register', array( $this, 'register_partials' ), 99 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses all fields and searches for the "partial_refresh" argument inside them.
|
||||
* If that argument is found, then it starts parsing the array of arguments.
|
||||
* Registers a selective_refresh in the customizer for each one of them.
|
||||
*
|
||||
* @param object $wp_customize WP_Customize_Manager.
|
||||
*/
|
||||
public function register_partials( $wp_customize ) {
|
||||
|
||||
// Abort if selective refresh is not available.
|
||||
if ( ! isset( $wp_customize->selective_refresh ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get an array of all fields.
|
||||
$fields = Kirki::$fields;
|
||||
|
||||
// Start parsing the fields.
|
||||
foreach ( $fields as $field ) {
|
||||
if ( isset( $field['partial_refresh'] ) && ! empty( $field['partial_refresh'] ) ) {
|
||||
// Start going through each item in the array of partial refreshes.
|
||||
foreach ( $field['partial_refresh'] as $partial_refresh => $partial_refresh_args ) {
|
||||
// If we have all we need, create the selective refresh call.
|
||||
if ( isset( $partial_refresh_args['render_callback'] ) && isset( $partial_refresh_args['selector'] ) ) {
|
||||
$partial_refresh_args = wp_parse_args(
|
||||
$partial_refresh_args, array(
|
||||
'settings' => $field['settings'],
|
||||
)
|
||||
);
|
||||
$wp_customize->selective_refresh->add_partial( $partial_refresh, $partial_refresh_args );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
/**
|
||||
* Injects tooltips to controls when the 'tooltip' argument is used.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds script for tooltips.
|
||||
*/
|
||||
class Kirki_Modules_Tooltips {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* An array containing field identifieds and their tooltips.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var array
|
||||
*/
|
||||
private $tooltips_content = array();
|
||||
|
||||
/**
|
||||
* The class constructor
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_controls_print_footer_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses fields and if any tooltips are found, they are added to the
|
||||
* object's $tooltips_content property.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private function parse_fields() {
|
||||
$fields = Kirki::$fields;
|
||||
foreach ( $fields as $field ) {
|
||||
if ( isset( $field['tooltip'] ) && ! empty( $field['tooltip'] ) ) {
|
||||
// Get the control ID and properly format it for the tooltips.
|
||||
$id = str_replace( '[', '-', str_replace( ']', '', $field['settings'] ) );
|
||||
// Add the tooltips content.
|
||||
$this->tooltips_content[ $id ] = array(
|
||||
'id' => $id,
|
||||
'content' => $field['tooltip'],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows us to add a tooltip to any control.
|
||||
*
|
||||
* @access public
|
||||
* @since 4.2.0
|
||||
* @param string $field_id The field-ID.
|
||||
* @param string $tooltip The tooltip content.
|
||||
*/
|
||||
public function add_tooltip( $field_id, $tooltip ) {
|
||||
$this->tooltips_content[ $field_id ] = array(
|
||||
'id' => sanitize_key( $field_id ),
|
||||
'content' => wp_kses_post( $tooltip ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function customize_controls_print_footer_scripts() {
|
||||
$this->parse_fields();
|
||||
|
||||
wp_enqueue_script( 'kirki-tooltip', trailingslashit( Kirki::$url ) . 'modules/tooltips/tooltip.js', array( 'jquery' ), KIRKI_VERSION, false );
|
||||
wp_localize_script( 'kirki-tooltip', 'kirkiTooltips', $this->tooltips_content );
|
||||
wp_enqueue_style( 'kirki-tooltip', trailingslashit( Kirki::$url ) . 'modules/tooltips/tooltip.css', array(), KIRKI_VERSION );
|
||||
}
|
||||
}
|
36
functions/kirki/modules/tooltips/tooltip.css
Normal file
36
functions/kirki/modules/tooltips/tooltip.css
Normal file
|
@ -0,0 +1,36 @@
|
|||
@charset "UTF-8";
|
||||
.tooltip-wrapper {
|
||||
float: right;
|
||||
position: relative;
|
||||
}
|
||||
.tooltip-wrapper .tooltip-trigger {
|
||||
text-decoration: none;
|
||||
cursor: help;
|
||||
}
|
||||
.tooltip-wrapper .tooltip-content {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: auto;
|
||||
top: -10px;
|
||||
left: -225px;
|
||||
background: #FFC107;
|
||||
color: #000;
|
||||
padding: 10px;
|
||||
z-index: 99999;
|
||||
border-radius: 3px;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
.tooltip-wrapper .tooltip-content a {
|
||||
color: #000;
|
||||
}
|
||||
.tooltip-wrapper .tooltip-content:after {
|
||||
content: "";
|
||||
font-family: dashicons;
|
||||
position: absolute;
|
||||
right: -12px;
|
||||
top: 11px;
|
||||
color: #FFC107;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=tooltip.css.map */
|
55
functions/kirki/modules/tooltips/tooltip.js
Normal file
55
functions/kirki/modules/tooltips/tooltip.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* global kirkiTooltips */
|
||||
jQuery( document ).ready( function() {
|
||||
|
||||
function kirkiTooltipAdd( control ) {
|
||||
_.each( kirkiTooltips, function( tooltip ) {
|
||||
let trigger,
|
||||
controlID,
|
||||
content;
|
||||
|
||||
if ( tooltip.id !== control.id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( control.container.find( '.tooltip-content' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
trigger = '<span class="tooltip-trigger" data-setting="' + tooltip.id + '"><span class="dashicons dashicons-editor-help"></span></span>';
|
||||
controlID = '#customize-control-' + tooltip.id;
|
||||
content = '<div class="tooltip-content hidden" data-setting="' + tooltip.id + '">' + tooltip.content + '</div>';
|
||||
|
||||
// Add the trigger & content.
|
||||
jQuery( '<div class="tooltip-wrapper">' + trigger + content + '</div>' ).prependTo( controlID );
|
||||
|
||||
// Handle onclick events.
|
||||
jQuery( '.tooltip-trigger[data-setting="' + tooltip.id + '"]' ).on( 'click', function() {
|
||||
jQuery( '.tooltip-content[data-setting="' + tooltip.id + '"]' ).toggleClass( 'hidden' );
|
||||
} );
|
||||
} );
|
||||
|
||||
// Close tooltips if we click anywhere else.
|
||||
jQuery( document ).mouseup( function( e ) {
|
||||
|
||||
if ( ! jQuery( '.tooltip-content' ).is( e.target ) ) {
|
||||
if ( ! jQuery( '.tooltip-content' ).hasClass( 'hidden' ) ) {
|
||||
jQuery( '.tooltip-content' ).addClass( 'hidden' );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
wp.customize.control.each( function( control ) {
|
||||
wp.customize.section( control.section(), function( section ) {
|
||||
if ( section.expanded() || wp.customize.settings.autofocus.control === control.id ) {
|
||||
kirkiTooltipAdd( control );
|
||||
} else {
|
||||
section.expanded.bind( function( expanded ) {
|
||||
if ( expanded ) {
|
||||
kirkiTooltipAdd( control );
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
/**
|
||||
* WebFont-Loader Module.
|
||||
*
|
||||
* @see https://github.com/typekit/webfontloader
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.26
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds script for tooltips.
|
||||
*/
|
||||
class Kirki_Modules_Webfont_Loader {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.26
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Only load the webfont script if this is true.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.26
|
||||
* @var bool
|
||||
*/
|
||||
public static $load = false;
|
||||
|
||||
/**
|
||||
* The class constructor
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.26
|
||||
*/
|
||||
protected function __construct() {
|
||||
add_action( 'wp_head', array( $this, 'enqueue_scripts' ), 20 );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ), 20 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.26
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.26
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
global $wp_customize;
|
||||
if ( self::$load || $wp_customize || is_customize_preview() ) {
|
||||
wp_enqueue_script( 'webfont-loader', trailingslashit( Kirki::$url ) . 'modules/webfont-loader/vendor-typekit/webfontloader.js', array(), '3.0.28', true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the $load property of this object.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.35
|
||||
* @param bool $load Set to false to disable loading.
|
||||
* @return void
|
||||
*/
|
||||
public function set_load( $load ) {
|
||||
self::$load = $load;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
/* Web Font Loader v{{version}} - (c) Adobe Systems, Google. License: Apache 2.0 */
|
||||
(function(){{{source}}}());
|
|
@ -0,0 +1,46 @@
|
|||
goog.provide('webfont.CssClassName');
|
||||
|
||||
/**
|
||||
* Handles sanitization and construction of css class names.
|
||||
* @param {string=} opt_joinChar The character to join parts of the name on.
|
||||
* Defaults to '-'.
|
||||
* @constructor
|
||||
*/
|
||||
webfont.CssClassName = function(opt_joinChar) {
|
||||
/** @type {string} */
|
||||
this.joinChar_ = opt_joinChar || webfont.CssClassName.DEFAULT_JOIN_CHAR;
|
||||
};
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
webfont.CssClassName.DEFAULT_JOIN_CHAR = '-';
|
||||
|
||||
goog.scope(function () {
|
||||
var CssClassName = webfont.CssClassName;
|
||||
|
||||
/**
|
||||
* Sanitizes a string for use as a css class name. Removes non-word and
|
||||
* underscore characters.
|
||||
* @param {string} name The string.
|
||||
* @return {string} The sanitized string.
|
||||
*/
|
||||
CssClassName.prototype.sanitize = function(name) {
|
||||
return name.replace(/[\W_]+/g, '').toLowerCase();
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds a complete css class name given a variable number of parts.
|
||||
* Sanitizes, then joins the parts together.
|
||||
* @param {...string} var_args The parts to join.
|
||||
* @return {string} The sanitized and joined string.
|
||||
*/
|
||||
CssClassName.prototype.build = function(var_args) {
|
||||
var parts = []
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
parts.push(this.sanitize(arguments[i]));
|
||||
}
|
||||
return parts.join(this.joinChar_);
|
||||
};
|
||||
});
|
|
@ -0,0 +1,405 @@
|
|||
goog.provide('webfont.DomHelper');
|
||||
|
||||
/**
|
||||
* Handles common DOM manipulation tasks. The aim of this library is to cover
|
||||
* the needs of typical font loading. Not more, not less.
|
||||
* @param {Window} mainWindow The main window webfontloader.js is loaded in.
|
||||
* @param {Window=} opt_loadWindow The window we'll load the font into. By
|
||||
* default, the main window is used.
|
||||
* @constructor
|
||||
*/
|
||||
webfont.DomHelper = function(mainWindow, opt_loadWindow) {
|
||||
this.mainWindow_ = mainWindow;
|
||||
this.loadWindow_ = opt_loadWindow || mainWindow;
|
||||
|
||||
/** @type {string} */
|
||||
this.protocol_;
|
||||
|
||||
/** @type {Document} */
|
||||
this.document_ = this.loadWindow_.document;
|
||||
};
|
||||
|
||||
goog.scope(function () {
|
||||
var DomHelper = webfont.DomHelper;
|
||||
|
||||
/**
|
||||
* The NativeFontWatchRunnner depends on the correct and reliable
|
||||
* |onload| event, and browsers with the native font loading API
|
||||
* have reliable @onload support as far as we know. So we use the
|
||||
* event for such a case and unconditionally invokes the callback
|
||||
* otherwise.
|
||||
*
|
||||
* @const
|
||||
* @type {boolean}
|
||||
*/
|
||||
DomHelper.CAN_WAIT_STYLESHEET = !!window['FontFace'];
|
||||
|
||||
/**
|
||||
* Creates an element.
|
||||
* @param {string} elem The element type.
|
||||
* @param {Object=} opt_attr A hash of attribute key/value pairs.
|
||||
* @param {string=} opt_innerHtml Contents of the element.
|
||||
* @return {Element} the new element.
|
||||
*/
|
||||
DomHelper.prototype.createElement = function(elem, opt_attr,
|
||||
opt_innerHtml) {
|
||||
var domElement = this.document_.createElement(elem);
|
||||
|
||||
if (opt_attr) {
|
||||
for (var attr in opt_attr) {
|
||||
// protect against native prototype augmentations
|
||||
if (opt_attr.hasOwnProperty(attr)) {
|
||||
if (attr == "style") {
|
||||
this.setStyle(domElement, opt_attr[attr]);
|
||||
} else {
|
||||
domElement.setAttribute(attr, opt_attr[attr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opt_innerHtml) {
|
||||
domElement.appendChild(this.document_.createTextNode(opt_innerHtml));
|
||||
}
|
||||
return domElement;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts an element into the document. This is intended for unambiguous
|
||||
* elements such as html, body, head.
|
||||
* @param {string} tagName The element name.
|
||||
* @param {Element} e The element to append.
|
||||
* @return {boolean} True if the element was inserted.
|
||||
*/
|
||||
DomHelper.prototype.insertInto = function(tagName, e) {
|
||||
var t = this.document_.getElementsByTagName(tagName)[0];
|
||||
|
||||
if (!t) { // opera allows documents without a head
|
||||
t = document.documentElement;
|
||||
}
|
||||
|
||||
// This is safer than appendChild in IE. appendChild causes random
|
||||
// JS errors in IE. Sometimes errors in other JS exectution, sometimes
|
||||
// complete 'This page cannot be displayed' errors. For our purposes,
|
||||
// it's equivalent because we don't need to insert at any specific
|
||||
// location.
|
||||
t.insertBefore(e, t.lastChild);
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls a function when the body tag exists.
|
||||
* @param {function()} callback The function to call.
|
||||
*/
|
||||
DomHelper.prototype.whenBodyExists = function(callback) {
|
||||
var that = this;
|
||||
|
||||
if (that.document_.body) {
|
||||
callback();
|
||||
} else {
|
||||
if (that.document_.addEventListener) {
|
||||
that.document_.addEventListener('DOMContentLoaded', callback);
|
||||
} else {
|
||||
that.document_.attachEvent('onreadystatechange', function () {
|
||||
if (that.document_.readyState == 'interactive' || that.document_.readyState == 'complete') {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes an element from the DOM.
|
||||
* @param {Element} node The element to remove.
|
||||
* @return {boolean} True if the element was removed.
|
||||
*/
|
||||
DomHelper.prototype.removeElement = function(node) {
|
||||
if (node.parentNode) {
|
||||
node.parentNode.removeChild(node);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use updateClassName().
|
||||
*
|
||||
* Appends a name to an element's class attribute.
|
||||
* @param {Element} e The element.
|
||||
* @param {string} name The class name to add.
|
||||
*/
|
||||
DomHelper.prototype.appendClassName = function(e, name) {
|
||||
this.updateClassName(e, [name]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use updateClassName().
|
||||
*
|
||||
* Removes a name to an element's class attribute.
|
||||
* @param {Element} e The element.
|
||||
* @param {string} name The class name to remove.
|
||||
*/
|
||||
DomHelper.prototype.removeClassName = function(e, name) {
|
||||
this.updateClassName(e, null, [name]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates an element's class attribute in a single change. This
|
||||
* allows multiple updates in a single class name change so there
|
||||
* is no chance for a browser to relayout in between changes.
|
||||
*
|
||||
* @param {Element} e The element.
|
||||
* @param {Array.<string>=} opt_add List of class names to add.
|
||||
* @param {Array.<string>=} opt_remove List of class names to remove.
|
||||
*/
|
||||
DomHelper.prototype.updateClassName = function (e, opt_add, opt_remove) {
|
||||
var add = opt_add || [],
|
||||
remove = opt_remove || [];
|
||||
|
||||
var classes = e.className.split(/\s+/);
|
||||
|
||||
for (var i = 0; i < add.length; i += 1) {
|
||||
var found = false;
|
||||
|
||||
for (var j = 0; j < classes.length; j += 1) {
|
||||
if (add[i] === classes[j]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
classes.push(add[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var remainingClasses = [];
|
||||
|
||||
for (var i = 0; i < classes.length; i += 1) {
|
||||
var found = false;
|
||||
|
||||
for (var j = 0; j < remove.length; j += 1) {
|
||||
if (classes[i] === remove[j]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
remainingClasses.push(classes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
e.className = remainingClasses.join(' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/^\s+|\s+$/, '');
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if an element has a given class name and false otherwise.
|
||||
* @param {Element} e The element.
|
||||
* @param {string} name The class name to check for.
|
||||
* @return {boolean} Whether or not the element has this class name.
|
||||
*/
|
||||
DomHelper.prototype.hasClassName = function(e, name) {
|
||||
var classes = e.className.split(/\s+/);
|
||||
for (var i = 0, len = classes.length; i < len; i++) {
|
||||
if (classes[i] == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the style attribute on an element.
|
||||
* @param {Element} e The element.
|
||||
* @param {string} styleString The style string.
|
||||
*/
|
||||
DomHelper.prototype.setStyle = function(e, styleString) {
|
||||
e.style.cssText = styleString;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Window} The main window webfontloader.js is loaded in (for config).
|
||||
*/
|
||||
DomHelper.prototype.getMainWindow = function() {
|
||||
return this.mainWindow_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Window} The window that we're loading the font(s) into.
|
||||
*/
|
||||
DomHelper.prototype.getLoadWindow = function() {
|
||||
return this.loadWindow_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the hostname of the current document.
|
||||
* @return {string} hostname.
|
||||
*/
|
||||
DomHelper.prototype.getHostName = function() {
|
||||
return this.getLoadWindow().location.hostname || this.getMainWindow().location.hostname;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a style element.
|
||||
* @param {string} css Contents of the style element.
|
||||
* @return {Element} a DOM element.
|
||||
*/
|
||||
DomHelper.prototype.createStyle = function(css) {
|
||||
var e = this.createElement('style');
|
||||
|
||||
e.setAttribute('type', 'text/css');
|
||||
if (e.styleSheet) { // IE
|
||||
e.styleSheet.cssText = css;
|
||||
} else {
|
||||
e.appendChild(document.createTextNode(css));
|
||||
}
|
||||
return e;
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads an external stylesheet.
|
||||
*
|
||||
* @param {string} href the URL of the stylesheet
|
||||
* @param {function(Error)=} opt_callback Called when the stylesheet has loaded or failed to
|
||||
* load. Note that the callback is *NOT* guaranteed to be called in all browsers. The first
|
||||
* argument to the callback is an error object that is falsy when there are no errors and
|
||||
* truthy when there are.
|
||||
* @param {boolean=} opt_async True if the stylesheet should be loaded asynchronously. Defaults to false.
|
||||
* @return {Element} The link element
|
||||
*/
|
||||
DomHelper.prototype.loadStylesheet = function (href, opt_callback, opt_async) {
|
||||
var link = this.createElement('link', {
|
||||
'rel': 'stylesheet',
|
||||
'href': href,
|
||||
'media': (opt_async ? 'only x' : 'all')
|
||||
});
|
||||
|
||||
var sheets = this.document_.styleSheets,
|
||||
eventFired = false,
|
||||
asyncResolved = !opt_async,
|
||||
callbackArg = null,
|
||||
callback = opt_callback || null;
|
||||
|
||||
function mayInvokeCallback() {
|
||||
if (callback && eventFired && asyncResolved) {
|
||||
callback(callbackArg);
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (DomHelper.CAN_WAIT_STYLESHEET) {
|
||||
link.onload = function () {
|
||||
eventFired = true;
|
||||
mayInvokeCallback();
|
||||
};
|
||||
|
||||
link.onerror = function () {
|
||||
eventFired = true;
|
||||
callbackArg = new Error('Stylesheet failed to load');
|
||||
mayInvokeCallback();
|
||||
};
|
||||
} else {
|
||||
// Some callers expect opt_callback being called asynchronously.
|
||||
setTimeout(function () {
|
||||
eventFired = true;
|
||||
mayInvokeCallback();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function onStylesheetAvailable(callback) {
|
||||
for (var i = 0; i < sheets.length; i++) {
|
||||
if (sheets[i].href && sheets[i].href.indexOf(href) !== -1) {
|
||||
return callback();
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
onStylesheetAvailable(callback);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function onMediaAvailable(callback) {
|
||||
for (var i = 0; i < sheets.length; i++) {
|
||||
if (sheets[i].href && sheets[i].href.indexOf(href) !== -1 && sheets[i].media) {
|
||||
/**
|
||||
* @type {string|MediaList|null}
|
||||
*/
|
||||
var media = sheets[i].media;
|
||||
|
||||
if (media === "all" || (media.mediaText && media.mediaText === "all")) {
|
||||
return callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
onMediaAvailable(callback);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
this.insertInto('head', link);
|
||||
|
||||
if (opt_async) {
|
||||
onStylesheetAvailable(function () {
|
||||
link.media = "all";
|
||||
// The media type change doesn't take effect immediately on Chrome, so
|
||||
// we'll query the media attribute on the stylesheet until it changes
|
||||
// to "all".
|
||||
onMediaAvailable(function () {
|
||||
asyncResolved = true;
|
||||
mayInvokeCallback();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return link;
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads an external script file.
|
||||
* @param {string} src URL of the script.
|
||||
* @param {function(Error)=} opt_callback callback when the script has loaded. The first argument to
|
||||
* the callback is an error object that is falsy when there are no errors and truthy when there are.
|
||||
* @param {number=} opt_timeout The number of milliseconds after which the callback will be called
|
||||
* with a timeout error. Defaults to 5 seconds.
|
||||
* @return {Element} The script element
|
||||
*/
|
||||
DomHelper.prototype.loadScript = function(src, opt_callback, opt_timeout) {
|
||||
var head = this.document_.getElementsByTagName('head')[0];
|
||||
|
||||
if (head) {
|
||||
var script = this.createElement('script', {
|
||||
'src': src
|
||||
});
|
||||
var done = false;
|
||||
script.onload = script.onreadystatechange = function() {
|
||||
if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
|
||||
done = true;
|
||||
if (opt_callback) {
|
||||
opt_callback(null);
|
||||
}
|
||||
script.onload = script.onreadystatechange = null;
|
||||
// Avoid a bizarre issue with unclosed <base> tag in IE6 - http://blog.dotsmart.net/2008/04/
|
||||
if (script.parentNode.tagName == 'HEAD') head.removeChild(script);
|
||||
}
|
||||
};
|
||||
head.appendChild(script);
|
||||
|
||||
setTimeout(function () {
|
||||
if (!done) {
|
||||
done = true;
|
||||
if (opt_callback) {
|
||||
opt_callback(new Error('Script load timeout'));
|
||||
}
|
||||
}
|
||||
}, opt_timeout || 5000);
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
});
|
|
@ -0,0 +1,195 @@
|
|||
goog.provide('webfont.EventDispatcher');
|
||||
|
||||
goog.require('webfont.CssClassName');
|
||||
|
||||
/**
|
||||
* A class to dispatch events and manage the event class names on an html
|
||||
* element that represent the current state of fonts on the page. Active class
|
||||
* names always overwrite inactive class names of the same type, while loading
|
||||
* class names may be present whenever a font is loading (regardless of if an
|
||||
* associated active or inactive class name is also present).
|
||||
*
|
||||
* @param {webfont.DomHelper} domHelper
|
||||
* @param {Object} config
|
||||
* @constructor
|
||||
*/
|
||||
webfont.EventDispatcher = function(domHelper, config) {
|
||||
this.domHelper_ = domHelper;
|
||||
this.htmlElement_ = domHelper.getLoadWindow().document.documentElement;
|
||||
this.callbacks_ = config;
|
||||
this.namespace_ = webfont.EventDispatcher.DEFAULT_NAMESPACE;
|
||||
this.cssClassName_ = new webfont.CssClassName('-');
|
||||
this.dispatchEvents_ = config['events'] !== false;
|
||||
this.setClasses_ = config['classes'] !== false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
webfont.EventDispatcher.DEFAULT_NAMESPACE = 'wf';
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
webfont.EventDispatcher.LOADING = 'loading';
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
webfont.EventDispatcher.ACTIVE = 'active';
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
webfont.EventDispatcher.INACTIVE = 'inactive';
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
webfont.EventDispatcher.FONT = 'font';
|
||||
|
||||
goog.scope(function () {
|
||||
var EventDispatcher = webfont.EventDispatcher;
|
||||
|
||||
/**
|
||||
* Dispatch the loading event and append the loading class name.
|
||||
*/
|
||||
EventDispatcher.prototype.dispatchLoading = function() {
|
||||
if (this.setClasses_) {
|
||||
this.domHelper_.updateClassName(this.htmlElement_,
|
||||
[
|
||||
this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.LOADING)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
this.dispatch_(webfont.EventDispatcher.LOADING);
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatch the font loading event and append the font loading class name.
|
||||
* @param {webfont.Font} font
|
||||
*/
|
||||
EventDispatcher.prototype.dispatchFontLoading = function(font) {
|
||||
if (this.setClasses_) {
|
||||
this.domHelper_.updateClassName(this.htmlElement_,
|
||||
[
|
||||
this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.LOADING)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
this.dispatch_(webfont.EventDispatcher.FONT + webfont.EventDispatcher.LOADING, font);
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatch the font active event, remove the font loading class name, remove
|
||||
* the font inactive class name, and append the font active class name.
|
||||
* @param {webfont.Font} font
|
||||
*/
|
||||
EventDispatcher.prototype.dispatchFontActive = function(font) {
|
||||
if (this.setClasses_) {
|
||||
this.domHelper_.updateClassName(
|
||||
this.htmlElement_,
|
||||
[
|
||||
this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.ACTIVE)
|
||||
],
|
||||
[
|
||||
this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.LOADING),
|
||||
this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.INACTIVE)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
this.dispatch_(webfont.EventDispatcher.FONT + webfont.EventDispatcher.ACTIVE, font);
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatch the font inactive event, remove the font loading class name, and
|
||||
* append the font inactive class name (unless the font active class name is
|
||||
* already present).
|
||||
* @param {webfont.Font} font
|
||||
*/
|
||||
EventDispatcher.prototype.dispatchFontInactive = function(font) {
|
||||
if (this.setClasses_) {
|
||||
var hasFontActive = this.domHelper_.hasClassName(this.htmlElement_,
|
||||
this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.ACTIVE)
|
||||
),
|
||||
add = [],
|
||||
remove = [
|
||||
this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.LOADING)
|
||||
];
|
||||
|
||||
if (!hasFontActive) {
|
||||
add.push(this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.INACTIVE));
|
||||
}
|
||||
|
||||
this.domHelper_.updateClassName(this.htmlElement_, add, remove);
|
||||
}
|
||||
|
||||
this.dispatch_(webfont.EventDispatcher.FONT + webfont.EventDispatcher.INACTIVE, font);
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatch the inactive event, remove the loading class name, and append the
|
||||
* inactive class name (unless the active class name is already present).
|
||||
*/
|
||||
EventDispatcher.prototype.dispatchInactive = function() {
|
||||
if (this.setClasses_) {
|
||||
var hasActive = this.domHelper_.hasClassName(this.htmlElement_,
|
||||
this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.ACTIVE)
|
||||
),
|
||||
add = [],
|
||||
remove = [
|
||||
this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.LOADING)
|
||||
];
|
||||
|
||||
if (!hasActive) {
|
||||
add.push(this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.INACTIVE));
|
||||
}
|
||||
|
||||
this.domHelper_.updateClassName(this.htmlElement_, add, remove);
|
||||
}
|
||||
|
||||
this.dispatch_(webfont.EventDispatcher.INACTIVE);
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatch the active event, remove the loading class name, remove the inactive
|
||||
* class name, and append the active class name.
|
||||
*/
|
||||
EventDispatcher.prototype.dispatchActive = function() {
|
||||
if (this.setClasses_) {
|
||||
this.domHelper_.updateClassName(this.htmlElement_,
|
||||
[
|
||||
this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.ACTIVE)
|
||||
],
|
||||
[
|
||||
this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.LOADING),
|
||||
this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.INACTIVE)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
this.dispatch_(webfont.EventDispatcher.ACTIVE);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} event
|
||||
* @param {webfont.Font=} opt_font
|
||||
*/
|
||||
EventDispatcher.prototype.dispatch_ = function(event, opt_font) {
|
||||
if (this.dispatchEvents_ && this.callbacks_[event]) {
|
||||
if (opt_font) {
|
||||
this.callbacks_[event](opt_font.getName(), opt_font.getVariation());
|
||||
} else {
|
||||
this.callbacks_[event]();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,140 @@
|
|||
goog.provide('webfont.Font');
|
||||
|
||||
/**
|
||||
* This class is an abstraction for a single font or typeface.
|
||||
* It contains the font name and the variation (i.e. style
|
||||
* and weight.) A collection Font instances can represent a
|
||||
* font family.
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} name The font family name
|
||||
* @param {string=} opt_variation A font variation description
|
||||
*/
|
||||
webfont.Font = function (name, opt_variation) {
|
||||
this.name_ = name;
|
||||
this.weight_ = 4;
|
||||
this.style_ = 'n'
|
||||
|
||||
var variation = opt_variation || 'n4',
|
||||
match = variation.match(/^([nio])([1-9])$/i);
|
||||
|
||||
if (match) {
|
||||
this.style_ = match[1];
|
||||
this.weight_ = parseInt(match[2], 10);
|
||||
}
|
||||
};
|
||||
|
||||
goog.scope(function () {
|
||||
var Font = webfont.Font;
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
Font.prototype.getName = function () {
|
||||
return this.name_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
Font.prototype.getCssName = function () {
|
||||
return this.quote_(this.name_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a CSS string representation of the font that
|
||||
* can be used as the CSS font property shorthand.
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
Font.prototype.toCssString = function () {
|
||||
return this.getCssStyle() + ' ' + this.getCssWeight() + ' 300px ' + this.getCssName();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} name
|
||||
* @return {string}
|
||||
*/
|
||||
Font.prototype.quote_ = function (name) {
|
||||
var quoted = [];
|
||||
var split = name.split(/,\s*/);
|
||||
for (var i = 0; i < split.length; i++) {
|
||||
var part = split[i].replace(/['"]/g, '');
|
||||
if (part.indexOf(' ') == -1 && !(/^\d/.test(part))) {
|
||||
quoted.push(part);
|
||||
} else {
|
||||
quoted.push("'" + part + "'");
|
||||
}
|
||||
}
|
||||
return quoted.join(',');
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
Font.prototype.getVariation = function () {
|
||||
return this.style_ + this.weight_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
Font.prototype.getCssVariation = function () {
|
||||
return 'font-style:' + this.getCssStyle() + ';font-weight:' + this.getCssWeight() + ';';
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
Font.prototype.getCssWeight = function () {
|
||||
return this.weight_ + '00';
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
Font.prototype.getCssStyle = function () {
|
||||
var style = 'normal';
|
||||
|
||||
if (this.style_ === 'o') {
|
||||
style = 'oblique';
|
||||
} else if (this.style_ === 'i') {
|
||||
style = 'italic';
|
||||
}
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a CSS font declaration and returns a font
|
||||
* variation description.
|
||||
*
|
||||
* @param {string} css
|
||||
* @return {string}
|
||||
*/
|
||||
Font.parseCssVariation = function (css) {
|
||||
var weight = 4,
|
||||
style = 'n',
|
||||
m = null;
|
||||
|
||||
if (css) {
|
||||
m = css.match(/(normal|oblique|italic)/i);
|
||||
|
||||
if (m && m[1]) {
|
||||
style = m[1].substr(0, 1).toLowerCase();
|
||||
}
|
||||
|
||||
m = css.match(/([1-9]00|normal|bold)/i);
|
||||
|
||||
if (m && m[1]) {
|
||||
if (/bold/i.test(m[1])) {
|
||||
weight = 7;
|
||||
} else if (/[1-9]00/.test(m[1])) {
|
||||
weight = parseInt(m[1].substr(0, 1), 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
return style + weight;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
goog.provide('webfont.FontModule');
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
webfont.FontModule = function () {};
|
||||
|
||||
goog.scope(function () {
|
||||
var FontModule = webfont.FontModule;
|
||||
|
||||
/**
|
||||
* @param {function(Array.<webfont.Font>, webfont.FontTestStrings=, Object.<string, boolean>=)} onReady
|
||||
*/
|
||||
FontModule.prototype.load = function (onReady) {};
|
||||
});
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
goog.provide('webfont.FontModuleLoader');
|
||||
goog.provide('webfont.FontModuleFactory');
|
||||
|
||||
/** @typedef {function(Object, webfont.DomHelper): webfont.FontModule} */
|
||||
webfont.FontModuleFactory;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
webfont.FontModuleLoader = function() {
|
||||
/**
|
||||
* @type {Object.<string, webfont.FontModuleFactory>}
|
||||
*/
|
||||
this.modules_ = {};
|
||||
};
|
||||
|
||||
goog.scope(function () {
|
||||
var FontModuleLoader = webfont.FontModuleLoader;
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {webfont.FontModuleFactory} factory
|
||||
*/
|
||||
FontModuleLoader.prototype.addModuleFactory = function(name, factory) {
|
||||
this.modules_[name] = factory;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} configuration
|
||||
* @param {webfont.DomHelper} domHelper
|
||||
* @return {Array.<webfont.FontModule>}
|
||||
*/
|
||||
FontModuleLoader.prototype.getModules = function(configuration, domHelper) {
|
||||
var modules = [];
|
||||
|
||||
for (var key in configuration) {
|
||||
if (configuration.hasOwnProperty(key)) {
|
||||
var moduleFactory = this.modules_[key];
|
||||
|
||||
if (moduleFactory) {
|
||||
modules.push(moduleFactory(configuration[key], domHelper));
|
||||
}
|
||||
}
|
||||
}
|
||||
return modules;
|
||||
};
|
||||
});
|
|
@ -0,0 +1,60 @@
|
|||
goog.provide('webfont.FontRuler');
|
||||
|
||||
/**
|
||||
* An element that can be used to measure the metrics
|
||||
* of a given font and string.
|
||||
* @constructor
|
||||
* @param {webfont.DomHelper} domHelper
|
||||
* @param {string} fontTestString
|
||||
*/
|
||||
webfont.FontRuler = function (domHelper, fontTestString) {
|
||||
this.domHelper_ = domHelper;
|
||||
this.fontTestString_ = fontTestString;
|
||||
this.el_ = this.domHelper_.createElement('span', {
|
||||
"aria-hidden": "true"
|
||||
}, this.fontTestString_);
|
||||
};
|
||||
|
||||
goog.scope(function () {
|
||||
var FontRuler = webfont.FontRuler;
|
||||
|
||||
/**
|
||||
* @param {webfont.Font} font
|
||||
*/
|
||||
FontRuler.prototype.setFont = function(font) {
|
||||
this.domHelper_.setStyle(this.el_, this.computeStyleString_(font));
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts the ruler into the DOM.
|
||||
*/
|
||||
FontRuler.prototype.insert = function() {
|
||||
this.domHelper_.insertInto('body', this.el_);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {webfont.Font} font
|
||||
* @return {string}
|
||||
*/
|
||||
FontRuler.prototype.computeStyleString_ = function(font) {
|
||||
return "display:block;position:absolute;top:-9999px;left:-9999px;" +
|
||||
"font-size:300px;width:auto;height:auto;line-height:normal;margin:0;" +
|
||||
"padding:0;font-variant:normal;white-space:nowrap;font-family:" +
|
||||
font.getCssName() + ";" + font.getCssVariation();
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
FontRuler.prototype.getWidth = function() {
|
||||
return this.el_.offsetWidth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the ruler element from the DOM.
|
||||
*/
|
||||
FontRuler.prototype.remove = function() {
|
||||
this.domHelper_.removeElement(this.el_);
|
||||
};
|
||||
});
|
|
@ -0,0 +1,171 @@
|
|||
goog.provide('webfont.FontWatcher');
|
||||
|
||||
goog.require('webfont.FontWatchRunner');
|
||||
goog.require('webfont.NativeFontWatchRunner');
|
||||
|
||||
/**
|
||||
* @typedef {Object.<string, Array.<string>>}
|
||||
*/
|
||||
webfont.FontTestStrings;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {webfont.DomHelper} domHelper
|
||||
* @param {webfont.EventDispatcher} eventDispatcher
|
||||
* @param {number=} opt_timeout
|
||||
*/
|
||||
webfont.FontWatcher = function(domHelper, eventDispatcher, opt_timeout) {
|
||||
this.domHelper_ = domHelper;
|
||||
this.eventDispatcher_ = eventDispatcher;
|
||||
this.currentlyWatched_ = 0;
|
||||
this.last_ = false;
|
||||
this.success_ = false;
|
||||
this.timeout_ = opt_timeout;
|
||||
};
|
||||
|
||||
goog.scope(function () {
|
||||
var FontWatcher = webfont.FontWatcher,
|
||||
FontWatchRunner = webfont.FontWatchRunner,
|
||||
NativeFontWatchRunner = webfont.NativeFontWatchRunner;
|
||||
|
||||
/**
|
||||
* @type {null|boolean}
|
||||
*/
|
||||
FontWatcher.SHOULD_USE_NATIVE_LOADER = null;
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
FontWatcher.getUserAgent = function () {
|
||||
return window.navigator.userAgent;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
FontWatcher.getVendor = function () {
|
||||
return window.navigator.vendor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if this browser has support for
|
||||
* the CSS font loading API.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
FontWatcher.shouldUseNativeLoader = function () {
|
||||
if (FontWatcher.SHOULD_USE_NATIVE_LOADER === null) {
|
||||
if (!!window.FontFace) {
|
||||
var match = /Gecko.*Firefox\/(\d+)/.exec(FontWatcher.getUserAgent());
|
||||
var safari10Match = /OS X.*Version\/10\..*Safari/.exec(FontWatcher.getUserAgent()) && /Apple/.exec(FontWatcher.getVendor());
|
||||
|
||||
if (match) {
|
||||
FontWatcher.SHOULD_USE_NATIVE_LOADER = parseInt(match[1], 10) > 42;
|
||||
} else if (safari10Match) {
|
||||
FontWatcher.SHOULD_USE_NATIVE_LOADER = false;
|
||||
} else {
|
||||
FontWatcher.SHOULD_USE_NATIVE_LOADER = true;
|
||||
}
|
||||
} else {
|
||||
FontWatcher.SHOULD_USE_NATIVE_LOADER = false;
|
||||
}
|
||||
}
|
||||
return FontWatcher.SHOULD_USE_NATIVE_LOADER;
|
||||
};
|
||||
|
||||
/**
|
||||
* Watches a set of font families.
|
||||
* @param {Array.<webfont.Font>} fonts The fonts to watch.
|
||||
* @param {webfont.FontTestStrings} fontTestStrings The font test strings for
|
||||
* each family.
|
||||
* @param {Object.<String, boolean>} metricCompatibleFonts
|
||||
* @param {boolean} last True if this is the last set of fonts to watch.
|
||||
*/
|
||||
FontWatcher.prototype.watchFonts = function(fonts,
|
||||
fontTestStrings, metricCompatibleFonts, last) {
|
||||
var length = fonts.length,
|
||||
testStrings = fontTestStrings || {};
|
||||
|
||||
if (length === 0 && last) {
|
||||
this.eventDispatcher_.dispatchInactive();
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentlyWatched_ += fonts.length;
|
||||
|
||||
if (last) {
|
||||
this.last_ = last;
|
||||
}
|
||||
|
||||
var i, fontWatchRunners = [];
|
||||
for (i = 0; i < fonts.length; i++) {
|
||||
var font = fonts[i],
|
||||
testString = testStrings[font.getName()];
|
||||
|
||||
this.eventDispatcher_.dispatchFontLoading(font);
|
||||
|
||||
var fontWatchRunner = null;
|
||||
|
||||
if (FontWatcher.shouldUseNativeLoader()) {
|
||||
fontWatchRunner = new NativeFontWatchRunner(
|
||||
goog.bind(this.fontActive_, this),
|
||||
goog.bind(this.fontInactive_, this),
|
||||
this.domHelper_,
|
||||
font,
|
||||
this.timeout_,
|
||||
testString
|
||||
);
|
||||
} else {
|
||||
fontWatchRunner = new FontWatchRunner(
|
||||
goog.bind(this.fontActive_, this),
|
||||
goog.bind(this.fontInactive_, this),
|
||||
this.domHelper_,
|
||||
font,
|
||||
this.timeout_,
|
||||
metricCompatibleFonts,
|
||||
testString
|
||||
);
|
||||
}
|
||||
|
||||
fontWatchRunners.push(fontWatchRunner);
|
||||
}
|
||||
|
||||
for (i = 0; i < fontWatchRunners.length; i++) {
|
||||
fontWatchRunners[i].start();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by a FontWatchRunner when a font has been detected as active.
|
||||
* @param {webfont.Font} font
|
||||
* @private
|
||||
*/
|
||||
FontWatcher.prototype.fontActive_ = function(font) {
|
||||
this.eventDispatcher_.dispatchFontActive(font);
|
||||
this.success_ = true;
|
||||
this.decreaseCurrentlyWatched_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by a FontWatchRunner when a font has been detected as inactive.
|
||||
* @param {webfont.Font} font
|
||||
* @private
|
||||
*/
|
||||
FontWatcher.prototype.fontInactive_ = function(font) {
|
||||
this.eventDispatcher_.dispatchFontInactive(font);
|
||||
this.decreaseCurrentlyWatched_();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
FontWatcher.prototype.decreaseCurrentlyWatched_ = function() {
|
||||
if (--this.currentlyWatched_ == 0 && this.last_) {
|
||||
if (this.success_) {
|
||||
this.eventDispatcher_.dispatchActive();
|
||||
} else {
|
||||
this.eventDispatcher_.dispatchInactive();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,249 @@
|
|||
goog.provide('webfont.FontWatchRunner');
|
||||
|
||||
goog.require('webfont.Font');
|
||||
goog.require('webfont.FontRuler');
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {function(webfont.Font)} activeCallback
|
||||
* @param {function(webfont.Font)} inactiveCallback
|
||||
* @param {webfont.DomHelper} domHelper
|
||||
* @param {webfont.Font} font
|
||||
* @param {number=} opt_timeout
|
||||
* @param {Object.<string, boolean>=} opt_metricCompatibleFonts
|
||||
* @param {string=} opt_fontTestString
|
||||
*/
|
||||
webfont.FontWatchRunner = function(activeCallback, inactiveCallback, domHelper,
|
||||
font, opt_timeout, opt_metricCompatibleFonts, opt_fontTestString) {
|
||||
this.activeCallback_ = activeCallback;
|
||||
this.inactiveCallback_ = inactiveCallback;
|
||||
this.domHelper_ = domHelper;
|
||||
this.font_ = font;
|
||||
this.fontTestString_ = opt_fontTestString || webfont.FontWatchRunner.DEFAULT_TEST_STRING;
|
||||
this.lastResortWidths_ = {};
|
||||
this.timeout_ = opt_timeout || 3000;
|
||||
|
||||
this.metricCompatibleFonts_ = opt_metricCompatibleFonts || null;
|
||||
|
||||
this.fontRulerA_ = null;
|
||||
this.fontRulerB_ = null;
|
||||
this.lastResortRulerA_ = null;
|
||||
this.lastResortRulerB_ = null;
|
||||
|
||||
this.setupRulers_();
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
* @const
|
||||
*/
|
||||
webfont.FontWatchRunner.LastResortFonts = {
|
||||
SERIF: 'serif',
|
||||
SANS_SERIF: 'sans-serif'
|
||||
};
|
||||
|
||||
/**
|
||||
* Default test string. Characters are chosen so that their widths vary a lot
|
||||
* between the fonts in the default stacks. We want each fallback stack
|
||||
* to always start out at a different width than the other.
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
webfont.FontWatchRunner.DEFAULT_TEST_STRING = 'BESbswy';
|
||||
|
||||
goog.scope(function () {
|
||||
var FontWatchRunner = webfont.FontWatchRunner,
|
||||
Font = webfont.Font,
|
||||
FontRuler = webfont.FontRuler;
|
||||
|
||||
/**
|
||||
* @type {null|boolean}
|
||||
*/
|
||||
FontWatchRunner.HAS_WEBKIT_FALLBACK_BUG = null;
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
FontWatchRunner.getUserAgent = function () {
|
||||
return window.navigator.userAgent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if this browser is WebKit and it has the fallback bug
|
||||
* which is present in WebKit 536.11 and earlier.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
FontWatchRunner.hasWebKitFallbackBug = function () {
|
||||
if (FontWatchRunner.HAS_WEBKIT_FALLBACK_BUG === null) {
|
||||
var match = /AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(FontWatchRunner.getUserAgent());
|
||||
|
||||
FontWatchRunner.HAS_WEBKIT_FALLBACK_BUG = !!match &&
|
||||
(parseInt(match[1], 10) < 536 ||
|
||||
(parseInt(match[1], 10) === 536 &&
|
||||
parseInt(match[2], 10) <= 11));
|
||||
}
|
||||
return FontWatchRunner.HAS_WEBKIT_FALLBACK_BUG;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
FontWatchRunner.prototype.setupRulers_ = function() {
|
||||
this.fontRulerA_ = new FontRuler(this.domHelper_, this.fontTestString_);
|
||||
this.fontRulerB_ = new FontRuler(this.domHelper_, this.fontTestString_);
|
||||
this.lastResortRulerA_ = new FontRuler(this.domHelper_, this.fontTestString_);
|
||||
this.lastResortRulerB_ = new FontRuler(this.domHelper_, this.fontTestString_);
|
||||
|
||||
this.fontRulerA_.setFont(new Font(this.font_.getName() + ',' + FontWatchRunner.LastResortFonts.SERIF, this.font_.getVariation()));
|
||||
this.fontRulerB_.setFont(new Font(this.font_.getName() + ',' + FontWatchRunner.LastResortFonts.SANS_SERIF, this.font_.getVariation()));
|
||||
this.lastResortRulerA_.setFont(new Font(FontWatchRunner.LastResortFonts.SERIF, this.font_.getVariation()));
|
||||
this.lastResortRulerB_.setFont(new Font(FontWatchRunner.LastResortFonts.SANS_SERIF, this.font_.getVariation()));
|
||||
|
||||
this.fontRulerA_.insert();
|
||||
this.fontRulerB_.insert();
|
||||
this.lastResortRulerA_.insert();
|
||||
this.lastResortRulerB_.insert();
|
||||
};
|
||||
|
||||
FontWatchRunner.prototype.start = function() {
|
||||
this.lastResortWidths_[FontWatchRunner.LastResortFonts.SERIF] = this.lastResortRulerA_.getWidth();
|
||||
this.lastResortWidths_[FontWatchRunner.LastResortFonts.SANS_SERIF] = this.lastResortRulerB_.getWidth();
|
||||
|
||||
this.started_ = goog.now();
|
||||
|
||||
this.check_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the given width matches the generic font family width.
|
||||
*
|
||||
* @private
|
||||
* @param {number} width
|
||||
* @param {string} lastResortFont
|
||||
* @return {boolean}
|
||||
*/
|
||||
FontWatchRunner.prototype.widthMatches_ = function(width, lastResortFont) {
|
||||
return width === this.lastResortWidths_[lastResortFont];
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the given widths match any of the generic font family
|
||||
* widths.
|
||||
*
|
||||
* @private
|
||||
* @param {number} a
|
||||
* @param {number} b
|
||||
* @return {boolean}
|
||||
*/
|
||||
FontWatchRunner.prototype.widthsMatchLastResortWidths_ = function(a, b) {
|
||||
for (var font in FontWatchRunner.LastResortFonts) {
|
||||
if (FontWatchRunner.LastResortFonts.hasOwnProperty(font)) {
|
||||
if (this.widthMatches_(a, FontWatchRunner.LastResortFonts[font]) &&
|
||||
this.widthMatches_(b, FontWatchRunner.LastResortFonts[font])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Returns true if the loading has timed out.
|
||||
* @return {boolean}
|
||||
*/
|
||||
FontWatchRunner.prototype.hasTimedOut_ = function() {
|
||||
return goog.now() - this.started_ >= this.timeout_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if both fonts match the normal fallback fonts.
|
||||
*
|
||||
* @private
|
||||
* @param {number} a
|
||||
* @param {number} b
|
||||
* @return {boolean}
|
||||
*/
|
||||
FontWatchRunner.prototype.isFallbackFont_ = function (a, b) {
|
||||
return this.widthMatches_(a, FontWatchRunner.LastResortFonts.SERIF) &&
|
||||
this.widthMatches_(b, FontWatchRunner.LastResortFonts.SANS_SERIF);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the WebKit bug is present and both widths match a last resort font.
|
||||
*
|
||||
* @private
|
||||
* @param {number} a
|
||||
* @param {number} b
|
||||
* @return {boolean}
|
||||
*/
|
||||
FontWatchRunner.prototype.isLastResortFont_ = function (a, b) {
|
||||
return FontWatchRunner.hasWebKitFallbackBug() && this.widthsMatchLastResortWidths_(a, b);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the current font is metric compatible. Also returns true
|
||||
* if we do not have a list of metric compatible fonts.
|
||||
*
|
||||
* @private
|
||||
* @return {boolean}
|
||||
*/
|
||||
FontWatchRunner.prototype.isMetricCompatibleFont_ = function () {
|
||||
return this.metricCompatibleFonts_ === null || this.metricCompatibleFonts_.hasOwnProperty(this.font_.getName());
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks the width of the two spans against their original widths during each
|
||||
* async loop. If the width of one of the spans is different than the original
|
||||
* width, then we know that the font is rendering and finish with the active
|
||||
* callback. If we wait more than 5 seconds and nothing has changed, we finish
|
||||
* with the inactive callback.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
FontWatchRunner.prototype.check_ = function() {
|
||||
var widthA = this.fontRulerA_.getWidth();
|
||||
var widthB = this.fontRulerB_.getWidth();
|
||||
|
||||
if (this.isFallbackFont_(widthA, widthB) || this.isLastResortFont_(widthA, widthB)) {
|
||||
if (this.hasTimedOut_()) {
|
||||
if (this.isLastResortFont_(widthA, widthB) && this.isMetricCompatibleFont_()) {
|
||||
this.finish_(this.activeCallback_);
|
||||
} else {
|
||||
this.finish_(this.inactiveCallback_);
|
||||
}
|
||||
} else {
|
||||
this.asyncCheck_();
|
||||
}
|
||||
} else {
|
||||
this.finish_(this.activeCallback_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
FontWatchRunner.prototype.asyncCheck_ = function() {
|
||||
setTimeout(goog.bind(function () {
|
||||
this.check_();
|
||||
}, this), 50);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {function(webfont.Font)} callback
|
||||
*/
|
||||
FontWatchRunner.prototype.finish_ = function(callback) {
|
||||
// Remove elements and trigger callback (which adds active/inactive class) asynchronously to avoid reflow chain if
|
||||
// several fonts are finished loading right after each other
|
||||
setTimeout(goog.bind(function () {
|
||||
this.fontRulerA_.remove();
|
||||
this.fontRulerB_.remove();
|
||||
this.lastResortRulerA_.remove();
|
||||
this.lastResortRulerB_.remove();
|
||||
callback(this.font_);
|
||||
}, this), 0);
|
||||
};
|
||||
|
||||
});
|
|
@ -0,0 +1,97 @@
|
|||
goog.provide('webfont');
|
||||
|
||||
goog.require('webfont.WebFont');
|
||||
|
||||
goog.require('webfont.modules.Typekit');
|
||||
goog.require('webfont.modules.Fontdeck');
|
||||
goog.require('webfont.modules.Monotype');
|
||||
goog.require('webfont.modules.Custom');
|
||||
goog.require('webfont.modules.google.GoogleFontApi');
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
*/
|
||||
var INCLUDE_CUSTOM_MODULE = false;
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
*/
|
||||
var INCLUDE_FONTDECK_MODULE = false;
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
*/
|
||||
var INCLUDE_MONOTYPE_MODULE = false;
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
*/
|
||||
var INCLUDE_TYPEKIT_MODULE = false;
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
*/
|
||||
var INCLUDE_GOOGLE_MODULE = false;
|
||||
|
||||
/**
|
||||
* @define {string}
|
||||
*/
|
||||
var WEBFONT = 'WebFont';
|
||||
|
||||
/**
|
||||
* @define {string}
|
||||
*/
|
||||
var WEBFONT_CONFIG = 'WebFontConfig';
|
||||
|
||||
/**
|
||||
* @type {webfont.WebFont}
|
||||
*/
|
||||
var webFontLoader = new webfont.WebFont(window);
|
||||
|
||||
if (INCLUDE_CUSTOM_MODULE) {
|
||||
webFontLoader.addModule(webfont.modules.Custom.NAME, function (configuration, domHelper) {
|
||||
return new webfont.modules.Custom(domHelper, configuration);
|
||||
});
|
||||
}
|
||||
|
||||
if (INCLUDE_FONTDECK_MODULE) {
|
||||
webFontLoader.addModule(webfont.modules.Fontdeck.NAME, function (configuration, domHelper) {
|
||||
return new webfont.modules.Fontdeck(domHelper, configuration);
|
||||
});
|
||||
}
|
||||
|
||||
if (INCLUDE_MONOTYPE_MODULE) {
|
||||
webFontLoader.addModule(webfont.modules.Monotype.NAME, function (configuration, domHelper) {
|
||||
return new webfont.modules.Monotype(domHelper, configuration);
|
||||
});
|
||||
}
|
||||
|
||||
if (INCLUDE_TYPEKIT_MODULE) {
|
||||
webFontLoader.addModule(webfont.modules.Typekit.NAME, function (configuration, domHelper) {
|
||||
return new webfont.modules.Typekit(domHelper, configuration);
|
||||
});
|
||||
}
|
||||
|
||||
if (INCLUDE_GOOGLE_MODULE) {
|
||||
webFontLoader.addModule(webfont.modules.google.GoogleFontApi.NAME, function (configuration, domHelper) {
|
||||
return new webfont.modules.google.GoogleFontApi(domHelper, configuration);
|
||||
});
|
||||
}
|
||||
|
||||
var exports = {
|
||||
'load': goog.bind(webFontLoader.load, webFontLoader)
|
||||
};
|
||||
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define(function () {
|
||||
return exports;
|
||||
});
|
||||
} else if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports = exports;
|
||||
} else {
|
||||
window[WEBFONT] = exports;
|
||||
|
||||
if (window[WEBFONT_CONFIG]) {
|
||||
webFontLoader.load(window[WEBFONT_CONFIG]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
goog.provide('webfont.NativeFontWatchRunner');
|
||||
|
||||
goog.require('webfont.Font');
|
||||
|
||||
goog.scope(function () {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {function(webfont.Font)} activeCallback
|
||||
* @param {function(webfont.Font)} inactiveCallback
|
||||
* @param {webfont.DomHelper} domHelper
|
||||
* @param {webfont.Font} font
|
||||
* @param {number=} opt_timeout
|
||||
* @param {string=} opt_fontTestString
|
||||
*/
|
||||
webfont.NativeFontWatchRunner = function(activeCallback, inactiveCallback, domHelper, font, opt_timeout, opt_fontTestString) {
|
||||
this.activeCallback_ = activeCallback;
|
||||
this.inactiveCallback_ = inactiveCallback;
|
||||
this.font_ = font;
|
||||
this.domHelper_ = domHelper;
|
||||
this.timeout_ = opt_timeout || 3000;
|
||||
this.fontTestString_ = opt_fontTestString || undefined;
|
||||
};
|
||||
|
||||
var NativeFontWatchRunner = webfont.NativeFontWatchRunner;
|
||||
|
||||
NativeFontWatchRunner.prototype.start = function () {
|
||||
var doc = this.domHelper_.getLoadWindow().document,
|
||||
that = this;
|
||||
|
||||
var start = goog.now();
|
||||
|
||||
var loader = new Promise(function (resolve, reject) {
|
||||
var check = function () {
|
||||
var now = goog.now();
|
||||
|
||||
if (now - start >= that.timeout_) {
|
||||
reject();
|
||||
} else {
|
||||
doc.fonts.load(that.font_.toCssString(), that.fontTestString_).then(function (fonts) {
|
||||
if (fonts.length >= 1) {
|
||||
resolve();
|
||||
} else {
|
||||
setTimeout(check, 25);
|
||||
}
|
||||
}, function () {
|
||||
reject();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
check();
|
||||
});
|
||||
|
||||
var timeoutId = null,
|
||||
timer = new Promise(function (resolve, reject) {
|
||||
timeoutId = setTimeout(reject, that.timeout_);
|
||||
});
|
||||
|
||||
Promise.race([timer, loader]).then(function () {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
}
|
||||
that.activeCallback_(that.font_);
|
||||
}, function () {
|
||||
that.inactiveCallback_(that.font_);
|
||||
});
|
||||
};
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
goog.provide('webfont.StyleSheetWaiter');
|
||||
|
||||
/**
|
||||
* A utility class for handling callback from DomHelper.loadStylesheet().
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
webfont.StyleSheetWaiter = function() {
|
||||
/** @private @type {number} */
|
||||
this.waitingCount_ = 0;
|
||||
/** @private @type {Function} */
|
||||
this.onReady_ = null;
|
||||
};
|
||||
|
||||
goog.scope(function () {
|
||||
var StyleSheetWaiter = webfont.StyleSheetWaiter;
|
||||
|
||||
/**
|
||||
* @return {function(Error)}
|
||||
*/
|
||||
StyleSheetWaiter.prototype.startWaitingLoad = function() {
|
||||
var self = this;
|
||||
self.waitingCount_++;
|
||||
return function(error) {
|
||||
self.waitingCount_--;
|
||||
self.fireIfReady_();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Function} fn
|
||||
*/
|
||||
StyleSheetWaiter.prototype.waitWhileNeededThen = function(fn) {
|
||||
this.onReady_ = fn;
|
||||
this.fireIfReady_();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
StyleSheetWaiter.prototype.fireIfReady_ = function() {
|
||||
var isReady = 0 == this.waitingCount_;
|
||||
if (isReady && this.onReady_) {
|
||||
this.onReady_();
|
||||
this.onReady_ = null;
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,97 @@
|
|||
goog.provide('webfont.WebFont');
|
||||
|
||||
goog.require('webfont.DomHelper');
|
||||
goog.require('webfont.EventDispatcher');
|
||||
goog.require('webfont.FontWatcher');
|
||||
goog.require('webfont.FontModuleLoader');
|
||||
|
||||
/**
|
||||
* @param {Window} mainWindow The main application window containing
|
||||
* webfontloader.js.
|
||||
* @constructor
|
||||
*/
|
||||
webfont.WebFont = function(mainWindow) {
|
||||
this.mainWindow_ = mainWindow;
|
||||
this.fontModuleLoader_ = new webfont.FontModuleLoader();
|
||||
this.moduleLoading_ = 0;
|
||||
this.events_ = true;
|
||||
this.classes_ = true;
|
||||
};
|
||||
|
||||
goog.scope(function () {
|
||||
var WebFont = webfont.WebFont,
|
||||
DomHelper = webfont.DomHelper,
|
||||
EventDispatcher = webfont.EventDispatcher,
|
||||
FontWatcher = webfont.FontWatcher;
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {webfont.FontModuleFactory} factory
|
||||
*/
|
||||
WebFont.prototype.addModule = function(name, factory) {
|
||||
this.fontModuleLoader_.addModuleFactory(name, factory);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} configuration
|
||||
*/
|
||||
WebFont.prototype.load = function(configuration) {
|
||||
var context = configuration['context'] || this.mainWindow_;
|
||||
this.domHelper_ = new DomHelper(this.mainWindow_, context);
|
||||
|
||||
this.events_ = configuration['events'] !== false;
|
||||
this.classes_ = configuration['classes'] !== false;
|
||||
|
||||
var eventDispatcher = new EventDispatcher(
|
||||
this.domHelper_,
|
||||
configuration
|
||||
);
|
||||
|
||||
this.load_(eventDispatcher, configuration);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {webfont.EventDispatcher} eventDispatcher
|
||||
* @param {webfont.FontWatcher} fontWatcher
|
||||
* @param {Array.<webfont.Font>} fonts
|
||||
* @param {webfont.FontTestStrings=} opt_fontTestStrings
|
||||
* @param {Object.<string, boolean>=} opt_metricCompatibleFonts
|
||||
*/
|
||||
WebFont.prototype.onModuleReady_ = function(eventDispatcher, fontWatcher, fonts, opt_fontTestStrings, opt_metricCompatibleFonts) {
|
||||
var allModulesLoaded = --this.moduleLoading_ == 0;
|
||||
|
||||
if (this.classes_ || this.events_) {
|
||||
setTimeout(function () {
|
||||
fontWatcher.watchFonts(fonts, opt_fontTestStrings || null, opt_metricCompatibleFonts || null, allModulesLoaded);
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {webfont.EventDispatcher} eventDispatcher
|
||||
* @param {Object} configuration
|
||||
*/
|
||||
WebFont.prototype.load_ = function(eventDispatcher, configuration) {
|
||||
var modules = [],
|
||||
timeout = configuration['timeout'],
|
||||
self = this;
|
||||
|
||||
// Immediately dispatch the loading event before initializing the modules
|
||||
// so we know for sure that the loading event is synchronous.
|
||||
eventDispatcher.dispatchLoading();
|
||||
|
||||
modules = this.fontModuleLoader_.getModules(configuration, this.domHelper_);
|
||||
|
||||
var fontWatcher = new webfont.FontWatcher(this.domHelper_, eventDispatcher, timeout);
|
||||
|
||||
this.moduleLoading_ = modules.length;
|
||||
|
||||
for (var i = 0, len = modules.length; i < len; i++) {
|
||||
var module = modules[i];
|
||||
|
||||
module.load(function (fonts, opt_fontTestStrings, opt_metricCompatibleFonts) {
|
||||
self.onModuleReady_(eventDispatcher, fontWatcher, fonts, opt_fontTestStrings, opt_metricCompatibleFonts);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
core:
|
||||
- ../tools/compiler/base.js
|
||||
- core/domhelper.js
|
||||
- core/stylesheetwaiter.js
|
||||
- core/cssclassname.js
|
||||
- core/font.js
|
||||
- core/eventdispatcher.js
|
||||
- core/fontmodule.js
|
||||
- core/fontmoduleloader.js
|
||||
- core/fontruler.js
|
||||
- core/nativefontwatchrunner.js
|
||||
- core/fontwatchrunner.js
|
||||
- core/fontwatcher.js
|
||||
- core/webfont.js
|
||||
- core/initialize.js
|
||||
|
||||
|
||||
|
||||
google:
|
||||
- modules/google/fontapiurlbuilder.js
|
||||
- modules/google/fontapiparser.js
|
||||
- modules/google/googlefontapi.js
|
||||
|
||||
fontdeck:
|
||||
- modules/fontdeck.js
|
||||
|
||||
typekit:
|
||||
- modules/typekit.js
|
||||
|
||||
monotype:
|
||||
- modules/monotype.js
|
||||
|
||||
custom:
|
||||
- modules/custom.js
|
|
@ -0,0 +1,63 @@
|
|||
goog.provide('webfont.modules.Custom');
|
||||
|
||||
goog.require('webfont.Font');
|
||||
goog.require('webfont.StyleSheetWaiter');
|
||||
|
||||
/**
|
||||
*
|
||||
* WebFont.load({
|
||||
* custom: {
|
||||
* families: ['Font1', 'Font2'],
|
||||
* urls: [ 'https://moo', 'https://meuh' ] }
|
||||
* });
|
||||
*
|
||||
* @constructor
|
||||
* @implements {webfont.FontModule}
|
||||
*/
|
||||
webfont.modules.Custom = function(domHelper, configuration) {
|
||||
this.domHelper_ = domHelper;
|
||||
this.configuration_ = configuration;
|
||||
};
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
webfont.modules.Custom.NAME = 'custom';
|
||||
|
||||
goog.scope(function () {
|
||||
var Custom = webfont.modules.Custom,
|
||||
Font = webfont.Font,
|
||||
StyleSheetWaiter = webfont.StyleSheetWaiter;
|
||||
|
||||
Custom.prototype.load = function(onReady) {
|
||||
var i, len;
|
||||
var urls = this.configuration_['urls'] || [];
|
||||
var familiesConfiguration = this.configuration_['families'] || [];
|
||||
var fontTestStrings = this.configuration_['testStrings'] || {};
|
||||
var waiter = new StyleSheetWaiter();
|
||||
for (i = 0, len = urls.length; i < len; i++) {
|
||||
this.domHelper_.loadStylesheet(urls[i], waiter.startWaitingLoad());
|
||||
}
|
||||
|
||||
var fonts = [];
|
||||
|
||||
for (i = 0, len = familiesConfiguration.length; i < len; i++) {
|
||||
var components = familiesConfiguration[i].split(":");
|
||||
|
||||
if (components[1]) {
|
||||
var variations = components[1].split(",");
|
||||
|
||||
for (var j = 0; j < variations.length; j += 1) {
|
||||
fonts.push(new Font(components[0], variations[j]));
|
||||
}
|
||||
} else {
|
||||
fonts.push(new Font(components[0]));
|
||||
}
|
||||
}
|
||||
|
||||
waiter.waitWhileNeededThen(function() {
|
||||
onReady(fonts, fontTestStrings);
|
||||
});
|
||||
};
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
goog.provide('webfont.modules.Fontdeck');
|
||||
|
||||
goog.require('webfont.Font');
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {webfont.FontModule}
|
||||
*/
|
||||
webfont.modules.Fontdeck = function(domHelper, configuration) {
|
||||
this.domHelper_ = domHelper;
|
||||
this.configuration_ = configuration;
|
||||
this.fonts_ = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
webfont.modules.Fontdeck.NAME = 'fontdeck';
|
||||
webfont.modules.Fontdeck.HOOK = '__webfontfontdeckmodule__';
|
||||
webfont.modules.Fontdeck.API = 'https://f.fontdeck.com/s/css/js/';
|
||||
|
||||
goog.scope(function () {
|
||||
var Fontdeck = webfont.modules.Fontdeck,
|
||||
Font = webfont.Font,
|
||||
FontVariationDescription = webfont.FontVariationDescription;
|
||||
|
||||
Fontdeck.prototype.getScriptSrc = function(projectId) {
|
||||
// For empty iframes, fall back to main window's hostname.
|
||||
var hostname = this.domHelper_.getHostName();
|
||||
var api = this.configuration_['api'] || webfont.modules.Fontdeck.API;
|
||||
return api + hostname + '/' + projectId + '.js';
|
||||
};
|
||||
|
||||
Fontdeck.prototype.load = function(onReady) {
|
||||
var projectId = this.configuration_['id'];
|
||||
var loadWindow = this.domHelper_.getLoadWindow();
|
||||
var self = this;
|
||||
|
||||
if (projectId) {
|
||||
// Provide data to Fontdeck for processing.
|
||||
if (!loadWindow[webfont.modules.Fontdeck.HOOK]) {
|
||||
loadWindow[webfont.modules.Fontdeck.HOOK] = {};
|
||||
}
|
||||
|
||||
// Fontdeck will call this function to indicate support status
|
||||
// and what fonts are provided.
|
||||
loadWindow[webfont.modules.Fontdeck.HOOK][projectId] = function(fontdeckSupports, data) {
|
||||
for (var i = 0, j = data['fonts'].length; i<j; ++i) {
|
||||
var font = data['fonts'][i];
|
||||
self.fonts_.push(new Font(font['name'], Font.parseCssVariation('font-weight:' + font['weight'] + ';font-style:' + font['style'])));
|
||||
}
|
||||
onReady(self.fonts_);
|
||||
};
|
||||
|
||||
// Call the Fontdeck API.
|
||||
this.domHelper_.loadScript(this.getScriptSrc(projectId), function (err) {
|
||||
if (err) {
|
||||
onReady([]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
onReady([]);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,181 @@
|
|||
goog.provide('webfont.modules.google.FontApiParser');
|
||||
|
||||
goog.require('webfont.Font');
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
webfont.modules.google.FontApiParser = function(fontFamilies) {
|
||||
this.fontFamilies_ = fontFamilies;
|
||||
this.parsedFonts_ = [];
|
||||
this.fontTestStrings_ = {};
|
||||
};
|
||||
|
||||
webfont.modules.google.FontApiParser.INT_FONTS = {
|
||||
'latin': webfont.FontWatchRunner.DEFAULT_TEST_STRING,
|
||||
'latin-ext': '\u00E7\u00F6\u00FC\u011F\u015F',
|
||||
'cyrillic': '\u0439\u044f\u0416',
|
||||
'greek': '\u03b1\u03b2\u03a3',
|
||||
'khmer': '\u1780\u1781\u1782',
|
||||
'Hanuman': '\u1780\u1781\u1782' // For backward compatibility
|
||||
};
|
||||
|
||||
webfont.modules.google.FontApiParser.WEIGHTS = {
|
||||
'thin': '1',
|
||||
'extralight': '2',
|
||||
'extra-light': '2',
|
||||
'ultralight': '2',
|
||||
'ultra-light': '2',
|
||||
'light': '3',
|
||||
'regular': '4',
|
||||
'book': '4',
|
||||
'medium': '5',
|
||||
'semi-bold': '6',
|
||||
'semibold': '6',
|
||||
'demi-bold': '6',
|
||||
'demibold': '6',
|
||||
'bold': '7',
|
||||
'extra-bold': '8',
|
||||
'extrabold': '8',
|
||||
'ultra-bold': '8',
|
||||
'ultrabold': '8',
|
||||
'black': '9',
|
||||
'heavy': '9',
|
||||
'l': '3',
|
||||
'r': '4',
|
||||
'b': '7'
|
||||
};
|
||||
|
||||
webfont.modules.google.FontApiParser.STYLES = {
|
||||
'i': 'i',
|
||||
'italic': 'i',
|
||||
'n': 'n',
|
||||
'normal': 'n'
|
||||
};
|
||||
|
||||
webfont.modules.google.FontApiParser.VARIATION_MATCH =
|
||||
new RegExp("^(thin|(?:(?:extra|ultra)-?)?light|regular|book|medium|" +
|
||||
"(?:(?:semi|demi|extra|ultra)-?)?bold|black|heavy|l|r|b|[1-9]00)?(n|i" +
|
||||
"|normal|italic)?$");
|
||||
|
||||
goog.scope(function () {
|
||||
var FontApiParser = webfont.modules.google.FontApiParser,
|
||||
Font = webfont.Font;
|
||||
|
||||
FontApiParser.prototype.parse = function() {
|
||||
var length = this.fontFamilies_.length;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var elements = this.fontFamilies_[i].split(":");
|
||||
var fontFamily = elements[0].replace(/\+/g, " ");
|
||||
var variations = ['n4'];
|
||||
|
||||
if (elements.length >= 2) {
|
||||
var fvds = this.parseVariations_(elements[1]);
|
||||
|
||||
if (fvds.length > 0) {
|
||||
variations = fvds;
|
||||
}
|
||||
if (elements.length == 3) {
|
||||
var subsets = this.parseSubsets_(elements[2]);
|
||||
if (subsets.length > 0) {
|
||||
var fontTestString = FontApiParser.INT_FONTS[subsets[0]];
|
||||
|
||||
if (fontTestString) {
|
||||
this.fontTestStrings_[fontFamily] = fontTestString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For backward compatibility
|
||||
if (!this.fontTestStrings_[fontFamily]) {
|
||||
var hanumanTestString = FontApiParser.INT_FONTS[fontFamily];
|
||||
if (hanumanTestString) {
|
||||
this.fontTestStrings_[fontFamily] = hanumanTestString;
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < variations.length; j += 1) {
|
||||
this.parsedFonts_.push(new Font(fontFamily, variations[j]));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FontApiParser.prototype.generateFontVariationDescription_ = function(variation) {
|
||||
if (!variation.match(/^[\w-]+$/)) {
|
||||
return '';
|
||||
}
|
||||
var normalizedVariation = variation.toLowerCase();
|
||||
var groups = FontApiParser.VARIATION_MATCH.exec(normalizedVariation);
|
||||
if (groups == null) {
|
||||
return '';
|
||||
}
|
||||
var styleMatch = this.normalizeStyle_(groups[2]);
|
||||
var weightMatch = this.normalizeWeight_(groups[1]);
|
||||
return [styleMatch, weightMatch].join('');
|
||||
};
|
||||
|
||||
|
||||
FontApiParser.prototype.normalizeStyle_ = function(parsedStyle) {
|
||||
if (parsedStyle == null || parsedStyle == '') {
|
||||
return 'n';
|
||||
}
|
||||
return FontApiParser.STYLES[parsedStyle];
|
||||
};
|
||||
|
||||
|
||||
FontApiParser.prototype.normalizeWeight_ = function(parsedWeight) {
|
||||
if (parsedWeight == null || parsedWeight == '') {
|
||||
return '4';
|
||||
}
|
||||
var weight = FontApiParser.WEIGHTS[parsedWeight];
|
||||
if (weight) {
|
||||
return weight;
|
||||
}
|
||||
if (isNaN(parsedWeight)) {
|
||||
return '4';
|
||||
}
|
||||
return parsedWeight.substr(0, 1);
|
||||
};
|
||||
|
||||
|
||||
FontApiParser.prototype.parseVariations_ = function(variations) {
|
||||
var finalVariations = [];
|
||||
|
||||
if (!variations) {
|
||||
return finalVariations;
|
||||
}
|
||||
var providedVariations = variations.split(",");
|
||||
var length = providedVariations.length;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var variation = providedVariations[i];
|
||||
var fvd = this.generateFontVariationDescription_(variation);
|
||||
|
||||
if (fvd) {
|
||||
finalVariations.push(fvd);
|
||||
}
|
||||
}
|
||||
return finalVariations;
|
||||
};
|
||||
|
||||
|
||||
FontApiParser.prototype.parseSubsets_ = function(subsets) {
|
||||
var finalSubsets = [];
|
||||
|
||||
if (!subsets) {
|
||||
return finalSubsets;
|
||||
}
|
||||
return subsets.split(",");
|
||||
};
|
||||
|
||||
|
||||
FontApiParser.prototype.getFonts = function() {
|
||||
return this.parsedFonts_;
|
||||
};
|
||||
|
||||
FontApiParser.prototype.getFontTestStrings = function() {
|
||||
return this.fontTestStrings_;
|
||||
};
|
||||
});
|
|
@ -0,0 +1,77 @@
|
|||
goog.provide('webfont.modules.google.FontApiUrlBuilder');
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
webfont.modules.google.FontApiUrlBuilder = function(apiUrl, text) {
|
||||
if (apiUrl) {
|
||||
this.apiUrl_ = apiUrl;
|
||||
} else {
|
||||
this.apiUrl_ = webfont.modules.google.FontApiUrlBuilder.DEFAULT_API_URL;
|
||||
}
|
||||
this.fontFamilies_ = [];
|
||||
this.subsets_ = [];
|
||||
this.text_ = text || '';
|
||||
};
|
||||
|
||||
|
||||
webfont.modules.google.FontApiUrlBuilder.DEFAULT_API_URL = 'https://fonts.googleapis.com/css';
|
||||
|
||||
goog.scope(function () {
|
||||
var FontApiUrlBuilder = webfont.modules.google.FontApiUrlBuilder;
|
||||
|
||||
FontApiUrlBuilder.prototype.setFontFamilies = function(fontFamilies) {
|
||||
this.parseFontFamilies_(fontFamilies);
|
||||
};
|
||||
|
||||
|
||||
FontApiUrlBuilder.prototype.parseFontFamilies_ =
|
||||
function(fontFamilies) {
|
||||
var length = fontFamilies.length;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var elements = fontFamilies[i].split(':');
|
||||
|
||||
if (elements.length == 3) {
|
||||
this.subsets_.push(elements.pop());
|
||||
}
|
||||
var joinCharacter = '';
|
||||
if (elements.length == 2 && elements[1] != ''){
|
||||
joinCharacter = ':';
|
||||
}
|
||||
this.fontFamilies_.push(elements.join(joinCharacter));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FontApiUrlBuilder.prototype.webSafe = function(string) {
|
||||
return string.replace(/ /g, '+');
|
||||
};
|
||||
|
||||
|
||||
FontApiUrlBuilder.prototype.build = function() {
|
||||
if (this.fontFamilies_.length == 0) {
|
||||
throw new Error('No fonts to load!');
|
||||
}
|
||||
if (this.apiUrl_.indexOf("kit=") != -1) {
|
||||
return this.apiUrl_;
|
||||
}
|
||||
var length = this.fontFamilies_.length;
|
||||
var sb = [];
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
sb.push(this.webSafe(this.fontFamilies_[i]));
|
||||
}
|
||||
var url = this.apiUrl_ + '?family=' + sb.join('%7C'); // '|' escaped.
|
||||
|
||||
if (this.subsets_.length > 0) {
|
||||
url += '&subset=' + this.subsets_.join(',');
|
||||
}
|
||||
|
||||
if (this.text_.length > 0) {
|
||||
url += '&text=' + encodeURIComponent(this.text_);
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
});
|
|
@ -0,0 +1,54 @@
|
|||
goog.provide('webfont.modules.google.GoogleFontApi');
|
||||
|
||||
goog.require('webfont.modules.google.FontApiUrlBuilder');
|
||||
goog.require('webfont.modules.google.FontApiParser');
|
||||
goog.require('webfont.FontWatchRunner');
|
||||
goog.require('webfont.StyleSheetWaiter');
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {webfont.FontModule}
|
||||
*/
|
||||
webfont.modules.google.GoogleFontApi = function(domHelper, configuration) {
|
||||
this.domHelper_ = domHelper;
|
||||
this.configuration_ = configuration;
|
||||
};
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
webfont.modules.google.GoogleFontApi.NAME = 'google';
|
||||
|
||||
goog.scope(function () {
|
||||
var GoogleFontApi = webfont.modules.google.GoogleFontApi,
|
||||
FontWatchRunner = webfont.FontWatchRunner,
|
||||
StyleSheetWaiter = webfont.StyleSheetWaiter,
|
||||
FontApiUrlBuilder = webfont.modules.google.FontApiUrlBuilder,
|
||||
FontApiParser = webfont.modules.google.FontApiParser;
|
||||
|
||||
GoogleFontApi.METRICS_COMPATIBLE_FONTS = {
|
||||
"Arimo": true,
|
||||
"Cousine": true,
|
||||
"Tinos": true
|
||||
};
|
||||
|
||||
GoogleFontApi.prototype.load = function(onReady) {
|
||||
var waiter = new StyleSheetWaiter();
|
||||
var domHelper = this.domHelper_;
|
||||
var fontApiUrlBuilder = new FontApiUrlBuilder(
|
||||
this.configuration_['api'],
|
||||
this.configuration_['text']
|
||||
);
|
||||
var fontFamilies = this.configuration_['families'];
|
||||
fontApiUrlBuilder.setFontFamilies(fontFamilies);
|
||||
|
||||
var fontApiParser = new FontApiParser(fontFamilies);
|
||||
fontApiParser.parse();
|
||||
|
||||
domHelper.loadStylesheet(fontApiUrlBuilder.build(), waiter.startWaitingLoad());
|
||||
waiter.waitWhileNeededThen(function() {
|
||||
onReady(fontApiParser.getFonts(), fontApiParser.getFontTestStrings(), GoogleFontApi.METRICS_COMPATIBLE_FONTS);
|
||||
});
|
||||
};
|
||||
});
|
|
@ -0,0 +1,110 @@
|
|||
goog.provide('webfont.modules.Monotype');
|
||||
|
||||
goog.require('webfont.Font');
|
||||
|
||||
/**
|
||||
webfont.load({
|
||||
monotype: {
|
||||
projectId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'//this is your Fonts.com Web fonts projectId
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {webfont.FontModule}
|
||||
*/
|
||||
webfont.modules.Monotype = function(domHelper, configuration) {
|
||||
this.domHelper_ = domHelper;
|
||||
this.configuration_ = configuration;
|
||||
};
|
||||
|
||||
/**
|
||||
* name of the module through which external API is supposed to call the MonotypeFontAPI.
|
||||
*
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
webfont.modules.Monotype.NAME = 'monotype';
|
||||
|
||||
/**
|
||||
* __mti_fntLst is the name of function that exposes Monotype's font list.
|
||||
* @const
|
||||
*/
|
||||
webfont.modules.Monotype.HOOK = '__mti_fntLst';
|
||||
|
||||
/**
|
||||
* __MonotypeAPIScript__ is the id of script added by google API. Currently 'fonts.com' supports only one script in a page.
|
||||
* This may require change in future if 'fonts.com' begins supporting multiple scripts per page.
|
||||
* @const
|
||||
*/
|
||||
webfont.modules.Monotype.SCRIPTID = '__MonotypeAPIScript__';
|
||||
|
||||
/**
|
||||
* __MonotypeConfiguration__ is function exposed to fonts.com. fonts.com will use this function to get webfontloader configuration
|
||||
* @const
|
||||
*/
|
||||
webfont.modules.Monotype.CONFIGURATION = '__MonotypeConfiguration__';
|
||||
|
||||
goog.scope(function() {
|
||||
var Monotype = webfont.modules.Monotype,
|
||||
Font = webfont.Font;
|
||||
|
||||
|
||||
Monotype.prototype.getScriptSrc = function(projectId, version) {
|
||||
var api = (this.configuration_['api'] || 'https://fast.fonts.net/jsapi')
|
||||
return api + '/' + projectId + '.js' + (version ? '?v=' + version : '');
|
||||
};
|
||||
|
||||
Monotype.prototype.load = function(onReady) {
|
||||
var self = this;
|
||||
var projectId = self.configuration_['projectId'];
|
||||
var version = self.configuration_['version'];
|
||||
|
||||
|
||||
function checkAndLoadIfDownloaded() {
|
||||
if (loadWindow[Monotype.HOOK + projectId]) {
|
||||
var mti_fnts = loadWindow[Monotype.HOOK + projectId](),
|
||||
fonts = [],
|
||||
fntVariation;
|
||||
|
||||
if (mti_fnts) {
|
||||
for (var i = 0; i < mti_fnts.length; i++) {
|
||||
var fnt = mti_fnts[i]["fontfamily"];
|
||||
|
||||
//Check if font-style and font-weight is available
|
||||
if (mti_fnts[i]["fontStyle"] != undefined && mti_fnts[i]["fontWeight"] != undefined) {
|
||||
fntVariation = mti_fnts[i]["fontStyle"] + mti_fnts[i]["fontWeight"];
|
||||
fonts.push(new Font(fnt, fntVariation));
|
||||
} else {
|
||||
fonts.push(new Font(fnt));
|
||||
}
|
||||
}
|
||||
}
|
||||
onReady(fonts);
|
||||
} else {
|
||||
setTimeout(function() {
|
||||
checkAndLoadIfDownloaded();
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
if (projectId) {
|
||||
var loadWindow = self.domHelper_.getLoadWindow();
|
||||
|
||||
var script = this.domHelper_.loadScript(self.getScriptSrc(projectId, version), function(err) {
|
||||
if (err) {
|
||||
onReady([]);
|
||||
} else {
|
||||
loadWindow[Monotype.CONFIGURATION+ projectId] = function() {
|
||||
return self.configuration_;
|
||||
};
|
||||
|
||||
checkAndLoadIfDownloaded();
|
||||
}
|
||||
});
|
||||
script["id"] = Monotype.SCRIPTID + projectId;
|
||||
} else {
|
||||
onReady([]);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
goog.provide('webfont.modules.Typekit');
|
||||
|
||||
goog.require('webfont.Font');
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {webfont.FontModule}
|
||||
*/
|
||||
webfont.modules.Typekit = function(domHelper, configuration) {
|
||||
this.domHelper_ = domHelper;
|
||||
this.configuration_ = configuration;
|
||||
};
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
webfont.modules.Typekit.NAME = 'typekit';
|
||||
|
||||
goog.scope(function () {
|
||||
var Typekit = webfont.modules.Typekit,
|
||||
Font = webfont.Font;
|
||||
|
||||
Typekit.prototype.getScriptSrc = function(kitId) {
|
||||
var api = this.configuration_['api'] || 'https://use.typekit.net';
|
||||
return api + '/' + kitId + '.js';
|
||||
};
|
||||
|
||||
Typekit.prototype.load = function(onReady) {
|
||||
var kitId = this.configuration_['id'];
|
||||
var configuration = this.configuration_;
|
||||
var loadWindow = this.domHelper_.getLoadWindow();
|
||||
var that = this;
|
||||
|
||||
if (kitId) {
|
||||
// Load the Typekit script. Once it is done loading we grab its configuration
|
||||
// and use that to populate the fonts we should watch.
|
||||
this.domHelper_.loadScript(this.getScriptSrc(kitId), function (err) {
|
||||
if (err) {
|
||||
onReady([]);
|
||||
} else {
|
||||
if (loadWindow['Typekit'] && loadWindow['Typekit']['config'] && loadWindow['Typekit']['config']['fn']) {
|
||||
var fn = loadWindow['Typekit']['config']['fn'],
|
||||
fonts = [];
|
||||
|
||||
for (var i = 0; i < fn.length; i += 2) {
|
||||
var font = fn[i],
|
||||
variations = fn[i + 1];
|
||||
|
||||
for (var j = 0; j < variations.length; j++) {
|
||||
fonts.push(new Font(font, variations[j]));
|
||||
}
|
||||
}
|
||||
|
||||
// Kick off font loading but disable font events so
|
||||
// we don't duplicate font watching.
|
||||
try {
|
||||
loadWindow['Typekit']['load']({
|
||||
'events': false,
|
||||
'classes': false,
|
||||
'async': true
|
||||
});
|
||||
} catch (e) {}
|
||||
|
||||
onReady(fonts);
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
} else {
|
||||
onReady([]);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
/* Web Font Loader v1.6.28 - (c) Adobe Systems, Google. License: Apache 2.0 */(function(){function aa(a,b,c){return a.call.apply(a.bind,arguments)}function ba(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}function p(a,b,c){p=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?aa:ba;return p.apply(null,arguments)}var q=Date.now||function(){return+new Date};function ca(a,b){this.a=a;this.o=b||a;this.c=this.o.document}var da=!!window.FontFace;function t(a,b,c,d){b=a.c.createElement(b);if(c)for(var e in c)c.hasOwnProperty(e)&&("style"==e?b.style.cssText=c[e]:b.setAttribute(e,c[e]));d&&b.appendChild(a.c.createTextNode(d));return b}function u(a,b,c){a=a.c.getElementsByTagName(b)[0];a||(a=document.documentElement);a.insertBefore(c,a.lastChild)}function v(a){a.parentNode&&a.parentNode.removeChild(a)}
|
||||
function w(a,b,c){b=b||[];c=c||[];for(var d=a.className.split(/\s+/),e=0;e<b.length;e+=1){for(var f=!1,g=0;g<d.length;g+=1)if(b[e]===d[g]){f=!0;break}f||d.push(b[e])}b=[];for(e=0;e<d.length;e+=1){f=!1;for(g=0;g<c.length;g+=1)if(d[e]===c[g]){f=!0;break}f||b.push(d[e])}a.className=b.join(" ").replace(/\s+/g," ").replace(/^\s+|\s+$/,"")}function y(a,b){for(var c=a.className.split(/\s+/),d=0,e=c.length;d<e;d++)if(c[d]==b)return!0;return!1}
|
||||
function ea(a){return a.o.location.hostname||a.a.location.hostname}function z(a,b,c){function d(){m&&e&&f&&(m(g),m=null)}b=t(a,"link",{rel:"stylesheet",href:b,media:"all"});var e=!1,f=!0,g=null,m=c||null;da?(b.onload=function(){e=!0;d()},b.onerror=function(){e=!0;g=Error("Stylesheet failed to load");d()}):setTimeout(function(){e=!0;d()},0);u(a,"head",b)}
|
||||
function A(a,b,c,d){var e=a.c.getElementsByTagName("head")[0];if(e){var f=t(a,"script",{src:b}),g=!1;f.onload=f.onreadystatechange=function(){g||this.readyState&&"loaded"!=this.readyState&&"complete"!=this.readyState||(g=!0,c&&c(null),f.onload=f.onreadystatechange=null,"HEAD"==f.parentNode.tagName&&e.removeChild(f))};e.appendChild(f);setTimeout(function(){g||(g=!0,c&&c(Error("Script load timeout")))},d||5E3);return f}return null};function B(){this.a=0;this.c=null}function C(a){a.a++;return function(){a.a--;D(a)}}function E(a,b){a.c=b;D(a)}function D(a){0==a.a&&a.c&&(a.c(),a.c=null)};function F(a){this.a=a||"-"}F.prototype.c=function(a){for(var b=[],c=0;c<arguments.length;c++)b.push(arguments[c].replace(/[\W_]+/g,"").toLowerCase());return b.join(this.a)};function G(a,b){this.c=a;this.f=4;this.a="n";var c=(b||"n4").match(/^([nio])([1-9])$/i);c&&(this.a=c[1],this.f=parseInt(c[2],10))}function fa(a){return H(a)+" "+(a.f+"00")+" 300px "+I(a.c)}function I(a){var b=[];a=a.split(/,\s*/);for(var c=0;c<a.length;c++){var d=a[c].replace(/['"]/g,"");-1!=d.indexOf(" ")||/^\d/.test(d)?b.push("'"+d+"'"):b.push(d)}return b.join(",")}function J(a){return a.a+a.f}function H(a){var b="normal";"o"===a.a?b="oblique":"i"===a.a&&(b="italic");return b}
|
||||
function ga(a){var b=4,c="n",d=null;a&&((d=a.match(/(normal|oblique|italic)/i))&&d[1]&&(c=d[1].substr(0,1).toLowerCase()),(d=a.match(/([1-9]00|normal|bold)/i))&&d[1]&&(/bold/i.test(d[1])?b=7:/[1-9]00/.test(d[1])&&(b=parseInt(d[1].substr(0,1),10))));return c+b};function ha(a,b){this.c=a;this.f=a.o.document.documentElement;this.h=b;this.a=new F("-");this.j=!1!==b.events;this.g=!1!==b.classes}function ia(a){a.g&&w(a.f,[a.a.c("wf","loading")]);K(a,"loading")}function L(a){if(a.g){var b=y(a.f,a.a.c("wf","active")),c=[],d=[a.a.c("wf","loading")];b||c.push(a.a.c("wf","inactive"));w(a.f,c,d)}K(a,"inactive")}function K(a,b,c){if(a.j&&a.h[b])if(c)a.h[b](c.c,J(c));else a.h[b]()};function ja(){this.c={}}function ka(a,b,c){var d=[],e;for(e in b)if(b.hasOwnProperty(e)){var f=a.c[e];f&&d.push(f(b[e],c))}return d};function M(a,b){this.c=a;this.f=b;this.a=t(this.c,"span",{"aria-hidden":"true"},this.f)}function N(a){u(a.c,"body",a.a)}function O(a){return"display:block;position:absolute;top:-9999px;left:-9999px;font-size:300px;width:auto;height:auto;line-height:normal;margin:0;padding:0;font-variant:normal;white-space:nowrap;font-family:"+I(a.c)+";"+("font-style:"+H(a)+";font-weight:"+(a.f+"00")+";")};function P(a,b,c,d,e,f){this.g=a;this.j=b;this.a=d;this.c=c;this.f=e||3E3;this.h=f||void 0}P.prototype.start=function(){var a=this.c.o.document,b=this,c=q(),d=new Promise(function(d,e){function f(){q()-c>=b.f?e():a.fonts.load(fa(b.a),b.h).then(function(a){1<=a.length?d():setTimeout(f,25)},function(){e()})}f()}),e=null,f=new Promise(function(a,d){e=setTimeout(d,b.f)});Promise.race([f,d]).then(function(){e&&(clearTimeout(e),e=null);b.g(b.a)},function(){b.j(b.a)})};function Q(a,b,c,d,e,f,g){this.v=a;this.B=b;this.c=c;this.a=d;this.s=g||"BESbswy";this.f={};this.w=e||3E3;this.u=f||null;this.m=this.j=this.h=this.g=null;this.g=new M(this.c,this.s);this.h=new M(this.c,this.s);this.j=new M(this.c,this.s);this.m=new M(this.c,this.s);a=new G(this.a.c+",serif",J(this.a));a=O(a);this.g.a.style.cssText=a;a=new G(this.a.c+",sans-serif",J(this.a));a=O(a);this.h.a.style.cssText=a;a=new G("serif",J(this.a));a=O(a);this.j.a.style.cssText=a;a=new G("sans-serif",J(this.a));a=
|
||||
O(a);this.m.a.style.cssText=a;N(this.g);N(this.h);N(this.j);N(this.m)}var R={D:"serif",C:"sans-serif"},S=null;function T(){if(null===S){var a=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent);S=!!a&&(536>parseInt(a[1],10)||536===parseInt(a[1],10)&&11>=parseInt(a[2],10))}return S}Q.prototype.start=function(){this.f.serif=this.j.a.offsetWidth;this.f["sans-serif"]=this.m.a.offsetWidth;this.A=q();U(this)};
|
||||
function la(a,b,c){for(var d in R)if(R.hasOwnProperty(d)&&b===a.f[R[d]]&&c===a.f[R[d]])return!0;return!1}function U(a){var b=a.g.a.offsetWidth,c=a.h.a.offsetWidth,d;(d=b===a.f.serif&&c===a.f["sans-serif"])||(d=T()&&la(a,b,c));d?q()-a.A>=a.w?T()&&la(a,b,c)&&(null===a.u||a.u.hasOwnProperty(a.a.c))?V(a,a.v):V(a,a.B):ma(a):V(a,a.v)}function ma(a){setTimeout(p(function(){U(this)},a),50)}function V(a,b){setTimeout(p(function(){v(this.g.a);v(this.h.a);v(this.j.a);v(this.m.a);b(this.a)},a),0)};function W(a,b,c){this.c=a;this.a=b;this.f=0;this.m=this.j=!1;this.s=c}var X=null;W.prototype.g=function(a){var b=this.a;b.g&&w(b.f,[b.a.c("wf",a.c,J(a).toString(),"active")],[b.a.c("wf",a.c,J(a).toString(),"loading"),b.a.c("wf",a.c,J(a).toString(),"inactive")]);K(b,"fontactive",a);this.m=!0;na(this)};
|
||||
W.prototype.h=function(a){var b=this.a;if(b.g){var c=y(b.f,b.a.c("wf",a.c,J(a).toString(),"active")),d=[],e=[b.a.c("wf",a.c,J(a).toString(),"loading")];c||d.push(b.a.c("wf",a.c,J(a).toString(),"inactive"));w(b.f,d,e)}K(b,"fontinactive",a);na(this)};function na(a){0==--a.f&&a.j&&(a.m?(a=a.a,a.g&&w(a.f,[a.a.c("wf","active")],[a.a.c("wf","loading"),a.a.c("wf","inactive")]),K(a,"active")):L(a.a))};function oa(a){this.j=a;this.a=new ja;this.h=0;this.f=this.g=!0}oa.prototype.load=function(a){this.c=new ca(this.j,a.context||this.j);this.g=!1!==a.events;this.f=!1!==a.classes;pa(this,new ha(this.c,a),a)};
|
||||
function qa(a,b,c,d,e){var f=0==--a.h;(a.f||a.g)&&setTimeout(function(){var a=e||null,m=d||null||{};if(0===c.length&&f)L(b.a);else{b.f+=c.length;f&&(b.j=f);var h,l=[];for(h=0;h<c.length;h++){var k=c[h],n=m[k.c],r=b.a,x=k;r.g&&w(r.f,[r.a.c("wf",x.c,J(x).toString(),"loading")]);K(r,"fontloading",x);r=null;if(null===X)if(window.FontFace){var x=/Gecko.*Firefox\/(\d+)/.exec(window.navigator.userAgent),xa=/OS X.*Version\/10\..*Safari/.exec(window.navigator.userAgent)&&/Apple/.exec(window.navigator.vendor);
|
||||
X=x?42<parseInt(x[1],10):xa?!1:!0}else X=!1;X?r=new P(p(b.g,b),p(b.h,b),b.c,k,b.s,n):r=new Q(p(b.g,b),p(b.h,b),b.c,k,b.s,a,n);l.push(r)}for(h=0;h<l.length;h++)l[h].start()}},0)}function pa(a,b,c){var d=[],e=c.timeout;ia(b);var d=ka(a.a,c,a.c),f=new W(a.c,b,e);a.h=d.length;b=0;for(c=d.length;b<c;b++)d[b].load(function(b,d,c){qa(a,f,b,d,c)})};function ra(a,b){this.c=a;this.a=b}
|
||||
ra.prototype.load=function(a){function b(){if(f["__mti_fntLst"+d]){var c=f["__mti_fntLst"+d](),e=[],h;if(c)for(var l=0;l<c.length;l++){var k=c[l].fontfamily;void 0!=c[l].fontStyle&&void 0!=c[l].fontWeight?(h=c[l].fontStyle+c[l].fontWeight,e.push(new G(k,h))):e.push(new G(k))}a(e)}else setTimeout(function(){b()},50)}var c=this,d=c.a.projectId,e=c.a.version;if(d){var f=c.c.o;A(this.c,(c.a.api||"https://fast.fonts.net/jsapi")+"/"+d+".js"+(e?"?v="+e:""),function(e){e?a([]):(f["__MonotypeConfiguration__"+
|
||||
d]=function(){return c.a},b())}).id="__MonotypeAPIScript__"+d}else a([])};function sa(a,b){this.c=a;this.a=b}sa.prototype.load=function(a){var b,c,d=this.a.urls||[],e=this.a.families||[],f=this.a.testStrings||{},g=new B;b=0;for(c=d.length;b<c;b++)z(this.c,d[b],C(g));var m=[];b=0;for(c=e.length;b<c;b++)if(d=e[b].split(":"),d[1])for(var h=d[1].split(","),l=0;l<h.length;l+=1)m.push(new G(d[0],h[l]));else m.push(new G(d[0]));E(g,function(){a(m,f)})};function ta(a,b){a?this.c=a:this.c=ua;this.a=[];this.f=[];this.g=b||""}var ua="https://fonts.googleapis.com/css";function va(a,b){for(var c=b.length,d=0;d<c;d++){var e=b[d].split(":");3==e.length&&a.f.push(e.pop());var f="";2==e.length&&""!=e[1]&&(f=":");a.a.push(e.join(f))}}
|
||||
function wa(a){if(0==a.a.length)throw Error("No fonts to load!");if(-1!=a.c.indexOf("kit="))return a.c;for(var b=a.a.length,c=[],d=0;d<b;d++)c.push(a.a[d].replace(/ /g,"+"));b=a.c+"?family="+c.join("%7C");0<a.f.length&&(b+="&subset="+a.f.join(","));0<a.g.length&&(b+="&text="+encodeURIComponent(a.g));return b};function ya(a){this.f=a;this.a=[];this.c={}}
|
||||
var za={latin:"BESbswy","latin-ext":"\u00e7\u00f6\u00fc\u011f\u015f",cyrillic:"\u0439\u044f\u0416",greek:"\u03b1\u03b2\u03a3",khmer:"\u1780\u1781\u1782",Hanuman:"\u1780\u1781\u1782"},Aa={thin:"1",extralight:"2","extra-light":"2",ultralight:"2","ultra-light":"2",light:"3",regular:"4",book:"4",medium:"5","semi-bold":"6",semibold:"6","demi-bold":"6",demibold:"6",bold:"7","extra-bold":"8",extrabold:"8","ultra-bold":"8",ultrabold:"8",black:"9",heavy:"9",l:"3",r:"4",b:"7"},Ba={i:"i",italic:"i",n:"n",normal:"n"},
|
||||
Ca=/^(thin|(?:(?:extra|ultra)-?)?light|regular|book|medium|(?:(?:semi|demi|extra|ultra)-?)?bold|black|heavy|l|r|b|[1-9]00)?(n|i|normal|italic)?$/;
|
||||
function Da(a){for(var b=a.f.length,c=0;c<b;c++){var d=a.f[c].split(":"),e=d[0].replace(/\+/g," "),f=["n4"];if(2<=d.length){var g;var m=d[1];g=[];if(m)for(var m=m.split(","),h=m.length,l=0;l<h;l++){var k;k=m[l];if(k.match(/^[\w-]+$/)){var n=Ca.exec(k.toLowerCase());if(null==n)k="";else{k=n[2];k=null==k||""==k?"n":Ba[k];n=n[1];if(null==n||""==n)n="4";else var r=Aa[n],n=r?r:isNaN(n)?"4":n.substr(0,1);k=[k,n].join("")}}else k="";k&&g.push(k)}0<g.length&&(f=g);3==d.length&&(d=d[2],g=[],d=d?d.split(","):
|
||||
g,0<d.length&&(d=za[d[0]])&&(a.c[e]=d))}a.c[e]||(d=za[e])&&(a.c[e]=d);for(d=0;d<f.length;d+=1)a.a.push(new G(e,f[d]))}};function Ea(a,b){this.c=a;this.a=b}var Fa={Arimo:!0,Cousine:!0,Tinos:!0};Ea.prototype.load=function(a){var b=new B,c=this.c,d=new ta(this.a.api,this.a.text),e=this.a.families;va(d,e);var f=new ya(e);Da(f);z(c,wa(d),C(b));E(b,function(){a(f.a,f.c,Fa)})};function Ga(a,b){this.c=a;this.a=b}Ga.prototype.load=function(a){var b=this.a.id,c=this.c.o;b?A(this.c,(this.a.api||"https://use.typekit.net")+"/"+b+".js",function(b){if(b)a([]);else if(c.Typekit&&c.Typekit.config&&c.Typekit.config.fn){b=c.Typekit.config.fn;for(var e=[],f=0;f<b.length;f+=2)for(var g=b[f],m=b[f+1],h=0;h<m.length;h++)e.push(new G(g,m[h]));try{c.Typekit.load({events:!1,classes:!1,async:!0})}catch(l){}a(e)}},2E3):a([])};function Ha(a,b){this.c=a;this.f=b;this.a=[]}Ha.prototype.load=function(a){var b=this.f.id,c=this.c.o,d=this;b?(c.__webfontfontdeckmodule__||(c.__webfontfontdeckmodule__={}),c.__webfontfontdeckmodule__[b]=function(b,c){for(var g=0,m=c.fonts.length;g<m;++g){var h=c.fonts[g];d.a.push(new G(h.name,ga("font-weight:"+h.weight+";font-style:"+h.style)))}a(d.a)},A(this.c,(this.f.api||"https://f.fontdeck.com/s/css/js/")+ea(this.c)+"/"+b+".js",function(b){b&&a([])})):a([])};var Y=new oa(window);Y.a.c.custom=function(a,b){return new sa(b,a)};Y.a.c.fontdeck=function(a,b){return new Ha(b,a)};Y.a.c.monotype=function(a,b){return new ra(b,a)};Y.a.c.typekit=function(a,b){return new Ga(b,a)};Y.a.c.google=function(a,b){return new Ea(b,a)};var Z={load:p(Y.load,Y)};"function"===typeof define&&define.amd?define(function(){return Z}):"undefined"!==typeof module&&module.exports?module.exports=Z:(window.WebFont=Z,window.WebFontConfig&&Y.load(window.WebFontConfig));}());
|
|
@ -0,0 +1,483 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles downloading a font from the google-fonts API locally.
|
||||
* Solves privacy concerns with Google's CDN
|
||||
* and their sometimes less-than-transparent policies.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Core
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.28
|
||||
*/
|
||||
|
||||
// Do not allow directly accessing this file.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 'Direct script access denied.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* The Kirki_Fonts object.
|
||||
*
|
||||
* @since 3.0.28
|
||||
*/
|
||||
final class Kirki_Fonts_Google_Local {
|
||||
|
||||
/**
|
||||
* The name of the font-family
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @var string
|
||||
*/
|
||||
private $family;
|
||||
|
||||
/**
|
||||
* The system path where font-files are stored.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @var string
|
||||
*/
|
||||
private $folder_path;
|
||||
|
||||
/**
|
||||
* The URL where files for this font can be found.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @var string
|
||||
*/
|
||||
private $folder_url;
|
||||
|
||||
/**
|
||||
* The font-family array from the google-fonts API.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @var array
|
||||
*/
|
||||
private $font;
|
||||
|
||||
/**
|
||||
* An array of instances for this object.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @var array
|
||||
*/
|
||||
private static $instances = array();
|
||||
|
||||
/**
|
||||
* Create an instance of this object for a specific font-family.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @param string $family The font-family name.
|
||||
* @return Kirki_Fonts_Google_Local
|
||||
*/
|
||||
public static function init( $family ) {
|
||||
$key = sanitize_key( $family );
|
||||
if ( ! isset( self::$instances[ $key ] ) ) {
|
||||
self::$instances[ $key ] = new self( $family );
|
||||
}
|
||||
return self::$instances[ $key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @param string $family The font-family name.
|
||||
*/
|
||||
private function __construct( $family ) {
|
||||
$this->family = $family;
|
||||
$key = sanitize_key( $this->family );
|
||||
$this->folder_path = $this->get_root_path() . "/$key";
|
||||
$this->folder_url = $this->get_root_url() . "/$key";
|
||||
$this->files = $this->get_font_family();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the @font-face CSS.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @param array $variants The variants we want to get.
|
||||
* @return string
|
||||
*/
|
||||
public function get_css( $variants = array() ) {
|
||||
if ( ! $this->files ) {
|
||||
return;
|
||||
}
|
||||
$key = md5( wp_json_encode( $this->files ) );
|
||||
$cached = get_transient( $key );
|
||||
if ( $cached ) {
|
||||
return $cached;
|
||||
}
|
||||
$css = '';
|
||||
|
||||
// If $variants is empty then use all variants available.
|
||||
if ( empty( $variants ) ) {
|
||||
$variants = array_keys( $this->files );
|
||||
}
|
||||
|
||||
// Download files.
|
||||
$this->download_font_family( $variants );
|
||||
|
||||
// Create the @font-face CSS.
|
||||
foreach ( $variants as $variant ) {
|
||||
$css .= $this->get_variant_fontface_css( $variant );
|
||||
}
|
||||
set_transient( $key, $css, DAY_IN_SECONDS );
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the @font-face CSS for a specific variant.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @param string $variant The variant.
|
||||
* @return string
|
||||
*/
|
||||
public function get_variant_fontface_css( $variant ) {
|
||||
$font_face = "@font-face{font-family:'{$this->family}';";
|
||||
|
||||
// Get the font-style.
|
||||
$font_style = ( false !== strpos( $variant, 'italic' ) ) ? 'italic' : 'normal';
|
||||
$font_face .= "font-style:{$font_style};";
|
||||
|
||||
// Get the font-weight.
|
||||
$font_weight = '400';
|
||||
$font_weight = str_replace( 'italic', '', $variant );
|
||||
$font_weight = ( ! $font_weight || 'regular' === $font_weight ) ? '400' : $font_weight;
|
||||
$font_face .= "font-weight:{$font_weight};";
|
||||
|
||||
// Get the font-names.
|
||||
$font_name_0 = $this->get_local_font_name( $variant, false );
|
||||
$font_name_1 = $this->get_local_font_name( $variant, true );
|
||||
$font_face .= "src:local('{$font_name_0}'),";
|
||||
if ( $font_name_0 !== $font_name_1 ) {
|
||||
$font_face .= "local('{$font_name_1}'),";
|
||||
}
|
||||
|
||||
// Get the font-url.
|
||||
$font_url = $this->get_variant_local_url( $variant );
|
||||
$paths = $this->get_font_files_paths();
|
||||
if ( ! file_exists( $paths[ $variant ] ) ) {
|
||||
$font_url = $this->files[ $variant ];
|
||||
}
|
||||
|
||||
// Get the font-format.
|
||||
$font_format = ( strpos( $font_url, '.woff2' ) ) ? 'woff2' : 'truetype';
|
||||
$font_format = ( strpos( $font_url, '.woff' ) && ! strpos( $font_url, '.woff2' ) ) ? 'woff' : $font_format;
|
||||
$font_face .= "url({$font_url}) format('{$font_format}');}";
|
||||
|
||||
return $font_face;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local URL for a variant.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @param string $variant The variant.
|
||||
* @return string The URL.
|
||||
*/
|
||||
public function get_variant_local_url( $variant ) {
|
||||
$local_urls = $this->get_font_files_urls_local();
|
||||
|
||||
if ( empty( $local_urls ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return the specific variant if we can find it.
|
||||
if ( isset( $local_urls[ $variant ] ) ) {
|
||||
return $local_urls[ $variant ];
|
||||
}
|
||||
|
||||
// Return regular if the one we want could not be found.
|
||||
if ( isset( $local_urls['regular'] ) ) {
|
||||
return $local_urls['regular'];
|
||||
}
|
||||
|
||||
// Return the first available if all else failed.
|
||||
$vals = array_values( $local_urls );
|
||||
return $vals[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the font-family.
|
||||
* This is used by @font-face in case the user already has the font downloaded locally.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @param string $variant The variant.
|
||||
* @param bool $compact Whether we want the compact formatting or not.
|
||||
* @return string
|
||||
*/
|
||||
public function get_local_font_name( $variant, $compact = false ) {
|
||||
$variant_names = array(
|
||||
'100' => 'Thin',
|
||||
'100i' => 'Thin Italic',
|
||||
'100italic' => 'Thin Italic',
|
||||
'200' => 'Extra-Light',
|
||||
'200i' => 'Extra-Light Italic',
|
||||
'200italic' => 'Extra-Light Italic',
|
||||
'300' => 'Light',
|
||||
'300i' => 'Light Italic',
|
||||
'300italic' => 'Light Italic',
|
||||
'400' => 'Regular',
|
||||
'regular' => 'Regular',
|
||||
'400i' => 'Regular Italic',
|
||||
'italic' => 'Italic',
|
||||
'400italic' => 'Regular Italic',
|
||||
'500' => 'Medium',
|
||||
'500i' => 'Medium Italic',
|
||||
'500italic' => 'Medium Italic',
|
||||
'600' => 'Semi-Bold',
|
||||
'600i' => 'Semi-Bold Italic',
|
||||
'600italic' => 'Semi-Bold Italic',
|
||||
'700' => 'Bold',
|
||||
'700i' => 'Bold Italic',
|
||||
'700italic' => 'Bold Italic',
|
||||
'800' => 'Extra-Bold',
|
||||
'800i' => 'Extra-Bold Italic',
|
||||
'800italic' => 'Extra-Bold Italic',
|
||||
'900' => 'Black',
|
||||
'900i' => 'Black Italic',
|
||||
'900italic' => 'Black Italic',
|
||||
);
|
||||
|
||||
$variant = (string) $variant;
|
||||
if ( $compact ) {
|
||||
if ( isset( $variant_names[ $variant ] ) ) {
|
||||
return str_replace( array( ' ', '-' ), '', $this->family ) . '-' . str_replace( array( ' ', '-' ), '', $variant_names[ $variant ] );
|
||||
}
|
||||
return str_replace( array( ' ', '-' ), '', $this->family );
|
||||
}
|
||||
|
||||
if ( isset( $variant_names[ $variant ] ) ) {
|
||||
return $this->family . ' ' . $variant_names[ $variant ];
|
||||
}
|
||||
return $this->family;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of font-files.
|
||||
* Only contains the filenames.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @return array
|
||||
*/
|
||||
public function get_font_files() {
|
||||
$files = array();
|
||||
foreach ( $this->files as $key => $url ) {
|
||||
$files[ $key ] = $this->get_filename_from_url( $url );
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of local file URLs.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @return array
|
||||
*/
|
||||
public function get_font_files_urls_local() {
|
||||
$urls = array();
|
||||
$files = $this->get_font_files();
|
||||
foreach ( $files as $key => $file ) {
|
||||
$urls[ $key ] = $this->folder_url . '/' . $file;
|
||||
}
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of local file paths.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @return array
|
||||
*/
|
||||
public function get_font_files_paths() {
|
||||
$paths = array();
|
||||
$files = $this->get_font_files();
|
||||
foreach ( $files as $key => $file ) {
|
||||
$paths[ $key ] = $this->folder_path . '/' . $file;
|
||||
}
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a font-file and saves it locally.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @param string $url The URL of the file we want to get.
|
||||
* @return bool
|
||||
*/
|
||||
private function download_font_file( $url ) {
|
||||
$contents = $this->get_remote_url_contents( $url );
|
||||
$path = $this->folder_path . '/' . $this->get_filename_from_url( $url );
|
||||
|
||||
// If the folder doesn't exist, create it.
|
||||
if ( ! file_exists( $this->folder_path ) ) {
|
||||
wp_mkdir_p( $this->folder_path );
|
||||
}
|
||||
// If the file exists no reason to do anything.
|
||||
if ( file_exists( $path ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write file.
|
||||
return Kirki_Helper::init_filesystem()->put_contents( $path, $contents, FS_CHMOD_FILE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a font-family from the array of google-fonts.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @return array
|
||||
*/
|
||||
public function get_font_family() {
|
||||
|
||||
// Get the fonts array.
|
||||
$fonts = $this->get_fonts();
|
||||
if ( isset( $fonts[ $this->family ] ) ) {
|
||||
return $fonts[ $this->family ];
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the filename by breaking-down the URL parts.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @param string $url The URL.
|
||||
* @return string The filename.
|
||||
*/
|
||||
private function get_filename_from_url( $url ) {
|
||||
$url_parts = explode( '/', $url );
|
||||
$parts_count = count( $url_parts );
|
||||
if ( 1 < $parts_count ) {
|
||||
return $url_parts[ count( $url_parts ) - 1 ];
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the font defined in the google-fonts API.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @return array
|
||||
*/
|
||||
private function get_fonts() {
|
||||
ob_start();
|
||||
include wp_normalize_path( dirname( __FILE__ ) . '/webfont-files.json' );
|
||||
$json = ob_get_clean();
|
||||
return json_decode( $json, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root fonts folder path.
|
||||
* Other paths are built based on this.
|
||||
*
|
||||
* @since 1.5
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function get_root_path() {
|
||||
|
||||
// Get the upload directory for this site.
|
||||
$upload_dir = wp_upload_dir();
|
||||
$path = untrailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . '/webfonts';
|
||||
|
||||
// If the folder doesn't exist, create it.
|
||||
if ( ! file_exists( $path ) ) {
|
||||
wp_mkdir_p( $path );
|
||||
}
|
||||
|
||||
// Return the path.
|
||||
return apply_filters( 'kirki_googlefonts_root_path', $path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root folder url.
|
||||
* Other urls are built based on this.
|
||||
*
|
||||
* @since 1.5
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function get_root_url() {
|
||||
|
||||
// Get the upload directory for this site.
|
||||
$upload_dir = wp_upload_dir();
|
||||
|
||||
// The URL.
|
||||
$url = trailingslashit( $upload_dir['baseurl'] );
|
||||
|
||||
// Take care of domain mapping.
|
||||
// When using domain mapping we have to make sure that the URL to the file
|
||||
// does not include the original domain but instead the mapped domain.
|
||||
if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ) {
|
||||
if ( function_exists( 'domain_mapping_siteurl' ) && function_exists( 'get_original_url' ) ) {
|
||||
$mapped_domain = domain_mapping_siteurl( false );
|
||||
$original_domain = get_original_url( 'siteurl' );
|
||||
$url = str_replace( $original_domain, $mapped_domain, $url );
|
||||
}
|
||||
}
|
||||
$url = str_replace( array( 'https://', 'http://' ), '//', $url );
|
||||
return apply_filters( 'kirki_googlefonts_root_url', untrailingslashit( esc_url_raw( $url ) ) . '/webfonts' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Download font-family files.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
* @param array $variants An array of variants to download. Leave empty to download all.
|
||||
* @return void
|
||||
*/
|
||||
public function download_font_family( $variants = array() ) {
|
||||
if ( empty( $variants ) ) {
|
||||
$variants = array_keys( $this->files );
|
||||
}
|
||||
foreach ( $this->files as $variant => $file ) {
|
||||
if ( in_array( $variant, $variants ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
|
||||
$this->download_font_file( $file );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remote URL contents.
|
||||
*
|
||||
* @access private
|
||||
* @since 3.0.28
|
||||
* @param string $url The URL we want to get.
|
||||
* @return string The contents of the remote URL.
|
||||
*/
|
||||
public function get_remote_url_contents( $url ) {
|
||||
$response = wp_remote_get( $url );
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return array();
|
||||
}
|
||||
$html = wp_remote_retrieve_body( $response );
|
||||
if ( is_wp_error( $html ) ) {
|
||||
return;
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
}
|
262
functions/kirki/modules/webfonts/class-kirki-fonts-google.php
Normal file
262
functions/kirki/modules/webfonts/class-kirki-fonts-google.php
Normal file
|
@ -0,0 +1,262 @@
|
|||
<?php
|
||||
/**
|
||||
* Processes typography-related fields
|
||||
* and generates the google-font link.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Core
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 1.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages the way Google Fonts are enqueued.
|
||||
*/
|
||||
final class Kirki_Fonts_Google {
|
||||
|
||||
/**
|
||||
* The Kirki_Fonts_Google instance.
|
||||
* We use the singleton pattern here to avoid loading the google-font array multiple times.
|
||||
* This is mostly a performance tweak.
|
||||
*
|
||||
* @access private
|
||||
* @var null|object
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* DUMMY. DOESN'T DO ANYTHING, SIMPLY BACKWARDS-COMPATIBILITY.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var bool
|
||||
*/
|
||||
public static $force_load_all_subsets = false;
|
||||
|
||||
/**
|
||||
* If set to true, forces loading ALL variants.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var bool
|
||||
*/
|
||||
public static $force_load_all_variants = false;
|
||||
|
||||
/**
|
||||
* The array of fonts
|
||||
*
|
||||
* @access public
|
||||
* @var array
|
||||
*/
|
||||
public $fonts = array();
|
||||
|
||||
/**
|
||||
* An array of all google fonts.
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $google_fonts = array();
|
||||
|
||||
/**
|
||||
* An array of fonts that should be hosted locally instead of served via the google-CDN.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.32
|
||||
* @var array
|
||||
*/
|
||||
protected $hosted_fonts = array();
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*/
|
||||
private function __construct() {
|
||||
$config = apply_filters( 'kirki_config', array() );
|
||||
|
||||
// If we have set $config['disable_google_fonts'] to true then do not proceed any further.
|
||||
if ( isset( $config['disable_google_fonts'] ) && true === $config['disable_google_fonts'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_kirki_fonts_google_all_get', array( $this, 'get_googlefonts_json' ) );
|
||||
add_action( 'wp_ajax_nopriv_kirki_fonts_google_all_get', array( $this, 'get_googlefonts_json' ) );
|
||||
add_action( 'wp_ajax_kirki_fonts_standard_all_get', array( $this, 'get_standardfonts_json' ) );
|
||||
add_action( 'wp_ajax_nopriv_kirki_fonts_standard_all_get', array( $this, 'get_standardfonts_json' ) );
|
||||
|
||||
// Populate the array of google fonts.
|
||||
$this->google_fonts = Kirki_Fonts::get_google_fonts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the one, true instance of this class.
|
||||
* Prevents performance issues since this is only loaded once.
|
||||
*
|
||||
* @return object Kirki_Fonts_Google
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new Kirki_Fonts_Google();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the arguments of a field
|
||||
* determines if it's a typography field
|
||||
* and if it is, then takes appropriate actions.
|
||||
*
|
||||
* @param array $args The field arguments.
|
||||
*/
|
||||
public function generate_google_font( $args ) {
|
||||
global $wp_customize;
|
||||
|
||||
// Process typography fields.
|
||||
if ( isset( $args['type'] ) && 'kirki-typography' === $args['type'] ) {
|
||||
|
||||
// Get the value.
|
||||
$value = Kirki_Values::get_sanitized_field_value( $args );
|
||||
|
||||
if ( isset( $value['downloadFont'] ) && $value['downloadFont'] ) {
|
||||
$this->hosted_fonts[] = $value['font-family'];
|
||||
}
|
||||
|
||||
// If we don't have a font-family then we can skip this.
|
||||
if ( ! $wp_customize && ( ! isset( $value['font-family'] ) || in_array( $value['font-family'], $this->hosted_fonts, true ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If not a google-font, then we can skip this.
|
||||
if ( ! isset( $value['font-family'] ) || ! Kirki_Fonts::is_google_font( $value['font-family'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set a default value for variants.
|
||||
if ( ! isset( $value['variant'] ) ) {
|
||||
$value['variant'] = 'regular';
|
||||
}
|
||||
|
||||
// Add the requested google-font.
|
||||
if ( ! isset( $this->fonts[ $value['font-family'] ] ) ) {
|
||||
$this->fonts[ $value['font-family'] ] = array();
|
||||
}
|
||||
if ( ! in_array( $value['variant'], $this->fonts[ $value['font-family'] ], true ) ) {
|
||||
$this->fonts[ $value['font-family'] ][] = $value['variant'];
|
||||
}
|
||||
|
||||
// Are we force-loading all variants?
|
||||
if ( true === self::$force_load_all_variants ) {
|
||||
$all_variants = Kirki_Fonts::get_all_variants();
|
||||
$args['choices']['variant'] = array_keys( $all_variants );
|
||||
}
|
||||
|
||||
if ( ! empty( $args['choices']['variant'] ) && is_array( $args['choices']['variant'] ) ) {
|
||||
foreach ( $args['choices']['variant'] as $extra_variant ) {
|
||||
$this->fonts[ $value['font-family'] ][] = $extra_variant;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Process non-typography fields.
|
||||
if ( isset( $args['output'] ) && is_array( $args['output'] ) ) {
|
||||
foreach ( $args['output'] as $output ) {
|
||||
|
||||
// If we don't have a typography-related output argument we can skip this.
|
||||
if ( ! isset( $output['property'] ) || ! in_array( $output['property'], array( 'font-family', 'font-weight' ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the value.
|
||||
$value = Kirki_Values::get_sanitized_field_value( $args );
|
||||
|
||||
if ( is_string( $value ) ) {
|
||||
if ( 'font-family' === $output['property'] ) {
|
||||
if ( ! array_key_exists( $value, $this->fonts ) ) {
|
||||
$this->fonts[ $value ] = array();
|
||||
}
|
||||
} elseif ( 'font-weight' === $output['property'] ) {
|
||||
foreach ( $this->fonts as $font => $variants ) {
|
||||
if ( ! in_array( $value, $variants, true ) ) {
|
||||
$this->fonts[ $font ][] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the vbalidity of the selected font as well as its properties.
|
||||
* This is vital to make sure that the google-font script that we'll generate later
|
||||
* does not contain any invalid options.
|
||||
*/
|
||||
public function process_fonts() {
|
||||
|
||||
// Early exit if font-family is empty.
|
||||
if ( empty( $this->fonts ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $this->fonts as $font => $variants ) {
|
||||
|
||||
// Determine if this is indeed a google font or not.
|
||||
// If it's not, then just remove it from the array.
|
||||
if ( ! array_key_exists( $font, $this->google_fonts ) ) {
|
||||
unset( $this->fonts[ $font ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get all valid font variants for this font.
|
||||
$font_variants = array();
|
||||
if ( isset( $this->google_fonts[ $font ]['variants'] ) ) {
|
||||
$font_variants = $this->google_fonts[ $font ]['variants'];
|
||||
}
|
||||
foreach ( $variants as $variant ) {
|
||||
|
||||
// If this is not a valid variant for this font-family
|
||||
// then unset it and move on to the next one.
|
||||
if ( ! in_array( $variant, $font_variants, true ) ) {
|
||||
$variant_key = array_search( $variant, $this->fonts[ $font ], true );
|
||||
unset( $this->fonts[ $font ][ $variant_key ] );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the googlefonts JSON file.
|
||||
*
|
||||
* @since 3.0.17
|
||||
* @return void
|
||||
*/
|
||||
public function get_googlefonts_json() {
|
||||
include wp_normalize_path( dirname( __FILE__ ) . '/webfonts.json' );
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the standard fonts JSON.
|
||||
*
|
||||
* @since 3.0.17
|
||||
* @return void
|
||||
*/
|
||||
public function get_standardfonts_json() {
|
||||
echo wp_json_encode( Kirki_Fonts::get_standard_fonts() );
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets $this->hosted_fonts.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.32
|
||||
* @return array
|
||||
*/
|
||||
public function get_hosted_fonts() {
|
||||
return $this->hosted_fonts;
|
||||
}
|
||||
}
|
262
functions/kirki/modules/webfonts/class-kirki-fonts.php
Normal file
262
functions/kirki/modules/webfonts/class-kirki-fonts.php
Normal file
|
@ -0,0 +1,262 @@
|
|||
<?php
|
||||
/**
|
||||
* A simple object containing properties for fonts.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Core
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 1.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Kirki_Fonts object.
|
||||
*/
|
||||
final class Kirki_Fonts {
|
||||
|
||||
/**
|
||||
* The mode we'll be using to add google fonts.
|
||||
* This is a todo item, not yet functional.
|
||||
*
|
||||
* @static
|
||||
* @todo
|
||||
* @access public
|
||||
* @var string
|
||||
*/
|
||||
public static $mode = 'link';
|
||||
|
||||
/**
|
||||
* Holds a single instance of this object.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @var null|object
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* An array of our google fonts.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @var null|object
|
||||
*/
|
||||
public static $google_fonts = null;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*/
|
||||
private function __construct() {}
|
||||
|
||||
/**
|
||||
* Get the one, true instance of this class.
|
||||
* Prevents performance issues since this is only loaded once.
|
||||
*
|
||||
* @return object Kirki_Fonts
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile font options from different sources.
|
||||
*
|
||||
* @return array All available fonts.
|
||||
*/
|
||||
public static function get_all_fonts() {
|
||||
$standard_fonts = self::get_standard_fonts();
|
||||
$google_fonts = self::get_google_fonts();
|
||||
return apply_filters( 'kirki_fonts_all', array_merge( $standard_fonts, $google_fonts ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of standard websafe fonts.
|
||||
*
|
||||
* @return array Standard websafe fonts.
|
||||
*/
|
||||
public static function get_standard_fonts() {
|
||||
$standard_fonts = array(
|
||||
'serif' => array(
|
||||
'label' => 'Serif',
|
||||
'stack' => 'Georgia,Times,"Times New Roman",serif',
|
||||
),
|
||||
'sans-serif' => array(
|
||||
'label' => 'Sans Serif',
|
||||
'stack' => '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif',
|
||||
),
|
||||
'monospace' => array(
|
||||
'label' => 'Monospace',
|
||||
'stack' => 'Monaco,"Lucida Sans Typewriter","Lucida Typewriter","Courier New",Courier,monospace',
|
||||
),
|
||||
);
|
||||
return apply_filters( 'kirki_fonts_standard_fonts', $standard_fonts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of backup fonts based on the font-category
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_backup_fonts() {
|
||||
$backup_fonts = array(
|
||||
'sans-serif' => 'Helvetica, Arial, sans-serif',
|
||||
'serif' => 'Georgia, serif',
|
||||
'display' => '"Comic Sans MS", cursive, sans-serif',
|
||||
'handwriting' => '"Comic Sans MS", cursive, sans-serif',
|
||||
'monospace' => '"Lucida Console", Monaco, monospace',
|
||||
);
|
||||
return apply_filters( 'kirki_fonts_backup_fonts', $backup_fonts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of all available Google Fonts.
|
||||
*
|
||||
* @return array All Google Fonts.
|
||||
*/
|
||||
public static function get_google_fonts() {
|
||||
|
||||
// Get fonts from cache.
|
||||
self::$google_fonts = get_site_transient( 'kirki_googlefonts_cache' );
|
||||
|
||||
// If we're debugging, don't use cached.
|
||||
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
||||
self::$google_fonts = false;
|
||||
}
|
||||
|
||||
// If cache is populated, return cached fonts array.
|
||||
if ( self::$google_fonts ) {
|
||||
return self::$google_fonts;
|
||||
}
|
||||
|
||||
// If we got this far, cache was empty so we need to get from JSON.
|
||||
ob_start();
|
||||
include wp_normalize_path( dirname( __FILE__ ) . '/webfonts.json' );
|
||||
|
||||
$fonts_json = ob_get_clean();
|
||||
$fonts = json_decode( $fonts_json, true );
|
||||
|
||||
$google_fonts = array();
|
||||
if ( is_array( $fonts ) ) {
|
||||
foreach ( $fonts['items'] as $font ) {
|
||||
$google_fonts[ $font['family'] ] = array(
|
||||
'label' => $font['family'],
|
||||
'variants' => $font['variants'],
|
||||
'category' => $font['category'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the 'kirki_fonts_google_fonts' filter.
|
||||
self::$google_fonts = apply_filters( 'kirki_fonts_google_fonts', $google_fonts );
|
||||
|
||||
// Save the array in cache.
|
||||
$cache_time = apply_filters( 'kirki_googlefonts_transient_time', HOUR_IN_SECONDS );
|
||||
set_site_transient( 'kirki_googlefonts_cache', self::$google_fonts, $cache_time );
|
||||
|
||||
return self::$google_fonts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all available subsets.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public static function get_google_font_subsets() {
|
||||
return array(
|
||||
'cyrillic' => 'Cyrillic',
|
||||
'cyrillic-ext' => 'Cyrillic Extended',
|
||||
'devanagari' => 'Devanagari',
|
||||
'greek' => 'Greek',
|
||||
'greek-ext' => 'Greek Extended',
|
||||
'khmer' => 'Khmer',
|
||||
'latin' => 'Latin',
|
||||
'latin-ext' => 'Latin Extended',
|
||||
'vietnamese' => 'Vietnamese',
|
||||
'hebrew' => 'Hebrew',
|
||||
'arabic' => 'Arabic',
|
||||
'bengali' => 'Bengali',
|
||||
'gujarati' => 'Gujarati',
|
||||
'tamil' => 'Tamil',
|
||||
'telugu' => 'Telugu',
|
||||
'thai' => 'Thai',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy function to avoid issues with backwards-compatibility.
|
||||
* This is not functional, but it will prevent PHP Fatal errors.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
*/
|
||||
public static function get_google_font_uri() {}
|
||||
|
||||
/**
|
||||
* Returns an array of all available variants.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public static function get_all_variants() {
|
||||
return array(
|
||||
'100' => esc_html__( 'Ultra-Light 100', 'kirki' ),
|
||||
'100light' => esc_html__( 'Ultra-Light 100', 'kirki' ),
|
||||
'100italic' => esc_html__( 'Ultra-Light 100 Italic', 'kirki' ),
|
||||
'200' => esc_html__( 'Light 200', 'kirki' ),
|
||||
'200italic' => esc_html__( 'Light 200 Italic', 'kirki' ),
|
||||
'300' => esc_html__( 'Book 300', 'kirki' ),
|
||||
'300italic' => esc_html__( 'Book 300 Italic', 'kirki' ),
|
||||
'400' => esc_html__( 'Normal 400', 'kirki' ),
|
||||
'regular' => esc_html__( 'Normal 400', 'kirki' ),
|
||||
'italic' => esc_html__( 'Normal 400 Italic', 'kirki' ),
|
||||
'500' => esc_html__( 'Medium 500', 'kirki' ),
|
||||
'500italic' => esc_html__( 'Medium 500 Italic', 'kirki' ),
|
||||
'600' => esc_html__( 'Semi-Bold 600', 'kirki' ),
|
||||
'600bold' => esc_html__( 'Semi-Bold 600', 'kirki' ),
|
||||
'600italic' => esc_html__( 'Semi-Bold 600 Italic', 'kirki' ),
|
||||
'700' => esc_html__( 'Bold 700', 'kirki' ),
|
||||
'700italic' => esc_html__( 'Bold 700 Italic', 'kirki' ),
|
||||
'800' => esc_html__( 'Extra-Bold 800', 'kirki' ),
|
||||
'800bold' => esc_html__( 'Extra-Bold 800', 'kirki' ),
|
||||
'800italic' => esc_html__( 'Extra-Bold 800 Italic', 'kirki' ),
|
||||
'900' => esc_html__( 'Ultra-Bold 900', 'kirki' ),
|
||||
'900bold' => esc_html__( 'Ultra-Bold 900', 'kirki' ),
|
||||
'900italic' => esc_html__( 'Ultra-Bold 900 Italic', 'kirki' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a font-name is a valid google font or not.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $fontname The name of the font we want to check.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_google_font( $fontname ) {
|
||||
return ( array_key_exists( $fontname, self::$google_fonts ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets available options for a font.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public static function get_font_choices() {
|
||||
$fonts = self::get_all_fonts();
|
||||
$fonts_array = array();
|
||||
foreach ( $fonts as $key => $args ) {
|
||||
$fonts_array[ $key ] = $key;
|
||||
}
|
||||
return $fonts_array;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
/**
|
||||
* Adds the Webfont Loader to load fonts asyncronously.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Core
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages the way Google Fonts are enqueued.
|
||||
*/
|
||||
final class Kirki_Modules_Webfonts_Async {
|
||||
|
||||
/**
|
||||
* The config ID.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @var string
|
||||
*/
|
||||
protected $config_id;
|
||||
|
||||
/**
|
||||
* The Kirki_Modules_Webfonts object.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
protected $webfonts;
|
||||
|
||||
/**
|
||||
* The Kirki_Fonts_Google object.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
protected $googlefonts;
|
||||
|
||||
/**
|
||||
* Fonts to load.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.26
|
||||
* @var array
|
||||
*/
|
||||
protected $fonts_to_load = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0
|
||||
* @param string $config_id The config-ID.
|
||||
* @param object $webfonts The Kirki_Modules_Webfonts object.
|
||||
* @param object $googlefonts The Kirki_Fonts_Google object.
|
||||
* @param array $args Extra args we want to pass.
|
||||
*/
|
||||
public function __construct( $config_id, $webfonts, $googlefonts, $args = array() ) {
|
||||
$this->config_id = $config_id;
|
||||
$this->webfonts = $webfonts;
|
||||
$this->googlefonts = $googlefonts;
|
||||
|
||||
add_action( 'wp_head', array( $this, 'webfont_loader' ) );
|
||||
add_action( 'wp_head', array( $this, 'webfont_loader_script' ), 30 );
|
||||
|
||||
// Add these in the dashboard to support editor-styles.
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'webfont_loader' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'webfont_loader_script' ), 30 );
|
||||
|
||||
add_filter( 'wp_resource_hints', array( $this, 'resource_hints' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add preconnect for Google Fonts.
|
||||
*
|
||||
* @access public
|
||||
* @param array $urls URLs to print for resource hints.
|
||||
* @param string $relation_type The relation type the URLs are printed.
|
||||
* @return array $urls URLs to print for resource hints.
|
||||
*/
|
||||
public function resource_hints( $urls, $relation_type ) {
|
||||
$fonts_to_load = $this->googlefonts->fonts;
|
||||
|
||||
if ( ! empty( $fonts_to_load ) && 'preconnect' === $relation_type ) {
|
||||
$urls[] = array(
|
||||
'href' => 'https://fonts.gstatic.com',
|
||||
'crossorigin',
|
||||
);
|
||||
}
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Webfont Loader for Google Fonts.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function webfont_loader() {
|
||||
|
||||
// Go through our fields and populate $this->fonts.
|
||||
$this->webfonts->loop_fields( $this->config_id );
|
||||
|
||||
$this->googlefonts->fonts = apply_filters( 'kirki_enqueue_google_fonts', $this->googlefonts->fonts );
|
||||
|
||||
// Goes through $this->fonts and adds or removes things as needed.
|
||||
$this->googlefonts->process_fonts();
|
||||
|
||||
foreach ( $this->googlefonts->fonts as $font => $weights ) {
|
||||
foreach ( $weights as $key => $value ) {
|
||||
if ( 'italic' === $value ) {
|
||||
$weights[ $key ] = '400i';
|
||||
} else {
|
||||
$weights[ $key ] = str_replace( array( 'regular', 'bold', 'italic' ), array( '400', '', 'i' ), $value );
|
||||
}
|
||||
}
|
||||
$this->fonts_to_load[] = $font . ':' . join( ',', $weights ) . ':cyrillic,cyrillic-ext,devanagari,greek,greek-ext,khmer,latin,latin-ext,vietnamese,hebrew,arabic,bengali,gujarati,tamil,telugu,thai';
|
||||
}
|
||||
if ( ! empty( $this->fonts_to_load ) ) {
|
||||
Kirki_Modules_Webfont_Loader::$load = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Webfont Loader script for Google Fonts.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function webfont_loader_script() {
|
||||
if ( ! empty( $this->fonts_to_load ) ) {
|
||||
wp_add_inline_script(
|
||||
'webfont-loader',
|
||||
'WebFont.load({google:{families:[\'' . join( '\', \'', $this->fonts_to_load ) . '\']}});',
|
||||
'after'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles adding to the footer the @font-face CSS for locally-hosted google-fonts.
|
||||
* Solves privacy concerns with Google's CDN and their sometimes less-than-transparent policies.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Core
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.28
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages the way Google Fonts are enqueued.
|
||||
*/
|
||||
final class Kirki_Modules_Webfonts_Local {
|
||||
|
||||
/**
|
||||
* The config ID.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.28
|
||||
* @var string
|
||||
*/
|
||||
protected $config_id;
|
||||
|
||||
/**
|
||||
* The Kirki_Modules_Webfonts object.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.28
|
||||
* @var object
|
||||
*/
|
||||
protected $webfonts;
|
||||
|
||||
/**
|
||||
* The Kirki_Fonts_Google object.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.28
|
||||
* @var object
|
||||
*/
|
||||
protected $googlefonts;
|
||||
|
||||
/**
|
||||
* Fonts to load.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.28
|
||||
* @var array
|
||||
*/
|
||||
protected $fonts_to_load = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access public
|
||||
* @since 3..28
|
||||
* @param object $webfonts The Kirki_Modules_Webfonts object.
|
||||
* @param object $googlefonts The Kirki_Fonts_Google object.
|
||||
*/
|
||||
public function __construct( $webfonts, $googlefonts ) {
|
||||
$this->webfonts = $webfonts;
|
||||
$this->googlefonts = $googlefonts;
|
||||
|
||||
add_action( 'wp_footer', array( $this, 'add_styles' ) );
|
||||
add_action( 'admin_footer', array( $this, 'add_styles' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Webfont Loader for Google Fonts.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.28
|
||||
*/
|
||||
public function add_styles() {
|
||||
|
||||
// Go through our fields and populate $this->fonts.
|
||||
$this->webfonts->loop_fields( $this->config_id );
|
||||
$this->googlefonts->process_fonts();
|
||||
$hosted_fonts = $this->googlefonts->get_hosted_fonts();
|
||||
|
||||
// Early exit if we don't need to add any fonts.
|
||||
if ( empty( $hosted_fonts ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we only do this once per font-family.
|
||||
$hosted_fonts = array_unique( $hosted_fonts );
|
||||
|
||||
// Start CSS.
|
||||
$css = '';
|
||||
foreach ( $hosted_fonts as $family ) {
|
||||
|
||||
// Add the @font-face CSS for this font-family.
|
||||
$css .= Kirki_Fonts_Google_Local::init( $family )->get_css();
|
||||
}
|
||||
|
||||
// If we've got CSS, add to the footer.
|
||||
if ( $css ) {
|
||||
echo '<style id="kirki-local-webfonts-' . esc_attr( sanitize_key( $this->config_id ) ) . '">' . $css . '</style>'; // WPCS: XSS ok.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles webfonts.
|
||||
*
|
||||
* @package Kirki
|
||||
* @category Modules
|
||||
* @author Aristeides Stathopoulos
|
||||
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds script for tooltips.
|
||||
*/
|
||||
class Kirki_Modules_Webfonts {
|
||||
|
||||
/**
|
||||
* The object instance.
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* The Kirki_Fonts_Google object.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
* @var object
|
||||
*/
|
||||
protected $fonts_google;
|
||||
|
||||
/**
|
||||
* The class constructor
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function __construct() {
|
||||
|
||||
include_once wp_normalize_path( dirname( __FILE__ ) . '/class-kirki-fonts.php' );
|
||||
include_once wp_normalize_path( dirname( __FILE__ ) . '/class-kirki-fonts-google.php' );
|
||||
include_once wp_normalize_path( dirname( __FILE__ ) . '/class-kirki-fonts-google-local.php' );
|
||||
|
||||
add_action( 'wp_loaded', array( $this, 'run' ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Run on after_setup_theme.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function run() {
|
||||
$this->fonts_google = Kirki_Fonts_Google::get_instance();
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this object.
|
||||
* Prevents duplicate instances which avoid artefacts and improves performance.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init other objects depending on the method we'll be using.
|
||||
*
|
||||
* @access protected
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function init() {
|
||||
foreach ( array_keys( Kirki::$config ) as $config_id ) {
|
||||
$method = $this->get_method( $config_id );
|
||||
$classname = 'Kirki_Modules_Webfonts_' . ucfirst( $method );
|
||||
new $classname( $config_id, $this, $this->fonts_google );
|
||||
}
|
||||
new Kirki_Modules_Webfonts_Local( $this, $this->fonts_google );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the method we're going to use.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0.0
|
||||
* @return string
|
||||
*/
|
||||
public function get_method() {
|
||||
|
||||
// Figure out which method to use.
|
||||
$method = apply_filters( 'kirki_googlefonts_load_method', 'async' );
|
||||
|
||||
// Fallback to 'async' if value is invalid.
|
||||
if ( 'async' !== $method && 'embed' !== $method && 'link' !== $method ) {
|
||||
$method = 'async';
|
||||
}
|
||||
|
||||
$classname = 'Kirki_Modules_Webfonts_' . ucfirst( $method );
|
||||
if ( ! class_exists( $classname ) ) {
|
||||
$method = 'async';
|
||||
}
|
||||
|
||||
// Force using the JS method while in the customizer.
|
||||
// This will help us work-out the live-previews for typography fields.
|
||||
// If we're not in the customizer use the defined method.
|
||||
return ( is_customize_preview() ) ? 'async' : $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes through all our fields and then populates the $this->fonts property.
|
||||
*
|
||||
* @access public
|
||||
* @param string $config_id The config-ID.
|
||||
*/
|
||||
public function loop_fields( $config_id ) {
|
||||
foreach ( Kirki::$fields as $field ) {
|
||||
if ( isset( $field['kirki_config'] ) && $config_id !== $field['kirki_config'] ) {
|
||||
continue;
|
||||
}
|
||||
if ( true === apply_filters( "kirki_{$config_id}_webfonts_skip_hidden", true ) ) {
|
||||
// Only continue if field dependencies are met.
|
||||
if ( ! empty( $field['required'] ) ) {
|
||||
$valid = true;
|
||||
|
||||
foreach ( $field['required'] as $requirement ) {
|
||||
if ( isset( $requirement['setting'] ) && isset( $requirement['value'] ) && isset( $requirement['operator'] ) ) {
|
||||
$controller_value = Kirki_Values::get_value( $config_id, $requirement['setting'] );
|
||||
if ( ! Kirki_Helper::compare_values( $controller_value, $requirement['value'], $requirement['operator'] ) ) {
|
||||
$valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $valid ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->fonts_google->generate_google_font( $field );
|
||||
}
|
||||
}
|
||||
}
|
1
functions/kirki/modules/webfonts/webfont-files.json
Normal file
1
functions/kirki/modules/webfonts/webfont-files.json
Normal file
File diff suppressed because one or more lines are too long
1
functions/kirki/modules/webfonts/webfont-names.json
Normal file
1
functions/kirki/modules/webfonts/webfont-names.json
Normal file
File diff suppressed because one or more lines are too long
1
functions/kirki/modules/webfonts/webfonts.json
Normal file
1
functions/kirki/modules/webfonts/webfonts.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue