Ticket #21785: 21785-customize-header.2.diff
File 21785-customize-header.2.diff, 48.2 KB (added by , 11 years ago) |
---|
-
src/wp-admin/css/customize-controls.css
diff --git src/wp-admin/css/customize-controls.css src/wp-admin/css/customize-controls.css index c76a6e5..482f002 100644
body { 455 455 -webkit-overflow-scrolling: touch; 456 456 } 457 457 458 /** Header control **/ 459 460 #customize-control-header_image .current { 461 margin-bottom: 8px; 462 } 463 464 #customize-control-header_image .uploaded { 465 margin-bottom: 18px; 466 } 467 468 /* Header control: current image container */ 469 470 #customize-control-header_image .current .container { 471 overflow: hidden; 472 border-radius: 2px; 473 } 474 475 #customize-control-header_image .placeholder { 476 width: 100%; 477 position: relative; 478 background: #262626; 479 text-align: center; 480 cursor: default; 481 } 482 483 #customize-control-header_image .inner { 484 display: none; 485 position: absolute; 486 width: 100%; 487 height: 18px; 488 margin-top: -9px; 489 top: 50%; 490 color: #eee; 491 } 492 493 /* Header control: overlay "close" button */ 494 495 #customize-control-header_image .header-view { 496 position: relative; 497 } 498 499 #customize-control-header_image .uploaded .header-view .close { 500 font-size: 2em; 501 color: grey; 502 position: absolute; 503 visibility: hidden; 504 top: 10px; 505 right: 10px; 506 z-index: 1; 507 width: 20px; 508 height: 20px; 509 cursor: pointer; 510 } 511 512 #customize-control-header_image .uploaded .header-view .close:hover { 513 color: black; 514 text-shadow: 515 -1px -1px 0 #fff, 516 1px -1px 0 #fff, 517 -1px 1px 0 #fff, 518 1px 1px 0 #fff; 519 } 520 521 #customize-control-header_image .header-view:hover .close { 522 visibility: visible; 523 } 524 525 /* Header control: randomiz(s)er */ 526 527 #customize-control-header_image .random.placeholder { 528 cursor: pointer; 529 border-radius: 2px; 530 height: 40px; 531 } 532 533 #customize-control-header_image .random .inner { 534 display: block; 535 } 536 537 #customize-control-header_image .dice { 538 font-size: 16px; 539 vertical-align: -1px; 540 } 541 542 #customize-control-header_image .placeholder:hover .dice { 543 -webkit-animation: dice-color-change 3s infinite; 544 -moz-animation: dice-color-change 3s infinite; 545 -ms-animation: dice-color-change 3s infinite; 546 animation: dice-color-change 3s infinite; 547 } 548 549 @-webkit-keyframes dice-color-change { 550 0% { color: #d4b146; } 551 50% { color: #ef54b0; } 552 75% { color: #7190d3; } 553 100% { color: #d4b146; } 554 } 555 556 @-moz-keyframes dice-color-change { 557 0% { color: #d4b146; } 558 50% { color: #ef54b0; } 559 75% { color: #7190d3; } 560 100% { color: #d4b146; } 561 } 562 563 @-ms-keyframes dice-color-change { 564 0% { color: #d4b146; } 565 50% { color: #ef54b0; } 566 75% { color: #7190d3; } 567 100% { color: #d4b146; } 568 } 569 570 @keyframes dice-color-change { 571 0% { color: #d4b146; } 572 50% { color: #ef54b0; } 573 75% { color: #7190d3; } 574 100% { color: #d4b146; } 575 } 576 577 /* Header control: actions and choices */ 578 579 #customize-control-header_image .actions { 580 margin-bottom: 32px; 581 } 582 583 #customize-control-header_image .choice { 584 position: relative; 585 display: block; 586 margin-bottom: 9px; 587 } 588 589 #customize-control-header_image .choice.random:before { 590 position: absolute; 591 content: attr(data-label); 592 left: 0; 593 top: 0; 594 } 595 596 #customize-control-header_image .uploaded div:last-child > .choice { 597 margin-bottom: 0; 598 } 599 600 #customize-control-header_image .choices hr { 601 visibility: hidden; 602 } 603 604 #customize-control-header_image img { 605 width: 100%; 606 border-radius: 2px; 607 } 608 609 #customize-control-header_image .remove { 610 float: left; 611 margin-right: 3px; 612 } 613 614 #customize-control-header_image .new { 615 float: right; 616 } 617 618 458 619 /** Handle cheaters. */ 459 620 body.cheatin { 460 621 min-width: 0; -
src/wp-admin/custom-header.php
diff --git src/wp-admin/custom-header.php src/wp-admin/custom-header.php index 633dc65..a48caad 100644
class Custom_Image_Header { 43 43 var $default_headers = array(); 44 44 45 45 /** 46 * Holds custom headers uploaded by the user 46 * Holds custom headers uploaded by the user. 47 47 * 48 48 * @var array 49 49 * @since 3.2.0 … … class Custom_Image_Header { 72 72 $this->admin_header_callback = $admin_header_callback; 73 73 $this->admin_image_div_callback = $admin_image_div_callback; 74 74 75 if ( current_theme_supports( 'custom-header' ) ) { 76 add_action( 'customize_save_after', array( $this, 'set_last_used' ) ); 77 add_action( 'wp_ajax_header_crop', array( $this, 'ajax_header_crop' ) ); 78 add_action( 'wp_ajax_header_add', array( $this, 'ajax_header_add' ) ); 79 add_action( 'wp_ajax_header_remove', array( $this, 'ajax_header_remove' ) ); 80 } 81 75 82 add_action( 'admin_menu', array( $this, 'init' ) ); 76 83 } 77 84 … … class Custom_Image_Header { 93 100 add_action("admin_head-$page", array($this, 'js'), 50); 94 101 if ( $this->admin_header_callback ) 95 102 add_action("admin_head-$page", $this->admin_header_callback, 51); 103 96 104 } 97 105 98 106 /** … … wp_nonce_field( 'custom-header-options', '_wpnonce-custom-header-options' ); ?> 819 827 $attachment_id = absint( $_POST['attachment_id'] ); 820 828 $original = get_attached_file($attachment_id); 821 829 822 823 $max_width = 0; 824 // For flex, limit size of image displayed to 1500px unless theme says otherwise 825 if ( current_theme_supports( 'custom-header', 'flex-width' ) ) 826 $max_width = 1500; 827 828 if ( current_theme_supports( 'custom-header', 'max-width' ) ) 829 $max_width = max( $max_width, get_theme_support( 'custom-header', 'max-width' ) ); 830 $max_width = max( $max_width, get_theme_support( 'custom-header', 'width' ) ); 831 832 if ( ( current_theme_supports( 'custom-header', 'flex-height' ) && ! current_theme_supports( 'custom-header', 'flex-width' ) ) || $_POST['width'] > $max_width ) 833 $dst_height = absint( $_POST['height'] * ( $max_width / $_POST['width'] ) ); 834 elseif ( current_theme_supports( 'custom-header', 'flex-height' ) && current_theme_supports( 'custom-header', 'flex-width' ) ) 835 $dst_height = absint( $_POST['height'] ); 836 else 837 $dst_height = get_theme_support( 'custom-header', 'height' ); 838 839 if ( ( current_theme_supports( 'custom-header', 'flex-width' ) && ! current_theme_supports( 'custom-header', 'flex-height' ) ) || $_POST['width'] > $max_width ) 840 $dst_width = absint( $_POST['width'] * ( $max_width / $_POST['width'] ) ); 841 elseif ( current_theme_supports( 'custom-header', 'flex-width' ) && current_theme_supports( 'custom-header', 'flex-height' ) ) 842 $dst_width = absint( $_POST['width'] ); 843 else 844 $dst_width = get_theme_support( 'custom-header', 'width' ); 830 extract( $this->get_header_dimensions( array( 831 'width' => $_POST['width'], 832 'height' => $_POST['height'], 833 ) ) ); 845 834 846 835 if ( empty( $_POST['skip-cropping'] ) ) 847 836 $cropped = wp_crop_image( $attachment_id, (int) $_POST['x1'], (int) $_POST['y1'], (int) $_POST['width'], (int) $_POST['height'], $dst_width, $dst_height ); … … wp_nonce_field( 'custom-header-options', '_wpnonce-custom-header-options' ); ?> 856 845 /** This filter is documented in wp-admin/custom-header.php */ 857 846 $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication 858 847 859 $parent = get_post($attachment_id); 860 $parent_url = $parent->guid; 861 $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url ); 862 863 $size = @getimagesize( $cropped ); 864 $image_type = ( $size ) ? $size['mime'] : 'image/jpeg'; 848 $object = $this->create_attachment_object( $cropped, $attachment_id ); 865 849 866 // Construct the object array867 $object = array(868 'ID' => $attachment_id,869 'post_title' => basename($cropped),870 'post_content' => $url,871 'post_mime_type' => $image_type,872 'guid' => $url,873 'context' => 'custom-header'874 );875 850 if ( ! empty( $_POST['create-new-attachment'] ) ) 876 851 unset( $object['ID'] ); 877 852 878 853 // Update the attachment 879 $attachment_id = wp_insert_attachment( $object, $cropped ); 880 wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $cropped ) ); 854 $attachment_id = $this->insert_attachment( $object, $cropped ); 881 855 882 856 $width = $dst_width; 883 857 $height = $dst_height; 858 $url = $object['guid']; 884 859 $this->set_header_image( compact( 'url', 'attachment_id', 'width', 'height' ) ); 885 860 886 861 // cleanup … … wp_nonce_field( 'custom-header-options', '_wpnonce-custom-header-options' ); ?> 1041 1016 set_theme_mod( 'header_image', $default ); 1042 1017 set_theme_mod( 'header_image_data', (object) $default_data ); 1043 1018 } 1019 1020 /** 1021 * Calculate dst_width and dst_height based on what the currently selected theme supports. 1022 * 1023 * @return array dst_height and dst_width of header image. 1024 */ 1025 final public function get_header_dimensions( $dimensions ) { 1026 $max_width = 0; 1027 $width = absint( $dimensions['width'] ); 1028 $height = absint( $dimensions['height'] ); 1029 $theme_height = get_theme_support( 'custom-header', 'height' ); 1030 $theme_width = get_theme_support( 'custom-header', 'width' ); 1031 $has_flex_width = current_theme_supports( 'custom-header', 'flex-width' ); 1032 $has_flex_height = current_theme_supports( 'custom-header', 'flex-height' ); 1033 $has_max_width = current_theme_supports( 'custom-header', 'max-width' ) ; 1034 $dst = array(); 1035 1036 // For flex, limit size of image displayed to 1500px unless theme says otherwise 1037 if ( $has_flex_width ) 1038 $max_width = 1500; 1039 1040 if ( $has_max_width ) 1041 $max_width = max( $max_width, get_theme_support( 'custom-header', 'max-width' ) ); 1042 $max_width = max( $max_width, $theme_width ); 1043 1044 if ( $has_flex_height && ( ! $has_flex_width || $width > $max_width ) ) 1045 $dst['dst_height'] = absint( $height * ( $max_width / $width ) ); 1046 elseif ( $has_flex_height && $has_flex_width ) 1047 $dst['dst_height'] = $height; 1048 else 1049 $dst['dst_height'] = $theme_height; 1050 1051 if ( $has_flex_width && ( ! $has_flex_height || $width > $max_width ) ) 1052 $dst['dst_width'] = absint( $width * ( $max_width / $width ) ); 1053 elseif ( $has_flex_width && $has_flex_height ) 1054 $dst['dst_width'] = $width; 1055 else 1056 $dst['dst_width'] = $theme_width; 1057 1058 return $dst; 1059 } 1060 1061 /** 1062 * Create an attachment 'object'. 1063 * 1064 * @param string $cropped Cropped image URL. 1065 * @param int $parent_attachment_id Attachment ID of parent image. 1066 * 1067 * @return array Attachment object. 1068 */ 1069 final public function create_attachment_object( $cropped, $parent_attachment_id ) { 1070 $parent = get_post( $parent_attachment_id ); 1071 $parent_url = $parent->guid; 1072 $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url ); 1073 1074 $size = @getimagesize( $cropped ); 1075 $image_type = ( $size ) ? $size['mime'] : 'image/jpeg'; 1076 1077 $object = array( 1078 'ID' => $parent_attachment_id, 1079 'post_title' => basename($cropped), 1080 'post_content' => $url, 1081 'post_mime_type' => $image_type, 1082 'guid' => $url, 1083 'context' => 'custom-header' 1084 ); 1085 1086 return $object; 1087 } 1088 1089 /** 1090 * Insert an attachment & its metadata. 1091 * 1092 * @param array $object Attachment object. 1093 * @param string $cropped Cropped image URL. 1094 * 1095 * @return int Attachment ID. 1096 */ 1097 final public function insert_attachment( $object, $cropped ) { 1098 $attachment_id = wp_insert_attachment( $object, $cropped ); 1099 $metadata = wp_generate_attachment_metadata( $attachment_id, $cropped ); 1100 /** 1101 * Allows us to insert custom meta data for an attachment. 1102 * 1103 */ 1104 $metadata = apply_filters( 'wp_header_image_attachment_metadata', $metadata ); 1105 wp_update_attachment_metadata( $attachment_id, $metadata ); 1106 return $attachment_id; 1107 } 1108 1109 function ajax_check_permission( $name, $nonce, $attachment_id = null ) { 1110 $name = $attachment_id === null ? $name : "${name}_${attachment_id}"; 1111 if ( ! isset( $nonce ) || ! wp_verify_nonce( $nonce, $name ) || 1112 ! current_user_can('edit_theme_options') ) { 1113 wp_die( __( 'Cheatin’ uh?' ) ); 1114 } 1115 } 1116 1117 /** 1118 * Gets attachment uploaded by Media Manager, crops it, then saves it as a 1119 * new object. Returns JSON-encoded object details. 1120 */ 1121 function ajax_header_crop() { 1122 $data = $_POST['data']; 1123 $this->ajax_check_permission( 'crop-image', $data['nonces']['crop'], $data['id'] ); 1124 if ( ! current_theme_supports( 'custom-header', 'uploads' ) ) 1125 wp_die( __( 'Cheatin’ uh?' ) ); 1126 1127 if ( ! empty( $data['skip-cropping'] ) && ! ( current_theme_supports( 'custom-header', 'flex-height' ) || current_theme_supports( 'custom-header', 'flex-width' ) ) ) 1128 wp_die( __( 'Cheatin’ uh?' ) ); 1129 1130 $crop_details = $data['cropDetails']; 1131 1132 $dimensions = $this->get_header_dimensions( array( 1133 'width' => $crop_details['width'], 1134 'height' => $crop_details['height'], 1135 ) ); 1136 1137 $attachment_id = absint( $data['id'] ); 1138 1139 $cropped = wp_crop_image( $attachment_id, (int) $crop_details['x1'], (int) $crop_details['y1'], (int) $crop_details['width'], (int) $crop_details['height'], (int) $dimensions['dst_width'], (int) $dimensions['dst_height'] ); 1140 1141 if ( ! $cropped || is_wp_error( $cropped ) ) 1142 wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) ); 1143 1144 $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication 1145 1146 $object = $this->create_attachment_object( $cropped, $attachment_id ); 1147 1148 unset( $object['ID'] ); 1149 1150 $new_attachment_id = $this->insert_attachment( $object, $cropped ); 1151 1152 $object['attachment_id'] = $new_attachment_id; 1153 $object['width'] = $dimensions['dst_width']; 1154 $object['height'] = $dimensions['dst_height']; 1155 1156 echo json_encode($object); 1157 wp_die(); 1158 } 1159 1160 /** 1161 * Given an attachment ID for a header image, updates its "last used" 1162 * timestamp to now. 1163 * 1164 * Triggered when the user tries adds a new header image from the 1165 * Media Manager, even if s/he doesn't save that change. 1166 */ 1167 function ajax_header_add() { 1168 $data = $_POST['data']; 1169 $this->ajax_check_permission( 'header-add', $_REQUEST['nonce'] ); 1170 1171 $attachment_id = absint( $data['attachment_id'] ); 1172 if ( $attachment_id < 1 ) 1173 return; 1174 1175 $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet(); 1176 update_post_meta( $attachment_id, $key, time() ); 1177 update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', get_stylesheet() ); 1178 1179 wp_die(); 1180 } 1181 1182 /** 1183 * Given an attachment ID for a header image, unsets it as a user-uploaded 1184 * header image for the current theme. 1185 * 1186 * Triggered when the user clicks the overlay "X" button next to each image 1187 * choice in the Customizer's Header tool. 1188 */ 1189 function ajax_header_remove() { 1190 $data = $_POST['data']; 1191 $this->ajax_check_permission( 'header-remove', $_REQUEST['nonce'] ); 1192 1193 $attachment_id = absint( $data['attachment_id'] ); 1194 if ( $attachment_id < 1 ) 1195 return; 1196 1197 $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet(); 1198 delete_post_meta( $attachment_id, $key ); 1199 delete_post_meta( $attachment_id, '_wp_attachment_is_custom_header', get_stylesheet() ); 1200 1201 wp_die(); 1202 } 1203 1204 function set_last_used( $manager ) { 1205 $data = $manager->get_setting( 'header_image_data' )->post_value(); 1206 1207 if ( !isset( $data['attachment_id'] ) ) 1208 return; 1209 1210 $attachment_id = $data['attachment_id']; 1211 $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet(); 1212 update_post_meta( $attachment_id, $key, time() ); 1213 } 1044 1214 } -
src/wp-admin/js/customize-controls.js
diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js index 3a05ad4..aed0f6d 100644
1 /* globals _wpCustomizeHeader, _wpMediaViewsL10n */ 1 2 (function( exports, $ ){ 2 3 var api = wp.customize; 3 4 … … 306 307 } 307 308 }); 308 309 310 api.HeaderControl = api.Control.extend({ 311 ready: function() { 312 this.btnRemove = $('.actions .remove'); 313 this.btnNew = $('.actions .new'); 314 315 _.bindAll(this, 'openMM', 'removeImage'); 316 317 this.btnNew.on( 'click', this.openMM ); 318 this.btnRemove.on( 'click', this.removeImage ); 319 320 api.HeaderTool.currentHeader = new api.HeaderTool.ImageModel(); 321 322 new api.HeaderTool.CurrentView({ 323 model: api.HeaderTool.currentHeader, 324 el: '.current .container' 325 }); 326 327 new api.HeaderTool.ChoiceListView({ 328 collection: api.HeaderTool.UploadsList = new api.HeaderTool.ChoiceList(), 329 el: '.choices .uploaded .list' 330 }); 331 332 new api.HeaderTool.ChoiceListView({ 333 collection: api.HeaderTool.DefaultsList = new api.HeaderTool.DefaultsList(), 334 el: '.choices .default .list' 335 }); 336 337 api.HeaderTool.combinedList = api.HeaderTool.CombinedList = new api.HeaderTool.CombinedList([ 338 api.HeaderTool.UploadsList, 339 api.HeaderTool.DefaultsList 340 ]); 341 }, 342 343 /** 344 * Returns a set of options, computed from the attached image data and 345 * theme-specific data, to be fed to the imgAreaSelect plugin in 346 * wp.media.view.Cropper. 347 * 348 * @param {wp.media.model.Attachment} attachment 349 * @param {wp.media.controller.Cropper} controller 350 * @returns {Object} Options 351 */ 352 calculateImageSelectOptions: function(attachment, controller) { 353 var xInit = parseInt(_wpCustomizeHeader.data.width, 10), 354 yInit = parseInt(_wpCustomizeHeader.data.height, 10), 355 flexWidth = !! parseInt(_wpCustomizeHeader.data['flex-width'], 10), 356 flexHeight = !! parseInt(_wpCustomizeHeader.data['flex-height'], 10), 357 ratio, xImg, yImg, realHeight, realWidth, 358 imgSelectOptions; 359 360 realWidth = attachment.get('width'); 361 realHeight = attachment.get('height'); 362 363 this.headerImage = new api.HeaderTool.ImageModel(); 364 this.headerImage.set({ 365 themeWidth: xInit, 366 themeHeight: yInit, 367 themeFlexWidth: flexWidth, 368 themeFlexHeight: flexHeight, 369 imageWidth: realWidth, 370 imageHeight: realHeight 371 }); 372 373 controller.set( 'canSkipCrop', ! this.headerImage.shouldBeCropped() ); 374 375 ratio = xInit / yInit; 376 xImg = realWidth; 377 yImg = realHeight; 378 379 if ( xImg / yImg > ratio ) { 380 yInit = yImg; 381 xInit = yInit * ratio; 382 } else { 383 xInit = xImg; 384 yInit = xInit / ratio; 385 } 386 387 imgSelectOptions = { 388 handles: true, 389 keys: true, 390 instance: true, 391 persistent: true, 392 parent: this.$el, 393 imageWidth: realWidth, 394 imageHeight: realHeight, 395 x1: 0, 396 y1: 0, 397 x2: xInit, 398 y2: yInit 399 }; 400 401 if (flexHeight === false && flexWidth === false) { 402 imgSelectOptions.aspectRatio = xInit + ':' + yInit; 403 } 404 if (flexHeight === false ) { 405 imgSelectOptions.maxHeight = yInit; 406 } 407 if (flexWidth === false ) { 408 imgSelectOptions.maxWidth = xInit; 409 } 410 411 return imgSelectOptions; 412 }, 413 414 /** 415 * Sets up and opens the Media Manager in order to select an image. 416 * Depending on both the size of the image and the properties of the 417 * current theme, a cropping step after selection may be required or 418 * skippable. 419 * 420 * @param {event} event 421 */ 422 openMM: function(event) { 423 var title, suggestedWidth, suggestedHeight, 424 l10n = _wpMediaViewsL10n; 425 426 event.preventDefault(); 427 428 suggestedWidth = l10n.suggestedWidth.replace('%d', _wpCustomizeHeader.data.width); 429 suggestedHeight = l10n.suggestedHeight.replace('%d', _wpCustomizeHeader.data.height); 430 431 title = { 432 html: l10n.chooseImage + ' <span class="suggested-dimensions">' + 433 suggestedWidth + ' ' + suggestedHeight +'</span>', 434 text: l10n.chooseImage 435 }; 436 437 this.frame = wp.media({ 438 title: title, 439 library: { 440 type: 'image' 441 }, 442 button: { 443 text: l10n.selectAndCrop, 444 close: false 445 }, 446 multiple: false, 447 imgSelectOptions: this.calculateImageSelectOptions 448 }); 449 450 this.frame.states.add([new wp.media.controller.Cropper()]); 451 452 this.frame.on('select', this.onSelect, this); 453 this.frame.on('cropped', this.onCropped, this); 454 this.frame.on('skippedcrop', this.onSkippedCrop, this); 455 456 this.frame.open(); 457 }, 458 459 onSelect: function() { 460 this.frame.setState('cropper'); 461 }, 462 onCropped: function(croppedImage) { 463 var url = croppedImage.post_content, 464 attachmentId = croppedImage.attachment_id, 465 w = croppedImage.width, 466 h = croppedImage.height; 467 this.setImageFromURL(url, attachmentId, w, h); 468 }, 469 onSkippedCrop: function(selection) { 470 var url = selection.get('url'), 471 w = selection.get('width'), 472 h = selection.get('height'); 473 this.setImageFromURL(url, selection.id, w, h); 474 }, 475 476 /** 477 * Creates a new wp.customize.HeaderTool.ImageModel from provided 478 * header image data and inserts it into the user-uploaded headers 479 * collection. 480 * 481 * @param {String} url 482 * @param {Number} attachmentId 483 * @param {Number} width 484 * @param {Number} height 485 */ 486 setImageFromURL: function(url, attachmentId, width, height) { 487 var choice, data = {}; 488 489 data.url = url; 490 data.thumbnail_url = url; 491 492 if (attachmentId) { 493 data.attachment_id = attachmentId; 494 } 495 496 if (width) { 497 data.width = width; 498 } 499 500 if (height) { 501 data.height = height; 502 } 503 504 choice = new api.HeaderTool.ImageModel({ 505 header: data, 506 choice: url.split('/').pop() 507 }); 508 api.HeaderTool.UploadsList.add(choice); 509 api.HeaderTool.currentHeader.set(choice.toJSON()); 510 choice.save(); 511 choice.importImage(); 512 }, 513 514 /** 515 * Triggers the necessary events to deselect an image which was set as 516 * the currently selected one. 517 */ 518 removeImage: function() { 519 api.HeaderTool.currentHeader.trigger('hide'); 520 api.HeaderTool.CombinedList.trigger('control:removeImage'); 521 } 522 523 }); 524 309 525 // Change objects contained within the main customize object to Settings. 310 526 api.defaultConstructor = api.Setting; 311 527 … … 686 902 api.controlConstructor = { 687 903 color: api.ColorControl, 688 904 upload: api.UploadControl, 689 image: api.ImageControl 905 image: api.ImageControl, 906 header: api.HeaderControl 690 907 }; 691 908 692 909 $( function() { … … 961 1178 }); 962 1179 }); 963 1180 964 // Handle header image data965 api.control( 'header_image', function( control ) {966 control.setting.bind( function( to ) {967 if ( to === control.params.removed )968 control.settings.data.set( false );969 });970 971 control.library.on( 'click', 'a', function() {972 control.settings.data.set( $(this).data('customizeHeaderImageData') );973 });974 975 control.uploader.success = function( attachment ) {976 var data;977 978 api.ImageControl.prototype.success.call( control, attachment );979 980 data = {981 attachment_id: attachment.get('id'),982 url: attachment.get('url'),983 thumbnail_url: attachment.get('url'),984 height: attachment.get('height'),985 width: attachment.get('width')986 };987 988 attachment.element.data( 'customizeHeaderImageData', data );989 control.settings.data.set( data );990 };991 });992 993 1181 api.trigger( 'ready' ); 994 1182 995 1183 // Make sure left column gets focus -
src/wp-includes/class-wp-customize-control.php
diff --git src/wp-includes/class-wp-customize-control.php src/wp-includes/class-wp-customize-control.php index fde8561..aed26fc 100644
class WP_Customize_Background_Image_Control extends WP_Customize_Image_Control { 691 691 } 692 692 } 693 693 694 /** 695 * Customize Header Image Control Class 696 * 697 * @package WordPress 698 * @subpackage Customize 699 * @since 3.4.0 700 */ 701 class WP_Customize_Header_Image_Control extends WP_Customize_Image_Control { 702 /** 703 * The processed default headers. 704 * @since 3.4.2 705 * @var array 706 */ 707 protected $default_headers; 708 709 /** 710 * The uploaded headers. 711 * @since 3.4.2 712 * @var array 713 */ 714 protected $uploaded_headers; 694 class WP_Customize_Header_Image_Control extends WP_Customize_Control { 695 public $type = 'header'; 715 696 716 /**717 * Constructor.718 *719 * @since 3.4.0720 * @uses WP_Customize_Image_Control::__construct()721 * @uses WP_Customize_Image_Control::add_tab()722 *723 * @param WP_Customize_Manager $manager724 */725 697 public function __construct( $manager ) { 726 698 parent::__construct( $manager, 'header_image', array( 727 699 'label' => __( 'Header Image' ), … … class WP_Customize_Header_Image_Control extends WP_Customize_Image_Control { 733 705 'context' => 'custom-header', 734 706 'removed' => 'remove-header', 735 707 'get_url' => 'get_header_image', 736 'statuses' => array( 737 '' => __('Default'), 738 'remove-header' => __('No Image'), 739 'random-default-image' => __('Random Default Image'), 740 'random-uploaded-image' => __('Random Uploaded Image'), 708 ) ); 709 710 } 711 712 public function to_json() { 713 parent::to_json(); 714 } 715 716 public function enqueue() { 717 wp_enqueue_media(); 718 wp_enqueue_script( 'customize-header-views' ); 719 720 $this->prepare_control(); 721 722 wp_localize_script( 'customize-header-views', '_wpCustomizeHeader', array( 723 'data' => array( 724 'width' => absint( get_theme_support( 'custom-header', 'width' ) ), 725 'height' => absint( get_theme_support( 'custom-header', 'height' ) ), 726 'flex-width' => absint( get_theme_support( 'custom-header', 'flex-width' ) ), 727 'flex-height' => absint( get_theme_support( 'custom-header', 'flex-height' ) ), 728 'currentImgSrc' => $this->get_current_image_src(), 729 ), 730 'nonces' => array( 731 'add' => wp_create_nonce( 'header-add' ), 732 'remove' => wp_create_nonce( 'header-remove' ), 733 ), 734 'l10n' => array( 735 /* translators: header images uploaded by user */ 736 'uploaded' => __( 'uploaded' ), 737 /* translators: header images suggested by the current theme */ 738 'default' => __( 'suggested' ) 739 ), 740 'uploads' => $this->uploaded_headers, 741 'defaults' => $this->default_headers 742 ) ); 743 744 parent::enqueue(); 745 } 746 747 public function get_default_header_images() { 748 global $custom_image_header; 749 750 // Get *the* default image if there is one 751 $default = get_theme_support( 'custom-header', 'default-image' ); 752 753 if ( ! $default ) { // If not, 754 return $custom_image_header->default_headers; // easy peasy. 755 } 756 757 $default = sprintf( $default, 758 get_template_directory_uri(), 759 get_stylesheet_directory_uri() ); 760 761 $header_images = array(); 762 $already_has_default = false; 763 764 // Get the whole set of default images 765 $default_header_images = $custom_image_header->default_headers; 766 foreach ( $default_header_images as $k => $h ) { 767 if ( $h['url'] == $default ) { 768 $already_has_default = true; 769 break; 770 } 771 } 772 773 // If *the one true image* isn't included in the default set, add it in 774 // first position 775 if ( ! $already_has_default ) { 776 $header_images['default'] = array( 777 'url' => $default, 778 'thumbnail_url' => $default, 779 'description' => 'Default' 780 ); 781 } 782 783 // The rest of the set comes after 784 $header_images = array_merge( $header_images, $default_header_images ); 785 786 return $header_images; 787 } 788 789 public function get_uploaded_header_images() { 790 $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet(); 791 $header_images = array(); 792 793 $headers_not_dated = get_posts( array( 794 'post_type' => 'attachment', 795 'meta_key' => '_wp_attachment_is_custom_header', 796 'meta_value' => get_option('stylesheet'), 797 'orderby' => 'none', 798 'nopaging' => true, 799 'meta_query' => array( 800 array( 801 'key' => '_wp_attachment_is_custom_header', 802 'value' => get_option( 'stylesheet' ), 803 'compare' => 'LIKE' 804 ), 805 array( 806 'key' => $key, 807 'value' => 'this string must not be empty', 808 'compare' => 'NOT EXISTS' 809 ), 741 810 ) 742 811 ) ); 743 812 744 // Remove the upload tab. 745 $this->remove_tab( 'upload-new' ); 813 $headers_dated = get_posts( array( 814 'post_type' => 'attachment', 815 'meta_key' => $key, 816 'orderby' => 'meta_value_num', 817 'order' => 'DESC', 818 'nopaging' => true, 819 'meta_query' => array( 820 array( 821 'key' => '_wp_attachment_is_custom_header', 822 'value' => get_option( 'stylesheet' ), 823 'compare' => 'LIKE' 824 ), 825 ), 826 ) ); 827 828 $limit = apply_filters( 'custom_header_uploaded_limit', 15 ); 829 $headers = array_merge( $headers_dated, $headers_not_dated ); 830 $headers = array_slice( $headers, 0, $limit ); 831 832 foreach ( (array) $headers as $header ) { 833 $url = esc_url_raw( $header->guid ); 834 $header_data = wp_get_attachment_metadata( $header->ID ); 835 $timestamp = get_post_meta( $header->ID, 836 '_wp_attachment_custom_header_last_used_' . get_stylesheet(), 837 true ); 838 839 $h = array( 840 'attachment_id' => $header->ID, 841 'url' => $url, 842 'thumbnail_url' => $url, 843 'timestamp' => $timestamp ? $timestamp : 0, 844 ); 845 846 if ( isset( $header_data['width'] ) ) { 847 $h['width'] = $header_data['width']; 848 } 849 if ( isset( $header_data['height'] ) ) { 850 $h['height'] = $header_data['height']; 851 } 852 853 $header_images[] = $h; 854 } 855 856 return $header_images; 746 857 } 747 858 748 /**749 * Prepares the control.750 *751 * If no tabs exist, removes the control from the manager.752 *753 * @since 3.4.2754 */755 859 public function prepare_control() { 756 860 global $custom_image_header; 757 if ( empty( $custom_image_header ) ) 758 return parent::prepare_control(); 861 if ( empty( $custom_image_header ) ) { 862 return; 863 } 759 864 760 865 // Process default headers and uploaded headers. 761 866 $custom_image_header->process_default_headers(); 762 $this->default_headers = $custom_image_header->default_headers; 763 $this->uploaded_headers = get_uploaded_header_images(); 867 $this->default_headers = $this->get_default_header_images(); 868 $this->uploaded_headers = $this->get_uploaded_header_images(); 869 } 764 870 765 if ( $this->default_headers ) 766 $this->add_tab( 'default', __('Default'), array( $this, 'tab_default_headers' ) ); 871 function print_header_image_template() { 872 ?> 873 <script type="text/template" id="tmpl-header-choice"> 874 <% if (random) { %> 875 876 <div class="placeholder random"> 877 <div class="inner"> 878 <span><span class="dice">⚄</span> 879 <?php /* translators: "nImages" is a number, "type" is either "uploaded" or "suggested" */ ?> 880 <?php _e( 'Randomize <%- nImages %> <%- type %> headers' ); ?> 881 </span> 882 </div> 883 </div> 767 884 768 if ( ! $this->uploaded_headers ) 769 $this->remove_tab( 'uploaded' ); 885 <% } else { %> 770 886 771 return parent::prepare_control(); 772 } 887 <% if (type == 'uploaded') { %> 888 <div class="dashicons dashicons-no close"></div> 889 <% } %> 773 890 774 /** 775 * @since 3.4.0 776 * 777 * @param mixed $choice Which header image to select. (@see Custom_Image_Header::get_header_image() ) 778 * @param array $header 779 */ 780 public function print_header_image( $choice, $header ) { 781 $header['url'] = set_url_scheme( $header['url'] ); 782 $header['thumbnail_url'] = set_url_scheme( $header['thumbnail_url'] ); 891 <a href="#" class="choice thumbnail %>" 892 data-customize-image-value="<%- header.url %>" 893 data-customize-header-image-data="<%- JSON.stringify(header) %>"> 894 <img src="<%- header.thumbnail_url %>"> 895 </a> 783 896 784 $header_image_data = array( 'choice' => $choice ); 785 foreach ( array( 'attachment_id', 'width', 'height', 'url', 'thumbnail_url' ) as $key ) { 786 if ( isset( $header[ $key ] ) ) 787 $header_image_data[ $key ] = $header[ $key ]; 788 } 897 <% } %> 898 </script> 789 899 900 <script type="text/template" id="tmpl-header-current"> 901 <% if (choice) { %> 902 <% if (random) { %> 790 903 791 ?> 792 <a href="#" class="thumbnail" 793 data-customize-image-value="<?php echo esc_url( $header['url'] ); ?>" 794 data-customize-header-image-data="<?php echo esc_attr( json_encode( $header_image_data ) ); ?>"> 795 <img src="<?php echo esc_url( $header['thumbnail_url'] ); ?>" /> 796 </a> 904 <div class="placeholder"> 905 <div class="inner"> 906 <span><span class="dice">⚄</span> 907 <?php /* translators: "nImages" is a number, "type" is either "uploaded" or "suggested" */ ?> 908 <?php _e( 'Randomizing <%- nImages %> <%- type %> headers' ); ?> 909 </span> 910 </div> 911 </div> 912 913 <% } else { %> 914 915 <img src="<%- header.thumbnail_url %>" /> 916 917 <% } %> 918 <% } else { %> 919 920 <div class="placeholder"> 921 <div class="inner"> 922 <span> 923 <?php _e( 'No image set' ); ?> 924 </span> 925 </div> 926 </div> 927 928 <% } %> 929 </script> 797 930 <?php 798 931 } 799 932 800 /** 801 * @since 3.4.0 802 */ 803 public function tab_uploaded() { 804 ?><div class="uploaded-target"></div><?php 805 806 foreach ( $this->uploaded_headers as $choice => $header ) 807 $this->print_header_image( $choice, $header ); 933 public function get_current_image_src() { 934 $src = $this->value(); 935 if ( isset( $this->get_url ) ) { 936 $src = call_user_func( $this->get_url, $src ); 937 return $src; 938 } 939 return null; 808 940 } 809 941 810 /** 811 * @since 3.4.0 812 */ 813 public function tab_default_headers() { 814 foreach ( $this->default_headers as $choice => $header ) 815 $this->print_header_image( $choice, $header ); 942 public function render_content() { 943 $this->print_header_image_template(); 944 $visibility = $this->get_current_image_src() ? '' : ' style="display:none" '; 945 $width = absint( get_theme_support( 'custom-header', 'width' ) ); 946 $height = absint( get_theme_support( 'custom-header', 'height' ) ); 947 ?> 948 949 950 <div class="customize-control-content"> 951 <p class="customizer-section-intro"> 952 <?php _e( 'Personalize your blog with your own header image.' ); ?> 953 <?php 954 if ( $width && $height ) { 955 printf( __( 'While you can crop images to your liking after clicking <strong>%s</strong>, your theme recommends a header size of <strong>%dx%d</strong> pixels.' ), 956 _x( 'Add new', 'new image', 'custom-header' ), $width, $height ); 957 } else { 958 if ( $width ) { 959 printf( __( 'While you can crop images to your liking after clicking <strong>%s</strong>, your theme recommends a header width of <strong>%d</strong> pixels.' ), 960 _x( 'Add new', 'new image', 'custom-header' ), $width ); 961 } 962 if ( $height ) { 963 printf( __( 'While you can crop images to your liking after clicking <strong>%s</strong>, your theme recommends a header height of <strong>%d</strong> pixels.' ), 964 _x( 'Add new', 'new image', 'custom-header' ), $height ); 965 } 966 } 967 ?> 968 </p> 969 <div class="current"> 970 <span class="customize-control-title"> 971 <?php _e( 'Current header', 'custom-header' ); ?> 972 </span> 973 <div class="container"> 974 </div> 975 </div> 976 <div class="actions"> 977 <?php /* translators: Hide as in hide header image via the Customizer */ ?> 978 <a href="#" <?php echo $visibility ?> class="button remove"><?php _e( 'Hide', 'custom-header' ); ?></a> 979 <?php /* translators: New as in add new header image via the Customizer */ ?> 980 <a href="#" class="button new"><?php _ex( 'Add new', 'new image', 'custom-header' ); ?></a> 981 <div style="clear:both"></div> 982 </div> 983 <div class="choices"> 984 <span class="customize-control-title header-previously-uploaded"> 985 <?php _e( 'Previously uploaded', 'custom-header' ); ?> 986 </span> 987 <div class="uploaded"> 988 <div class="list"> 989 </div> 990 </div> 991 <span class="customize-control-title header-default"> 992 <?php _e( 'Suggested', 'custom-header' ); ?> 993 </span> 994 <div class="default"> 995 <div class="list"> 996 </div> 997 </div> 998 </div> 999 </div> 1000 <?php 816 1001 } 817 } 818 No newline at end of file 1002 1003 } -
new file src/wp-includes/js/customize-header-models.js
diff --git src/wp-includes/js/customize-header-models.js src/wp-includes/js/customize-header-models.js new file mode 100644 index 0000000..475ca4c
- + 1 /* globals jQuery, _wpCustomizeHeader, _wpCustomizeSettings */ 2 ;( function( $, wp ) { 3 var api = wp.customize; 4 api.HeaderTool = {}; 5 6 7 /** 8 * wp.customize.HeaderTool.ImageModel 9 * 10 * A header image. This is where saves via the Customizer API are 11 * abstracted away, plus our own AJAX calls to add images to and remove 12 * images from the user's recently uploaded images setting on the server. 13 * These calls are made regardless of whether the user actually saves new 14 * Customizer settings. 15 * 16 * @constructor 17 * @augments Backbone.Model 18 */ 19 api.HeaderTool.ImageModel = Backbone.Model.extend({ 20 defaults: function() { 21 return { 22 header: { 23 attachment_id: 0, 24 url: '', 25 timestamp: Date.now(), 26 thumbnail_url: '' 27 }, 28 choice: '', 29 hidden: false, 30 random: false 31 }; 32 }, 33 34 initialize: function() { 35 this.on('hide', this.hide, this); 36 }, 37 38 hide: function() { 39 this.set('choice', ''); 40 api('header_image').set('remove-header'); 41 api('header_image_data').set('remove-header'); 42 }, 43 44 destroy: function() { 45 var data = this.get('header'), 46 curr = api.HeaderTool.currentHeader.get('header').attachment_id; 47 48 // If the image we're removing is also the current header, unset 49 // the latter 50 if (curr && data.attachment_id === curr) { 51 api.HeaderTool.currentHeader.trigger('hide'); 52 } 53 54 $.post(_wpCustomizeSettings.url.ajax, { 55 wp_customize: 'on', 56 theme: api.settings.theme.stylesheet, 57 dataType: 'json', 58 action: 'header_remove', 59 nonce: _wpCustomizeHeader.nonces.remove, 60 data: data 61 }); 62 63 this.trigger('destroy', this, this.collection); 64 }, 65 66 save: function() { 67 if (this.get('random')) { 68 api('header_image').set(this.get('header').random); 69 api('header_image_data').set(this.get('header').random); 70 } else { 71 if (this.get('header').defaultName) { 72 api('header_image').set(this.get('header').url); 73 api('header_image_data').set(this.get('header').defaultName); 74 } else { 75 api('header_image').set(this.get('header').url); 76 api('header_image_data').set(this.get('header')); 77 } 78 } 79 80 api.HeaderTool.combinedList.trigger('control:setImage', this); 81 }, 82 83 importImage: function() { 84 var data = this.get('header'); 85 if (data.attachment_id === undefined) { 86 return; 87 } 88 89 $.post(_wpCustomizeSettings.url.ajax, { 90 wp_customize: 'on', 91 theme: api.settings.theme.stylesheet, 92 dataType: 'json', 93 action: 'header_add', 94 nonce: _wpCustomizeHeader.nonces.add, 95 data: data 96 }); 97 }, 98 99 shouldBeCropped: function() { 100 if (this.get('themeFlexWidth') === true && 101 this.get('themeFlexHeight') === true) { 102 return false; 103 } 104 105 if (this.get('themeFlexWidth') === true && 106 this.get('themeHeight') === this.get('imageHeight')) { 107 return false; 108 } 109 110 if (this.get('themeFlexHeight') === true && 111 this.get('themeWidth') === this.get('imageWidth')) { 112 return false; 113 } 114 115 if (this.get('themeWidth') === this.get('imageWidth') && 116 this.get('themeHeight') === this.get('imageHeight')) { 117 return false; 118 } 119 120 return true; 121 } 122 }); 123 124 125 /** 126 * wp.customize.HeaderTool.ChoiceList 127 * 128 * @constructor 129 * @augments Backbone.Collection 130 */ 131 api.HeaderTool.ChoiceList = Backbone.Collection.extend({ 132 model: api.HeaderTool.ImageModel, 133 134 // Ordered from most recently used to least 135 comparator: function(model) { 136 return -model.get('header').timestamp; 137 }, 138 139 initialize: function() { 140 var current = api.HeaderTool.currentHeader.get('choice').replace(/^https?:\/\//, ''), 141 isRandom = this.isRandomChoice(api.get().header_image); 142 143 // Overridable by an extending class 144 if (!this.type) { 145 this.type = 'uploaded'; 146 } 147 148 // Overridable by an extending class 149 if (!this.data) { 150 this.data = _wpCustomizeHeader.uploads; 151 } 152 153 if (isRandom) { 154 // So that when adding data we don't hide regular images 155 current = api.get().header_image; 156 } 157 158 this.on('control:setImage', this.setImage, this); 159 this.on('control:removeImage', this.removeImage, this); 160 this.on('add', this.maybeAddRandomChoice, this); 161 162 _.each(this.data, function(elt, index) { 163 if (!elt.attachment_id) { 164 elt.defaultName = index; 165 } 166 167 this.add({ 168 header: elt, 169 choice: elt.url.split('/').pop(), 170 hidden: current === elt.url.replace(/^https?:\/\//, '') 171 }, { silent: true }); 172 }, this); 173 174 if (this.size() > 0) { 175 this.addRandomChoice(current); 176 } 177 }, 178 179 maybeAddRandomChoice: function() { 180 if (this.size() === 1) { 181 this.addRandomChoice(); 182 } 183 }, 184 185 addRandomChoice: function(initialChoice) { 186 var isRandomSameType = RegExp(this.type).test(initialChoice), 187 randomChoice = 'random-' + this.type + '-image'; 188 189 this.add({ 190 header: { 191 timestamp: 0, 192 random: randomChoice, 193 width: 245, 194 height: 41 195 }, 196 choice: randomChoice, 197 random: true, 198 hidden: isRandomSameType 199 }); 200 }, 201 202 isRandomChoice: function(choice) { 203 return (/^random-(uploaded|default)-image$/).test(choice); 204 }, 205 206 shouldHideTitle: function() { 207 return _.every(this.pluck('hidden')); 208 }, 209 210 setImage: function(model) { 211 this.each(function(m) { 212 m.set('hidden', false); 213 }); 214 215 if (model) { 216 model.set('hidden', true); 217 // Bump images to top except for special "Randomize" images 218 if (!model.get('random')) { 219 model.get('header').timestamp = Date.now(); 220 this.sort(); 221 } 222 } 223 }, 224 225 removeImage: function() { 226 this.each(function(m) { 227 m.set('hidden', false); 228 }); 229 }, 230 231 shown: function() { 232 var filtered = this.where({ hidden: false }); 233 return new api.HeaderTool.ChoiceList( filtered ); 234 } 235 }); 236 237 238 /** 239 * wp.customize.HeaderTool.DefaultsList 240 * 241 * @constructor 242 * @augments wp.customize.HeaderTool.ChoiceList 243 * @augments Backbone.Collection 244 */ 245 api.HeaderTool.DefaultsList = api.HeaderTool.ChoiceList.extend({ 246 initialize: function() { 247 this.type = 'default'; 248 this.data = _wpCustomizeHeader.defaults; 249 api.HeaderTool.ChoiceList.prototype.initialize.apply(this); 250 } 251 }); 252 253 })( jQuery, this.wp ); -
new file src/wp-includes/js/customize-header-views.js
diff --git src/wp-includes/js/customize-header-views.js src/wp-includes/js/customize-header-views.js new file mode 100644 index 0000000..06be379
- + 1 /* globals jQuery, _, Backbone, _wpCustomizeHeader */ 2 ;( function( $, wp, _ ) { 3 if ( ! wp || ! wp.customize ) { return; } 4 var api = wp.customize; 5 6 7 /** 8 * wp.customize.HeaderTool.CurrentView 9 * 10 * Displays the currently selected header image, or a placeholder in lack 11 * thereof. 12 * 13 * Instantiate with model wp.customize.HeaderTool.currentHeader. 14 * 15 * @constructor 16 * @augments Backbone.View 17 */ 18 api.HeaderTool.CurrentView = Backbone.View.extend({ 19 template: _.template($('#tmpl-header-current').html()), 20 21 initialize: function() { 22 this.listenTo(this.model, 'change', this.render); 23 this.render(); 24 }, 25 26 render: function() { 27 this.$el.html(this.template(this.model.toJSON())); 28 this.setPlaceholder(); 29 this.setButtons(); 30 return this; 31 }, 32 33 getHeight: function() { 34 var image = this.$el.find('img'), 35 saved = this.model.get('savedHeight'), 36 height = image.height() || saved, 37 headerImageData; 38 39 if (image.length) { 40 this.$el.find('.inner').hide(); 41 } else { 42 this.$el.find('.inner').show(); 43 } 44 45 // happens at ready 46 if (!height) { 47 headerImageData = api.get().header_image_data; 48 49 if (headerImageData && headerImageData.width && headerImageData.height) { 50 // hardcoded container width 51 height = 260 / headerImageData.width * headerImageData.height; 52 } 53 else { 54 // fallback for when no image is set 55 height = 40; 56 } 57 } 58 59 return height; 60 }, 61 62 setPlaceholder: function(_height) { 63 var height = _height || this.getHeight(); 64 this.model.set('savedHeight', height); 65 this.$el 66 .add(this.$el.find('.placeholder')) 67 .height(height); 68 }, 69 70 setButtons: function() { 71 var elements = $('.actions .remove'); 72 if (this.model.get('choice')) { 73 elements.show(); 74 } else { 75 elements.hide(); 76 } 77 } 78 }); 79 80 81 /** 82 * wp.customize.HeaderTool.ChoiceView 83 * 84 * Represents a choosable header image, be it user-uploaded, 85 * theme-suggested or a special Randomize choice. 86 * 87 * Takes a wp.customize.HeaderTool.ImageModel. 88 * 89 * Manually changes model wp.customize.HeaderTool.currentHeader via the 90 * `select` method. 91 * 92 * @constructor 93 * @augments Backbone.View 94 */ 95 (function () { // closures FTW 96 var lastHeight = 0; 97 api.HeaderTool.ChoiceView = Backbone.View.extend({ 98 template: _.template($('#tmpl-header-choice').html()), 99 100 className: 'header-view', 101 102 events: { 103 'click .choice,.random': 'select', 104 'click .close': 'removeImage' 105 }, 106 107 initialize: function() { 108 var properties = [ 109 this.model.get('header').url, 110 this.model.get('choice') 111 ]; 112 113 this.listenTo(this.model, 'change', this.render); 114 115 if (_.contains(properties, api.get().header_image)) { 116 api.HeaderTool.currentHeader.set(this.extendedModel()); 117 } 118 }, 119 120 render: function() { 121 var model = this.model; 122 123 this.$el.html(this.template(this.extendedModel())); 124 125 if (model.get('random')) { 126 this.setPlaceholder(40); 127 } 128 else { 129 lastHeight = this.getHeight(); 130 } 131 132 this.$el.toggleClass('hidden', model.get('hidden')); 133 return this; 134 }, 135 136 extendedModel: function() { 137 var c = this.model.get('collection'), 138 t = _wpCustomizeHeader.l10n[c.type] || ''; 139 140 return _.extend(this.model.toJSON(), { 141 // -1 to exclude the randomize button 142 nImages: c.size() - 1, 143 type: t 144 }); 145 }, 146 147 getHeight: api.HeaderTool.CurrentView.prototype.getHeight, 148 149 setPlaceholder: api.HeaderTool.CurrentView.prototype.setPlaceholder, 150 151 select: function() { 152 this.model.save(); 153 api.HeaderTool.currentHeader.set(this.extendedModel()); 154 }, 155 156 removeImage: function(e) { 157 e.stopPropagation(); 158 this.model.destroy(); 159 this.remove(); 160 } 161 }); 162 })(); 163 164 165 /** 166 * wp.customize.HeaderTool.ChoiceListView 167 * 168 * A container for ChoiceViews. These choices should be of one same type: 169 * user-uploaded headers or theme-defined ones. 170 * 171 * Takes a wp.customize.HeaderTool.ChoiceList. 172 * 173 * @constructor 174 * @augments Backbone.View 175 */ 176 api.HeaderTool.ChoiceListView = Backbone.View.extend({ 177 initialize: function() { 178 this.listenTo(this.collection, 'add', this.addOne); 179 this.listenTo(this.collection, 'remove', this.render); 180 this.listenTo(this.collection, 'sort', this.render); 181 this.listenTo(this.collection, 'change:hidden', this.toggleTitle); 182 this.listenTo(this.collection, 'change:hidden', this.setMaxListHeight); 183 this.render(); 184 }, 185 186 render: function() { 187 this.$el.empty(); 188 this.collection.each(this.addOne, this); 189 this.toggleTitle(); 190 }, 191 192 addOne: function(choice) { 193 var view; 194 choice.set({ collection: this.collection }); 195 view = new api.HeaderTool.ChoiceView({ model: choice }); 196 this.$el.append(view.render().el); 197 }, 198 199 toggleTitle: function() { 200 var title = this.$el.parents().prev('.customize-control-title'); 201 if (this.collection.shouldHideTitle()) { 202 title.hide(); 203 } else { 204 title.show(); 205 } 206 } 207 }); 208 209 210 /** 211 * wp.customize.HeaderTool.CombinedList 212 * 213 * Aggregates wp.customize.HeaderTool.ChoiceList collections (or any 214 * Backbone object, really) and acts as a bus to feed them events. 215 * 216 * @constructor 217 * @augments Backbone.View 218 */ 219 api.HeaderTool.CombinedList = Backbone.View.extend({ 220 initialize: function(collections) { 221 this.collections = collections; 222 this.on('all', this.propagate, this); 223 }, 224 propagate: function(event, arg) { 225 _.each(this.collections, function(collection) { 226 collection.trigger(event, arg); 227 }); 228 } 229 }); 230 231 })( jQuery, this.wp, _ ); -
src/wp-includes/script-loader.php
diff --git src/wp-includes/script-loader.php src/wp-includes/script-loader.php index a098b19..97e1501 100644
function wp_default_scripts( &$scripts ) { 381 381 'allowedFiles' => __( 'Allowed Files' ), 382 382 ) ); 383 383 384 $scripts->add( 'customize-header-models', "/wp-includes/js/customize-header-models.js", array( 'underscore', 'backbone' ), false, 1 ); 385 $scripts->add( 'customize-header-views', "/wp-includes/js/customize-header-views.js", array( 'jquery', 'underscore', 'imgareaselect', 'customize-header-models' ), false, 1 ); 386 384 387 $scripts->add( 'accordion', "/wp-admin/js/accordion$suffix.js", array( 'jquery' ), false, 1 ); 385 388 386 389 $scripts->add( 'shortcode', "/wp-includes/js/shortcode$suffix.js", array( 'underscore' ), false, 1 ); … … function wp_default_styles( &$styles ) { 602 605 $styles->add( 'login', "/wp-admin/css/login$suffix.css", array( 'buttons', 'open-sans', 'dashicons' ) ); 603 606 $styles->add( 'install', "/wp-admin/css/install$suffix.css", array( 'buttons', 'open-sans' ) ); 604 607 $styles->add( 'wp-color-picker', "/wp-admin/css/color-picker$suffix.css" ); 605 $styles->add( 'customize-controls', "/wp-admin/css/customize-controls$suffix.css", array( 'wp-admin', 'colors', 'ie' ) );608 $styles->add( 'customize-controls', "/wp-admin/css/customize-controls$suffix.css", array( 'wp-admin', 'colors', 'ie', 'imgareaselect' ) ); 606 609 $styles->add( 'ie', "/wp-admin/css/ie$suffix.css" ); 607 610 608 611 $styles->add_data( 'ie', 'conditional', 'lte IE 7' );