Changeset 46357 for trunk/src/wp-content/themes/twentytwenty/functions.php
- Timestamp:
- 09/30/2019 05:40:14 PM (5 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-content/themes/twentytwenty/functions.php
r46283 r46357 25 25 */ 26 26 27 if ( ! function_exists( 'twentytwenty_theme_support' ) ) { 28 /** 29 * Sets up theme defaults and registers support for various WordPress features. 27 /** 28 * Sets up theme defaults and registers support for various WordPress features. 29 * 30 * Note that this function is hooked into the after_setup_theme hook, which 31 * runs before the init hook. The init hook is too late for some features, such 32 * as indicating support for post thumbnails. 33 */ 34 function twentytwenty_theme_support() { 35 36 // Add default posts and comments RSS feed links to head. 37 add_theme_support( 'automatic-feed-links' ); 38 39 // Custom background color. 40 add_theme_support( 41 'custom-background', 42 array( 43 'default-color' => 'f5efe0', 44 ) 45 ); 46 47 // Set content-width. 48 global $content_width; 49 if ( ! isset( $content_width ) ) { 50 $content_width = 580; 51 } 52 53 /* 54 * Enable support for Post Thumbnails on posts and pages. 30 55 * 31 * Note that this function is hooked into the after_setup_theme hook, which 32 * runs before the init hook. The init hook is too late for some features, such 33 * as indicating support for post thumbnails. 56 * @link https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/ 34 57 */ 35 function twentytwenty_theme_support() { 36 37 // Add default posts and comments RSS feed links to head. 38 add_theme_support( 'automatic-feed-links' ); 39 40 // Custom background color. 41 add_theme_support( 42 'custom-background', 43 array( 44 'default-color' => 'f5efe0', 45 ) 46 ); 47 48 // Set content-width. 49 global $content_width; 50 if ( ! isset( $content_width ) ) { 51 $content_width = 580; 52 } 53 54 /* 55 * Enable support for Post Thumbnails on posts and pages. 56 * 57 * @link https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/ 58 */ 59 add_theme_support( 'post-thumbnails' ); 60 61 // Set post thumbnail size. 62 set_post_thumbnail_size( 1200, 9999 ); 63 64 // Add custom image sizes. 65 add_image_size( 'twentytwenty-fullscreen', 1980, 9999 ); 66 67 // Custom logo. 68 $logo_id = get_theme_mod( 'custom_logo' ); 69 $logo_width = 120; 70 $logo_height = 90; 71 72 // If the retina setting is active, double the recommended width and height. 73 if ( get_theme_mod( 'twentytwenty_retina_logo', false ) ) { 74 $logo_width = floor( $logo_width * 2 ); 75 $logo_height = floor( $logo_height * 2 ); 76 } 77 78 add_theme_support( 79 'custom-logo', 80 array( 81 'height' => $logo_height, 82 'width' => $logo_width, 83 'flex-height' => true, 84 'flex-width' => true, 85 'header-text' => array( 'site-title', 'site-description' ), 86 ) 87 ); 88 89 /* 90 * Let WordPress manage the document title. 91 * By adding theme support, we declare that this theme does not use a 92 * hard-coded <title> tag in the document head, and expect WordPress to 93 * provide it for us. 94 */ 95 add_theme_support( 'title-tag' ); 96 97 /* 98 * Switch default core markup for search form, comment form, and comments 99 * to output valid HTML5. 100 */ 101 add_theme_support( 102 'html5', 103 array( 104 'search-form', 105 'comment-form', 106 'comment-list', 107 'gallery', 108 'caption', 109 'script', 110 'style', 111 ) 112 ); 113 114 /* 115 * Make theme available for translation. 116 * Translations can be filed in the /languages/ directory. 117 * If you're building a theme based on Twenty Nineteen, use a find and replace 118 * to change 'twentynineteen' to the name of your theme in all the template files. 119 */ 120 load_theme_textdomain( 'twentytwenty' ); 121 122 // Add support for full and wide align images. 123 add_theme_support( 'align-wide' ); 124 125 // Adds starter content to highlight the theme on fresh sites. 126 add_theme_support( 'starter-content', twentytwenty_get_starter_content() ); 127 128 // Add theme support for selective refresh for widgets. 129 add_theme_support( 'customize-selective-refresh-widgets' ); 130 131 /* 132 * Adds `async` and `defer` support for scripts registered or enqueued 133 * by the theme. 134 */ 135 $loader = new TwentyTwenty_Script_Loader(); 136 add_filter( 'script_loader_tag', [ $loader, 'filter_script_loader_tag' ], 10, 2 ); 137 138 } 139 140 add_action( 'after_setup_theme', 'twentytwenty_theme_support' ); 141 142 } 58 add_theme_support( 'post-thumbnails' ); 59 60 // Set post thumbnail size. 61 set_post_thumbnail_size( 1200, 9999 ); 62 63 // Add custom image sizes. 64 add_image_size( 'twentytwenty-fullscreen', 1980, 9999 ); 65 66 // Custom logo. 67 $logo_id = get_theme_mod( 'custom_logo' ); 68 $logo_width = 120; 69 $logo_height = 90; 70 71 // If the retina setting is active, double the recommended width and height. 72 if ( get_theme_mod( 'twentytwenty_retina_logo', false ) ) { 73 $logo_width = floor( $logo_width * 2 ); 74 $logo_height = floor( $logo_height * 2 ); 75 } 76 77 add_theme_support( 78 'custom-logo', 79 array( 80 'height' => $logo_height, 81 'width' => $logo_width, 82 'flex-height' => true, 83 'flex-width' => true, 84 'header-text' => array( 'site-title', 'site-description' ), 85 ) 86 ); 87 88 /* 89 * Let WordPress manage the document title. 90 * By adding theme support, we declare that this theme does not use a 91 * hard-coded <title> tag in the document head, and expect WordPress to 92 * provide it for us. 93 */ 94 add_theme_support( 'title-tag' ); 95 96 /* 97 * Switch default core markup for search form, comment form, and comments 98 * to output valid HTML5. 99 */ 100 add_theme_support( 101 'html5', 102 array( 103 'search-form', 104 'comment-form', 105 'comment-list', 106 'gallery', 107 'caption', 108 'script', 109 'style', 110 ) 111 ); 112 113 /* 114 * Make theme available for translation. 115 * Translations can be filed in the /languages/ directory. 116 * If you're building a theme based on Twenty Twenty, use a find and replace 117 * to change 'twentytwenty' to the name of your theme in all the template files. 118 */ 119 load_theme_textdomain( 'twentytwenty' ); 120 121 // Add support for full and wide align images. 122 add_theme_support( 'align-wide' ); 123 124 // Adds starter content to highlight the theme on fresh sites. 125 add_theme_support( 'starter-content', twentytwenty_get_starter_content() ); 126 127 // Add theme support for selective refresh for widgets. 128 add_theme_support( 'customize-selective-refresh-widgets' ); 129 130 /* 131 * Adds `async` and `defer` support for scripts registered or enqueued 132 * by the theme. 133 */ 134 $loader = new TwentyTwenty_Script_Loader(); 135 add_filter( 'script_loader_tag', array( $loader, 'filter_script_loader_tag' ), 10, 2 ); 136 137 } 138 139 add_action( 'after_setup_theme', 'twentytwenty_theme_support' ); 143 140 144 141 /** … … 173 170 require get_template_directory() . '/inc/starter-content.php'; 174 171 175 if ( ! function_exists( 'twentytwenty_register_styles' ) ) { 172 /** 173 * Register and Enqueue Styles. 174 */ 175 function twentytwenty_register_styles() { 176 177 $theme_version = wp_get_theme()->get( 'Version' ); 178 $css_dependencies = array(); 179 176 180 /** 177 * Register and Enqueue Styles. 178 */ 179 function twentytwenty_register_styles() { 180 181 $theme_version = wp_get_theme()->get( 'Version' ); 182 $css_dependencies = array(); 183 184 // By default, only load the Font Awesome fonts if the social menu is in use. 185 $load_font_awesome = apply_filters( 'twentytwenty_load_font_awesome', has_nav_menu( 'social' ) ); 186 187 if ( $load_font_awesome ) { 188 wp_register_style( 'font-awesome', get_template_directory_uri() . '/assets/css/font-awesome.css', false, '5.10.2', 'all' ); 189 $css_dependencies[] = 'font-awesome'; 190 } 191 192 wp_enqueue_style( 'twentytwenty-style', get_template_directory_uri() . '/style.css', $css_dependencies, $theme_version ); 193 wp_style_add_data( 'twentytwenty-style', 'rtl', 'replace' ); 194 195 // Add output of Customizer settings as inline style. 196 wp_add_inline_style( 'twentytwenty-style', twentytwenty_get_customizer_css( 'front-end' ) ); 197 198 } 199 200 add_action( 'wp_enqueue_scripts', 'twentytwenty_register_styles' ); 201 202 } 203 204 if ( ! function_exists( 'twentytwenty_register_scripts' ) ) { 205 /** 206 * Register and Enqueue Scripts. 207 */ 208 function twentytwenty_register_scripts() { 209 210 $theme_version = wp_get_theme()->get( 'Version' ); 211 212 if ( ( ! is_admin() ) && is_singular() && comments_open() && get_option( 'thread_comments' ) ) { 213 wp_enqueue_script( 'comment-reply' ); 214 } 215 216 wp_enqueue_script( 'twentytwenty-js', get_template_directory_uri() . '/assets/js/index.js', array(), $theme_version, false ); 217 wp_script_add_data( 'twentytwenty-js', 'async', true ); 218 219 } 220 221 add_action( 'wp_enqueue_scripts', 'twentytwenty_register_scripts' ); 222 223 } 224 225 if ( ! function_exists( 'twentytwenty_menus' ) ) { 226 /** 227 * Register navigation menus uses wp_nav_menu in five places. 228 */ 229 function twentytwenty_menus() { 230 231 $locations = array( 232 'primary' => __( 'Desktop Horizontal Menu', 'twentytwenty' ), 233 'expanded' => __( 'Desktop Expanded Menu', 'twentytwenty' ), 234 'mobile' => __( 'Mobile Menu', 'twentytwenty' ), 235 'footer' => __( 'Footer Menu', 'twentytwenty' ), 236 'social' => __( 'Social Menu', 'twentytwenty' ), 237 ); 238 239 register_nav_menus( $locations ); 240 } 241 242 add_action( 'init', 'twentytwenty_menus' ); 243 244 } 181 * Filter to load, unload Font Awesome CSS 182 * 183 * By default, only load the Font Awesome fonts if the social menu is in use or 184 * using filter Font Awesome css be loaded 185 * 186 * @since 1.0.0 187 * 188 * @param bool Whether to load font awesome, Default false. 189 */ 190 $load_font_awesome = apply_filters( 'twentytwenty_load_font_awesome', has_nav_menu( 'social' ) ); 191 192 if ( $load_font_awesome ) { 193 wp_register_style( 'font-awesome', get_template_directory_uri() . '/assets/css/font-awesome.css', false, '5.10.2', 'all' ); 194 $css_dependencies[] = 'font-awesome'; 195 } 196 197 wp_enqueue_style( 'twentytwenty-style', get_template_directory_uri() . '/style.css', $css_dependencies, $theme_version ); 198 wp_style_add_data( 'twentytwenty-style', 'rtl', 'replace' ); 199 200 // Add output of Customizer settings as inline style. 201 wp_add_inline_style( 'twentytwenty-style', twentytwenty_get_customizer_css( 'front-end' ) ); 202 203 // Add print CSS. 204 wp_enqueue_style( 'twentytwenty-print-style', get_template_directory_uri() . '/print.css', null, $theme_version, 'print' ); 205 206 } 207 208 add_action( 'wp_enqueue_scripts', 'twentytwenty_register_styles' ); 209 210 /** 211 * Register and Enqueue Scripts. 212 */ 213 function twentytwenty_register_scripts() { 214 215 $theme_version = wp_get_theme()->get( 'Version' ); 216 217 if ( ( ! is_admin() ) && is_singular() && comments_open() && get_option( 'thread_comments' ) ) { 218 wp_enqueue_script( 'comment-reply' ); 219 } 220 221 wp_enqueue_script( 'twentytwenty-js', get_template_directory_uri() . '/assets/js/index.js', array(), $theme_version, false ); 222 wp_script_add_data( 'twentytwenty-js', 'async', true ); 223 224 } 225 226 add_action( 'wp_enqueue_scripts', 'twentytwenty_register_scripts' ); 227 228 /** 229 * Register navigation menus uses wp_nav_menu in five places. 230 */ 231 function twentytwenty_menus() { 232 233 $locations = array( 234 'primary' => __( 'Desktop Horizontal Menu', 'twentytwenty' ), 235 'expanded' => __( 'Desktop Expanded Menu', 'twentytwenty' ), 236 'mobile' => __( 'Mobile Menu', 'twentytwenty' ), 237 'footer' => __( 'Footer Menu', 'twentytwenty' ), 238 'social' => __( 'Social Menu', 'twentytwenty' ), 239 ); 240 241 register_nav_menus( $locations ); 242 } 243 244 add_action( 'init', 'twentytwenty_menus' ); 245 245 246 246 /** … … 299 299 } 300 300 301 if ( ! function_exists( 'twentytwenty_skip_link' ) ) { 301 /** 302 * Include a skip to content link at the top of the page so that users can bypass the menu. 303 */ 304 function twentytwenty_skip_link() { 305 echo '<a class="skip-link screen-reader-text" href="#site-content">' . __( 'Skip to the content', 'twentytwenty' ) . '</a>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- core trusts translations 306 } 307 308 add_action( 'wp_body_open', 'twentytwenty_skip_link', 5 ); 309 310 /** 311 * Register widget areas. 312 * 313 * @link https://developer.wordpress.org/themes/functionality/sidebars/#registering-a-sidebar 314 */ 315 function twentytwenty_sidebar_registration() { 316 317 // Arguments used in all register_sidebar() calls. 318 $shared_args = array( 319 'before_title' => '<h2 class="widget-title subheading heading-size-3">', 320 'after_title' => '</h2>', 321 'before_widget' => '<div class="widget %2$s"><div class="widget-content">', 322 'after_widget' => '</div></div>', 323 ); 324 325 // Footer #1. 326 register_sidebar( 327 array_merge( 328 $shared_args, 329 array( 330 'name' => __( 'Footer #1', 'twentytwenty' ), 331 'id' => 'sidebar-1', 332 'description' => __( 'Widgets in this area will be displayed in the first column in the footer.', 'twentytwenty' ), 333 ) 334 ) 335 ); 336 337 // Footer #2. 338 register_sidebar( 339 array_merge( 340 $shared_args, 341 array( 342 'name' => __( 'Footer #2', 'twentytwenty' ), 343 'id' => 'sidebar-2', 344 'description' => __( 'Widgets in this area will be displayed in the second column in the footer.', 'twentytwenty' ), 345 ) 346 ) 347 ); 348 349 } 350 351 add_action( 'widgets_init', 'twentytwenty_sidebar_registration' ); 352 353 /** 354 * Enqueue supplemental block editor styles. 355 */ 356 function twentytwenty_block_editor_styles() { 357 358 $css_dependencies = array(); 359 360 // Enqueue the editor styles. 361 wp_enqueue_style( 'twentytwenty-block-editor-styles', get_theme_file_uri( '/assets/css/editor-style-block.css' ), $css_dependencies, wp_get_theme()->get( 'Version' ), 'all' ); 362 wp_style_add_data( 'twentytwenty-block-editor-styles', 'rtl', 'replace' ); 363 364 // Add inline style from the Customizer. 365 wp_add_inline_style( 'twentytwenty-block-editor-styles', twentytwenty_get_customizer_css( 'block-editor' ) ); 366 367 // Enqueue the editor script. 368 wp_enqueue_script( 'twentytwenty-block-editor-script', get_theme_file_uri( '/assets/js/editor-script-block.js' ), array( 'wp-blocks', 'wp-dom' ), wp_get_theme()->get( 'Version' ), true ); 369 } 370 371 add_action( 'enqueue_block_editor_assets', 'twentytwenty_block_editor_styles', 1, 1 ); 372 373 /** 374 * Enqueue classic editor styles. 375 */ 376 function twentytwenty_classic_editor_styles() { 377 378 $classic_editor_styles = array( 379 '/assets/css/editor-style-classic.css', 380 ); 381 382 add_editor_style( $classic_editor_styles ); 383 384 } 385 386 add_action( 'init', 'twentytwenty_classic_editor_styles' ); 387 388 /** 389 * Output Customizer Settings in the Classic Editor. 390 * Adds styles to the head of the TinyMCE iframe. Kudos to @Otto42 for the original solution. 391 * 392 * @param array $mce_init TinyMCE styles. 393 */ 394 function twentytwenty_add_classic_editor_customizer_styles( $mce_init ) { 395 396 $styles = twentytwenty_get_customizer_css( 'classic-editor' ); 397 398 if ( ! isset( $mce_init['content_style'] ) ) { 399 $mce_init['content_style'] = $styles . ' '; 400 } else { 401 $mce_init['content_style'] .= ' ' . $styles . ' '; 402 } 403 404 return $mce_init; 405 406 } 407 408 add_filter( 'tiny_mce_before_init', 'twentytwenty_add_classic_editor_customizer_styles' ); 409 410 /** 411 * Block Editor Settings. 412 * Add custom colors and font sizes to the block editor. 413 */ 414 function twentytwenty_block_editor_settings() { 415 416 // Block Editor Palette. 417 $editor_color_palette = array( 418 array( 419 'name' => esc_html__( 'Accent Color', 'twentytwenty' ), 420 'slug' => 'accent', 421 'color' => twentytwenty_get_color_for_area( 'content', 'accent' ), 422 ), 423 array( 424 'name' => esc_html__( 'Secondary', 'twentytwenty' ), 425 'slug' => 'secondary', 426 'color' => twentytwenty_get_color_for_area( 'content', 'secondary' ), 427 ), 428 array( 429 'name' => esc_html__( 'Subtle Background', 'twentytwenty' ), 430 'slug' => 'subtle-background', 431 'color' => twentytwenty_get_color_for_area( 'content', 'borders' ), 432 ), 433 ); 434 435 // Add the background option. 436 $background_color = get_theme_mod( 'background_color' ); 437 if ( ! $background_color ) { 438 $background_color_arr = get_theme_support( 'custom-background' ); 439 $background_color = $background_color_arr[0]['default-color']; 440 } 441 $editor_color_palette[] = array( 442 'name' => __( 'Background Color', 'twentytwenty' ), 443 'slug' => 'background', 444 'color' => '#' . $background_color, 445 ); 446 447 // If we have accent colors, add them to the block editor palette. 448 if ( $editor_color_palette ) { 449 add_theme_support( 'editor-color-palette', $editor_color_palette ); 450 } 451 452 // Gutenberg Font Sizes. 453 add_theme_support( 454 'editor-font-sizes', 455 array( 456 array( 457 'name' => _x( 'Small', 'Name of the small font size in Gutenberg', 'twentytwenty' ), 458 'shortName' => _x( 'S', 'Short name of the small font size in the Gutenberg editor.', 'twentytwenty' ), 459 'size' => 16, 460 'slug' => 'small', 461 ), 462 array( 463 'name' => _x( 'Regular', 'Name of the regular font size in Gutenberg', 'twentytwenty' ), 464 'shortName' => _x( 'M', 'Short name of the regular font size in the Gutenberg editor.', 'twentytwenty' ), 465 'size' => 18, 466 'slug' => 'regular', 467 ), 468 array( 469 'name' => _x( 'Large', 'Name of the large font size in Gutenberg', 'twentytwenty' ), 470 'shortName' => _x( 'L', 'Short name of the large font size in the Gutenberg editor.', 'twentytwenty' ), 471 'size' => 24, 472 'slug' => 'large', 473 ), 474 array( 475 'name' => _x( 'Larger', 'Name of the larger font size in Gutenberg', 'twentytwenty' ), 476 'shortName' => _x( 'XL', 'Short name of the larger font size in the Gutenberg editor.', 'twentytwenty' ), 477 'size' => 32, 478 'slug' => 'larger', 479 ), 480 ) 481 ); 482 483 // If we have a dark background color then add support for dark editor style. 484 // We can determine if the background color is dark by checking if the text-color is white. 485 if ( '#ffffff' === strtolower( twentytwenty_get_color_for_area( 'content', 'text' ) ) ) { 486 add_theme_support( 'dark-editor-style' ); 487 } 488 489 } 490 491 add_action( 'after_setup_theme', 'twentytwenty_block_editor_settings' ); 492 493 /** 494 * Read More Link 495 * Overwrite default (more ...) tag 496 */ 497 function twentytwenty_read_more_tag() { 498 return sprintf( 499 '<a href="%1$s" class="more-link faux-button">%2$s <span class="screen-reader-text">"%3$s"</span></a>', 500 esc_url( get_permalink( get_the_ID() ) ), 501 esc_html__( 'Continue reading', 'twentytwenty' ), 502 get_the_title( get_the_ID() ) 503 ); 504 } 505 add_filter( 'the_content_more_link', 'twentytwenty_read_more_tag' ); 506 507 /** 508 * Enqueues scripts for customizer controls & settings. 509 * 510 * @since 1.0.0 511 * 512 * @return void 513 */ 514 function twentytwenty_customize_controls_enqueue_scripts() { 515 $theme_version = wp_get_theme()->get( 'Version' ); 516 517 // Add script for color calculations. 518 wp_enqueue_script( 'twentytwenty-color-calculations', get_template_directory_uri() . '/assets/js/color-calculations.js', array( 'wp-color-picker' ), $theme_version, false ); 519 520 // Add script for controls. 521 wp_enqueue_script( 'twentytwenty-customize-controls', get_template_directory_uri() . '/assets/js/customize-controls.js', array( 'twentytwenty-color-calculations', 'customize-controls', 'underscore', 'jquery' ), $theme_version, false ); 522 wp_localize_script( 'twentytwenty-customize-controls', 'twentyTwentyBgColors', twentytwenty_get_customizer_color_vars() ); 523 } 524 525 add_action( 'customize_controls_enqueue_scripts', 'twentytwenty_customize_controls_enqueue_scripts' ); 526 527 /** 528 * Enqueue scripts for the customizer preview. 529 * 530 * @since 1.0.0 531 * 532 * @return void 533 */ 534 function twentytwenty_customize_preview_init() { 535 $theme_version = wp_get_theme()->get( 'Version' ); 536 537 wp_enqueue_script( 'twentytwenty-customize-preview', get_theme_file_uri( '/assets/js/customize-preview.js' ), array( 'customize-preview', 'customize-selective-refresh', 'jquery' ), $theme_version, true ); 538 wp_localize_script( 'twentytwenty-customize-preview', 'twentyTwentyBgColors', twentytwenty_get_customizer_color_vars() ); 539 wp_localize_script( 'twentytwenty-customize-preview', 'twentyTwentyPreviewEls', twentytwenty_get_elements_array() ); 540 541 wp_add_inline_script( 542 'twentytwenty-customize-preview', 543 sprintf( 544 'wp.customize.selectiveRefresh.partialConstructor[ %1$s ].prototype.attrs = %2$s;', 545 wp_json_encode( 'cover_opacity' ), 546 wp_json_encode( twentytwenty_customize_opacity_range() ) 547 ) 548 ); 549 } 550 551 add_action( 'customize_preview_init', 'twentytwenty_customize_preview_init' ); 552 553 /** 554 * Get accessible color for an area. 555 * 556 * @since 1.0.0 557 * 558 * @param string $area The area we want to get the colors for. 559 * @param string $context Can be 'text' or 'accent'. 560 * @return string Returns a HEX color. 561 */ 562 function twentytwenty_get_color_for_area( $area = 'content', $context = 'text' ) { 563 564 // Get the value from the theme-mod. 565 $settings = get_theme_mod( 566 'accent_accessible_colors', 567 array( 568 'content' => array( 569 'text' => '#000000', 570 'accent' => '#cd2653', 571 'secondary' => '#6d6d6d', 572 'borders' => '#dcd7ca', 573 ), 574 'header-footer' => array( 575 'text' => '#000000', 576 'accent' => '#cd2653', 577 'secondary' => '#6d6d6d', 578 'borders' => '#dcd7ca', 579 ), 580 ) 581 ); 582 583 // If we have a value return it. 584 if ( isset( $settings[ $area ] ) && isset( $settings[ $area ][ $context ] ) ) { 585 return $settings[ $area ][ $context ]; 586 } 587 588 // Return false if the option doesn't exist. 589 return false; 590 } 591 592 /** 593 * Returns an array of variables for the customizer preview. 594 * 595 * @since 1.0.0 596 * 597 * @return array 598 */ 599 function twentytwenty_get_customizer_color_vars() { 600 $colors = array( 601 'content' => array( 602 'setting' => 'background_color', 603 ), 604 'header-footer' => array( 605 'setting' => 'header_footer_background_color', 606 ), 607 ); 608 return $colors; 609 } 610 611 /** 612 * Get an array of elements. 613 * 614 * @since 1.0 615 * 616 * @return array 617 */ 618 function twentytwenty_get_elements_array() { 619 620 // The array is formatted like this: 621 // [key-in-saved-setting][sub-key-in-setting][css-property] = [elements]. 622 $elements = array( 623 'content' => array( 624 'accent' => array( 625 'color' => array( '.color-accent', '.color-accent-hover:hover', '.color-accent-hover:focus', '.has-accent-color', '.has-drop-cap:not(:focus):first-letter', '.wp-block-button.is-style-outline', 'a' ), 626 'border-color' => array( 'blockquote', '.border-color-accent', '.border-color-accent-hover:hover', '.border-color-accent-hover:focus' ), 627 'background' => array( 'button:not(.toggle)', '.button', '.faux-button', '.wp-block-button__link', '.wp-block-file__button', 'input[type="button"]', 'input[type="reset"]', 'input[type="submit"]' ), 628 'background-color' => array( '.bg-accent', '.bg-accent-hover:hover', '.bg-accent-hover:focus', '.has-accent-background-color', '.comment-reply-link' ), 629 'fill' => array( '.fill-children-accent', '.fill-children-accent *' ), 630 ), 631 'background' => array( 632 'color' => array( 'button', '.button', '.faux-button', '.wp-block-button__link', '.wp-block-button__link:active', '.wp-block-button__link:focus', '.wp-block-button__link:visited', '.wp-block-button__link:hover', '.wp-block-file__button', 'input[type="button"]', 'input[type="reset"]', 'input[type="submit"]', '.comment-reply-link' ), 633 'background' => array(), 634 ), 635 'text' => array( 636 'color' => array( 'body', '.entry-title a' ), 637 ), 638 'secondary' => array( 639 'color' => array( 'cite', 'figcaption', '.wp-caption-text', '.post-meta', '.entry-content .wp-block-archives li', '.entry-content .wp-block-categories li', '.entry-content .wp-block-latest-posts li', '.wp-block-latest-comments__comment-date', '.wp-block-latest-posts__post-date', '.wp-block-embed figcaption', '.wp-block-image figcaption', '.wp-block-pullquote cite', '.comment-metadata', '.comment-respond .comment-notes', '.comment-respond .logged-in-as', '.pagination .dots', '.entry-content hr:not(.has-background)', 'hr.styled-separator' ), 640 ), 641 'borders' => array( 642 'border-color' => array( 'pre', 'fieldset', 'input', 'textarea', 'table', 'table *', 'hr' ), 643 'background' => array( 'caption', 'code', 'code', 'kbd', 'samp', '.wp-block-table.is-style-stripes tbody tr:nth-child(odd)' ), 644 'border-bottom-color' => array( '.wp-block-table.is-style-stripes' ), 645 'border-top-color' => array( '.wp-block-latest-posts.is-grid li' ), 646 ), 647 ), 648 'header-footer' => array( 649 'accent' => array( 650 'color' => array( 'body:not(.overlay-header) .primary-menu > li > a', 'body:not(.overlay-header) .primary-menu > li > .icon', '.modal-menu a', '.footer-menu a, .footer-widgets a', '#site-footer .wp-block-button.is-style-outline', '.wp-block-pullquote:before', '.singular:not(.overlay-header) .entry-header a', '.archive-header a', '.header-footer-group .color-accent', '.header-footer-group .color-accent-hover:hover' ), 651 'background' => array( '.social-icons a', '#site-footer button:not(.toggle)', '#site-footer .button', '#site-footer .faux-button', '#site-footer .wp-block-button__link', '#site-footer .wp-block-file__button', '#site-footer input[type="button"]', '#site-footer input[type="reset"]', '#site-footer input[type="submit"]' ), 652 ), 653 'background' => array( 654 'color' => array( '.social-icons a', '.overlay-header .header-inner', '.primary-menu ul', '.header-footer-group button', '.header-footer-group .button', '.header-footer-group .faux-button', '.header-footer-group .wp-block-button:not(.is-style-outline) .wp-block-button__link', '.header-footer-group .wp-block-file__button', '.header-footer-group input[type="button"]', '.header-footer-group input[type="reset"]', '.header-footer-group input[type="submit"]' ), 655 'background' => array( '#site-header', '.footer-nav-widgets-wrapper', '#site-footer', '.menu-modal', '.menu-modal-inner', '.search-modal-inner', '.archive-header', '.singular .entry-header', '.singular .featured-media:before', '.wp-block-pullquote:before' ), 656 ), 657 'text' => array( 658 'color' => array( '.header-footer-group', 'body:not(.overlay-header) #site-header .toggle', '.menu-modal .toggle' ), 659 'background' => array( 'body:not(.overlay-header) .primary-menu ul' ), 660 'border-bottom-color' => array( 'body:not(.overlay-header) .primary-menu > li > ul:after' ), 661 'border-left-color' => array( 'body:not(.overlay-header) .primary-menu ul ul:after' ), 662 ), 663 'secondary' => array( 664 'color' => array( '.site-description', 'body:not(.overlay-header) .toggle-inner .toggle-text', '.widget .post-date', '.widget .rss-date', '.widget_archive li', '.widget_categories li', '.widget cite', '.widget_pages li', '.widget_meta li', '.widget_nav_menu li', '.powered-by-wordpress', '.to-the-top', '.singular .entry-header .post-meta', '.singular:not(.overlay-header) .entry-header .post-meta a' ), 665 ), 666 'borders' => array( 667 'border-color' => array( '.header-footer-group pre', '.header-footer-group fieldset', '.header-footer-group input', '.header-footer-group textarea', '.header-footer-group table', '.header-footer-group table *', '.footer-nav-widgets-wrapper', '#site-footer', '.menu-modal nav *', '.footer-widgets-outer-wrapper', '.footer-top' ), 668 'background' => array( '.header-footer-group table caption', 'body:not(.overlay-header) .header-inner .toggle-wrapper::before' ), 669 ), 670 ), 671 ); 302 672 303 673 /** 304 * Include a skip to content link at the top of the page so that users can bypass the menu. 305 */ 306 function twentytwenty_skip_link() { 307 echo '<a class="skip-link faux-button screen-reader-text" href="#site-content">' . __( 'Skip to the content', 'twentytwenty' ) . '</a>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- core trusts translations 308 } 309 310 add_action( 'wp_body_open', 'twentytwenty_skip_link', 5 ); 311 312 } 313 314 if ( ! function_exists( 'twentytwenty_sidebar_registration' ) ) { 315 316 /** 317 * Register widget areas. 318 * 319 * @link https://developer.wordpress.org/themes/functionality/sidebars/#registering-a-sidebar 320 */ 321 function twentytwenty_sidebar_registration() { 322 323 // Arguments used in all register_sidebar() calls. 324 $shared_args = array( 325 'before_title' => '<h2 class="widget-title subheading heading-size-3">', 326 'after_title' => '</h2>', 327 'before_widget' => '<div class="widget %2$s"><div class="widget-content">', 328 'after_widget' => '</div></div>', 329 ); 330 331 // Footer #1. 332 register_sidebar( 333 array_merge( 334 $shared_args, 335 array( 336 'name' => __( 'Footer #1', 'twentytwenty' ), 337 'id' => 'sidebar-1', 338 'description' => __( 'Widgets in this area will be displayed in the first column in the footer.', 'twentytwenty' ), 339 ) 340 ) 341 ); 342 343 // Footer #2. 344 register_sidebar( 345 array_merge( 346 $shared_args, 347 array( 348 'name' => __( 'Footer #2', 'twentytwenty' ), 349 'id' => 'sidebar-2', 350 'description' => __( 'Widgets in this area will be displayed in the second column in the footer.', 'twentytwenty' ), 351 ) 352 ) 353 ); 354 355 } 356 357 add_action( 'widgets_init', 'twentytwenty_sidebar_registration' ); 358 359 } 360 361 if ( ! function_exists( 'twentytwenty_block_editor_styles' ) ) { 362 363 /** 364 * Enqueue supplemental block editor styles. 365 */ 366 function twentytwenty_block_editor_styles() { 367 368 $css_dependencies = array(); 369 370 // Enqueue the editor styles. 371 wp_enqueue_style( 'twentytwenty-block-editor-styles', get_theme_file_uri( '/assets/css/editor-style-block.css' ), $css_dependencies, wp_get_theme()->get( 'Version' ), 'all' ); 372 wp_style_add_data( 'twentytwenty-block-editor-styles', 'rtl', 'replace' ); 373 374 // Add inline style from the Customizer. 375 wp_add_inline_style( 'twentytwenty-block-editor-styles', twentytwenty_get_customizer_css( 'block-editor' ) ); 376 377 } 378 379 add_action( 'enqueue_block_editor_assets', 'twentytwenty_block_editor_styles', 1, 1 ); 380 381 } 382 383 if ( ! function_exists( 'twentytwenty_classic_editor_styles' ) ) { 384 385 /** 386 * Enqueue classic editor styles. 387 */ 388 function twentytwenty_classic_editor_styles() { 389 390 $classic_editor_styles = array( 391 '/assets/css/editor-style-classic.css', 392 ); 393 394 add_editor_style( $classic_editor_styles ); 395 396 } 397 398 add_action( 'init', 'twentytwenty_classic_editor_styles' ); 399 400 } 401 402 if ( ! function_exists( 'twentytwenty_add_classic_editor_customizer_styles' ) ) { 403 404 /** 405 * Output Customizer Settings in the Classic Editor. 406 * Adds styles to the head of the TinyMCE iframe. Kudos to @Otto42 for the original solution. 407 * 408 * @param array $mce_init TinyMCE styles. 409 */ 410 function twentytwenty_add_classic_editor_customizer_styles( $mce_init ) { 411 412 $styles = twentytwenty_get_customizer_css( 'classic-editor' ); 413 414 if ( ! isset( $mce_init['content_style'] ) ) { 415 $mce_init['content_style'] = $styles . ' '; 416 } else { 417 $mce_init['content_style'] .= ' ' . $styles . ' '; 418 } 419 420 return $mce_init; 421 422 } 423 424 add_filter( 'tiny_mce_before_init', 'twentytwenty_add_classic_editor_customizer_styles' ); 425 426 } 427 428 if ( ! function_exists( 'twentytwenty_block_editor_settings' ) ) { 429 430 /** 431 * Block Editor Settings. 432 * Add custom colors and font sizes to the block editor. 433 */ 434 function twentytwenty_block_editor_settings() { 435 436 // Block Editor Palette. 437 $editor_color_palette = array( 438 array( 439 'name' => esc_html__( 'Accent Color', 'twentytwenty' ), 440 'slug' => 'accent', 441 'color' => twentytwenty_get_color_for_area( 'content', 'accent' ), 442 ), 443 array( 444 'name' => esc_html__( 'Secondary', 'twentytwenty' ), 445 'slug' => 'secondary', 446 'color' => twentytwenty_get_color_for_area( 'content', 'secondary' ), 447 ), 448 array( 449 'name' => esc_html__( 'Subtle Background', 'twentytwenty' ), 450 'slug' => 'subtle-background', 451 'color' => twentytwenty_get_color_for_area( 'content', 'borders' ), 452 ), 453 ); 454 455 // Get the color options. 456 $accent_color_options = TwentyTwenty_Customize::get_color_options(); 457 458 // Loop over them and construct an array for the editor-color-palette. 459 if ( $accent_color_options ) { 460 foreach ( $accent_color_options as $color_option_name => $color_option ) { 461 $editor_color_palette[] = array( 462 'name' => $color_option['label'], 463 'slug' => $color_option['slug'], 464 'color' => get_theme_mod( $color_option_name, $color_option['default'] ), 465 ); 466 } 467 } 468 469 // Add the background option. 470 $background_color = get_theme_mod( 'background_color' ); 471 if ( ! $background_color ) { 472 $background_color_arr = get_theme_support( 'custom-background' ); 473 $background_color = $background_color_arr[0]['default-color']; 474 } 475 $editor_color_palette[] = array( 476 'name' => __( 'Background Color', 'twentytwenty' ), 477 'slug' => 'background', 478 'color' => '#' . $background_color, 479 ); 480 481 // If we have accent colors, add them to the block editor palette. 482 if ( $editor_color_palette ) { 483 add_theme_support( 'editor-color-palette', $editor_color_palette ); 484 } 485 486 // the block editor Font Sizes. 487 add_theme_support( 488 'editor-font-sizes', 489 array( 490 array( 491 'name' => _x( 'Small', 'Name of the small font size in the block editor', 'twentytwenty' ), 492 'shortName' => _x( 'S', 'Short name of the small font size in the block editor.', 'twentytwenty' ), 493 'size' => 16, 494 'slug' => 'small', 495 ), 496 array( 497 'name' => _x( 'Regular', 'Name of the regular font size in the block editor', 'twentytwenty' ), 498 'shortName' => _x( 'M', 'Short name of the regular font size in the block editor.', 'twentytwenty' ), 499 'size' => 18, 500 'slug' => 'regular', 501 ), 502 array( 503 'name' => _x( 'Large', 'Name of the large font size in the block editor', 'twentytwenty' ), 504 'shortName' => _x( 'L', 'Short name of the large font size in the block editor.', 'twentytwenty' ), 505 'size' => 24, 506 'slug' => 'large', 507 ), 508 array( 509 'name' => _x( 'Larger', 'Name of the larger font size in the block editor', 'twentytwenty' ), 510 'shortName' => _x( 'XL', 'Short name of the larger font size in the block editor.', 'twentytwenty' ), 511 'size' => 32, 512 'slug' => 'larger', 513 ), 514 ) 515 ); 516 517 // If we have a dark background color then add support for dark editor style. 518 // We can determine if the background color is dark by checking if the text-color is white. 519 if ( '#ffffff' === strtolower( twentytwenty_get_color_for_area( 'content', 'text' ) ) ) { 520 add_theme_support( 'dark-editor-style' ); 521 } 522 523 } 524 525 add_action( 'after_setup_theme', 'twentytwenty_block_editor_settings' ); 526 527 } 528 529 if ( ! function_exists( 'twentytwenty_read_more_tag' ) ) { 530 531 /** 532 * Read More Link 533 * Overwrite default (more ...) tag 534 */ 535 function twentytwenty_read_more_tag() { 536 return sprintf( 537 '<a href="%1$s" class="more-link faux-button">%2$s <span class="screen-reader-text">"%3$s"</span></a>', 538 esc_url( get_permalink( get_the_ID() ) ), 539 esc_html__( 'Continue reading', 'twentytwenty' ), 540 get_the_title( get_the_ID() ) 541 ); 542 } 543 add_filter( 'the_content_more_link', 'twentytwenty_read_more_tag' ); 544 545 } 546 547 if ( ! function_exists( 'twentytwenty_customize_controls_enqueue_scripts' ) ) { 548 /** 549 * Enqueues scripts for customizer controls & settings. 550 * 551 * @since 1.0.0 552 * 553 * @return void 554 */ 555 function twentytwenty_customize_controls_enqueue_scripts() { 556 $theme_version = wp_get_theme()->get( 'Version' ); 557 558 // Add script for color calculations. 559 wp_enqueue_script( 'twentytwenty-color-calculations', get_template_directory_uri() . '/assets/js/color-calculations.js', array( 'wp-color-picker' ), $theme_version, false ); 560 561 // Add script for controls. 562 wp_enqueue_script( 'twentytwenty-customize-controls', get_template_directory_uri() . '/assets/js/customize-controls.js', array( 'twentytwenty-color-calculations', 'customize-controls', 'underscore', 'jquery' ), $theme_version, false ); 563 wp_localize_script( 'twentytwenty-customize-controls', 'backgroundColors', twentytwenty_get_customizer_color_vars() ); 564 } 565 566 add_action( 'customize_controls_enqueue_scripts', 'twentytwenty_customize_controls_enqueue_scripts' ); 567 } 568 569 if ( ! function_exists( 'twentytwenty_customize_preview_init' ) ) { 570 /** 571 * Enqueue scripts for the customizer preview. 572 * 573 * @since 1.0.0 574 * 575 * @return void 576 */ 577 function twentytwenty_customize_preview_init() { 578 $theme_version = wp_get_theme()->get( 'Version' ); 579 580 wp_enqueue_script( 'twentytwenty-customize-preview', get_theme_file_uri( '/assets/js/customize-preview.js' ), array( 'customize-preview', 'jquery' ), $theme_version, true ); 581 wp_localize_script( 'twentytwenty-customize-preview', 'backgroundColors', twentytwenty_get_customizer_color_vars() ); 582 wp_localize_script( 'twentytwenty-customize-preview', 'previewElements', twentytwenty_get_elements_array() ); 583 } 584 585 add_action( 'customize_preview_init', 'twentytwenty_customize_preview_init' ); 586 } 587 588 if ( ! function_exists( 'twentytwenty_get_color_for_area' ) ) { 589 /** 590 * Get accessible color for an area. 591 * 592 * @since 1.0.0 593 * 594 * @param string $area The area we want to get the colors for. 595 * @param string $context Can be 'text' or 'accent'. 596 * @return string Returns a HEX color. 597 */ 598 function twentytwenty_get_color_for_area( $area = 'content', $context = 'text' ) { 599 600 // Get the value from the theme-mod. 601 $settings = get_theme_mod( 602 'accent_accessible_colors', 603 array( 604 'content' => array( 605 'text' => '#000000', 606 'accent' => '#cd2653', 607 'secondary' => '#6d6d6d', 608 'borders' => '#dcd7ca', 609 ), 610 'header-footer' => array( 611 'text' => '#000000', 612 'accent' => '#cd2653', 613 'secondary' => '#6d6d6d', 614 'borders' => '#dcd7ca', 615 ), 616 ) 617 ); 618 619 // If we have a value return it. 620 if ( isset( $settings[ $area ] ) && isset( $settings[ $area ][ $context ] ) ) { 621 return $settings[ $area ][ $context ]; 622 } 623 624 // Return false if the option doesn't exist. 625 return false; 626 } 627 } 628 629 if ( ! function_exists( 'twentytwenty_get_customizer_color_vars' ) ) { 630 631 /** 632 * Returns an array of variables for the customizer preview. 633 * 634 * @since 1.0.0 635 * 636 * @return array 637 */ 638 function twentytwenty_get_customizer_color_vars() { 639 $colors = array( 640 'content' => array( 641 'setting' => 'background_color', 642 ), 643 'header-footer' => array( 644 'setting' => 'header_footer_background_color', 645 ), 646 ); 647 return $colors; 648 } 649 } 650 651 if ( ! function_exists( 'twentytwenty_get_elements_array' ) ) { 652 653 /** 654 * Get an array of elements. 655 * 656 * @since 1.0 657 * 658 * @return array 659 */ 660 function twentytwenty_get_elements_array() { 661 662 // The array is formatted like this: 663 // [key-in-saved-setting][sub-key-in-setting][css-property] = [elements]. 664 $elements = array( 665 'content' => array( 666 'accent' => array( 667 'color' => array( '.color-accent', '.color-accent-hover:hover', '.has-accent-color', '.has-drop-cap:not(:focus):first-letter', '.wp-block-button.is-style-outline', 'a' ), 668 'border-color' => array( 'blockquote', '.border-color-accent', '.border-color-accent-hover:hover' ), 669 'background' => array( 'button:not(.toggle)', '.button', '.faux-button', '.wp-block-button__link', '.wp-block-file__button', 'input[type="button"]', 'input[type="reset"]', 'input[type="submit"]' ), 670 'background-color' => array( '.bg-accent', '.bg-accent-hover:hover', '.has-accent-background-color', '.comment-reply-link', '.edit-comment-link' ), 671 'fill' => array( '.fill-children-accent', '.fill-children-accent *' ), 672 ), 673 'background' => array( 674 'color' => array( 'button', '.button', '.faux-button', '.wp-block-button__link', '.wp-block-file__button', 'input[type="button"]', 'input[type="reset"]', 'input[type="submit"]', '.comment-reply-link', '.edit-comment-link' ), 675 'background' => array(), 676 ), 677 'text' => array( 678 'color' => array( 'body', '.entry-title a' ), 679 ), 680 'secondary' => array( 681 'color' => array( 'cite', 'figcaption', '.wp-caption-text', '.post-meta', '.entry-content .wp-block-archives li', '.entry-content .wp-block-categories li', '.entry-content .wp-block-latest-posts li', '.wp-block-latest-comments__comment-date', '.wp-block-latest-posts__post-date', '.wp-block-embed figcaption', '.wp-block-image figcaption', '.wp-block-pullquote cite', '.comment-metadata', '.comment-respond .comment-notes', '.comment-respond .logged-in-as', '.pagination .dots' ), 682 ), 683 'borders' => array( 684 'border-color' => array( 'pre', 'fieldset', 'input', 'textarea', 'table', 'table *' ), 685 'background' => array( 'caption', 'code', 'code', 'kbd', 'samp' ), 686 'border-bottom-color' => array( '.wp-block-table.is-style-stripes' ), 687 'border-top-color' => array( '.wp-block-latest-posts.is-grid li' ), 688 'color' => array( 'hr:not(.is-style-dots):not(.reset-hr)' ), 689 ), 690 ), 691 'header-footer' => array( 692 'accent' => array( 693 'color' => array( 'body:not(.overlay-header) .primary-menu > li > a', 'body:not(.overlay-header) .primary-menu > li > .icon', '.modal-menu a', '.footer-menu a, .footer-widgets a', '#site-footer .wp-block-button.is-style-outline', '.wp-block-pullquote:before', '.singular .entry-header a', '.archive-header a', '.header-footer-group .color-accent', '.header-footer-group .color-accent-hover:hover' ), 694 'background' => array( '.social-icons a', '#site-footer button:not(.toggle)', '#site-footer .button', '#site-footer .faux-button', '#site-footer .wp-block-button__link', '#site-footer .wp-block-file__button', '#site-footer input[type="button"]', '#site-footer input[type="reset"]', '#site-footer input[type="submit"]' ), 695 ), 696 'background' => array( 697 'color' => array( '.social-icons a', '.overlay-header:not(.showing-menu-modal) .header-inner', '.primary-menu ul', '.header-footer-group button', '.header-footer-group .button', '.header-footer-group .faux-button', '.header-footer-group .wp-block-button:not(.is-style-outline) .wp-block-button__link', '.header-footer-group .wp-block-file__button', '.header-footer-group input[type="button"]', '.header-footer-group input[type="reset"]', '.header-footer-group input[type="submit"]' ), 698 'background' => array( '#site-header', '#site-footer', '.menu-modal', '.menu-modal-inner', '.search-modal-inner', '.archive-header', '.singular .entry-header', '.singular .featured-media:before', '.wp-block-pullquote:before' ), 699 ), 700 'text' => array( 701 'color' => array( '.header-footer-group', 'body:not(.overlay-header) #site-header .toggle', '.menu-modal .toggle' ), 702 'background' => array( 'body:not(.overlay-header) .primary-menu ul' ), 703 'border-bottom-color' => array( 'body:not(.overlay-header) .primary-menu > li > ul:after' ), 704 'border-left-color' => array( 'body:not(.overlay-header) .primary-menu ul ul:after' ), 705 ), 706 'secondary' => array( 707 'color' => array( '.site-description', '.toggle-inner .toggle-text', '.widget .post-date', '.widget .rss-date', '.widget_archive li', '.widget_categories li', '.widget cite', '.widget_pages li', '.widget_meta li', '.widget_nav_menu li', '.powered-by-wordpress', '.to-the-top', '.singular .entry-header .post-meta', '.singular .entry-header .post-meta a' ), 708 ), 709 'borders' => array( 710 'border-color' => array( '.header-footer-group pre', '.header-footer-group fieldset', '.header-footer-group input', '.header-footer-group textarea', '.header-footer-group table', '.header-footer-group table *', '.menu-modal nav *', '.footer-widgets-outer-wrapper', '.footer-top' ), 711 'background' => array( '.header-footer-group table caption', 'body:not(.overlay-header) .header-inner .toggle-wrapper::before' ), 712 'border-bottom-color' => array( '.wp-block-table.is-style-stripes' ), 713 'border-top-color' => array( '.wp-block-latest-posts.is-grid li' ), 714 ), 715 ), 716 ); 717 718 return apply_filters( 'twentytwenty_get_elements_array', $elements ); 719 } 720 } 674 * Filters Twenty Twenty theme elements 675 * 676 * @since 1.0.0 677 * 678 * @param array Array of elements 679 */ 680 return apply_filters( 'twentytwenty_get_elements_array', $elements ); 681 }
Note: See TracChangeset
for help on using the changeset viewer.