Ticket #24048: 24048.8.diff
File 24048.8.diff, 30.7 KB (added by , 7 years ago) |
---|
-
src/wp-admin/css/common.css
diff --git src/wp-admin/css/common.css src/wp-admin/css/common.css index 3a11a58e30..8bc9f1d883 100644
div.error { 1466 1466 .wrap #templateside .notice { 1467 1467 display: block; 1468 1468 margin: 0; 1469 padding: 5px 12px;1469 padding: 5px 8px; 1470 1470 font-weight: 600; 1471 1471 text-decoration: none; 1472 1472 } 1473 1473 1474 1474 .wrap #templateside span.notice { 1475 1475 margin-left: -12px; 1476 1476 } 1477 1477 1478 1478 #templateside li.notice a { 1479 1479 padding: 0; 1480 1480 } 1481 1481 1482 1482 /* Update icon. */ … … img { 3036 3036 width: 97%; 3037 3037 height: calc( 100vh - 280px ); 3038 3038 } 3039 3039 3040 #templateside { 3040 3041 margin-top: 31px; 3041 overflow: scroll; 3042 overflow: auto; 3043 padding: 2px; 3044 height: calc(100vh - 280px); 3045 } 3046 #templateside ul ul { 3047 padding-left: 12px; 3048 } 3049 3050 /* 3051 * Styles for Theme and Plugin editors. 3052 */ 3053 3054 /* Hide collapsed items. */ 3055 [role="treeitem"][aria-expanded="false"] > ul { 3056 display: none; 3042 3057 } 3043 3058 3059 /* Use arrow dashicons for folder states, but hide from screen readers. */ 3060 [role="treeitem"] span[aria-hidden] { 3061 display: inline; 3062 font-family: dashicons; 3063 font-size: 20px; 3064 position: absolute; 3065 pointer-events: none; 3066 } 3067 [role="treeitem"][aria-expanded="false"] > .folder-label .icon:after { 3068 content: "\f139"; 3069 } 3070 [role="treeitem"][aria-expanded="true"] > .folder-label .icon:after { 3071 content: "\f140"; 3072 } 3073 [role="treeitem"] .folder-label { 3074 display: block; 3075 padding: 3px 3px 3px 12px; 3076 cursor: pointer; 3077 } 3078 3079 /* Remove outline, and create our own focus and hover styles */ 3080 [role="treeitem"] { 3081 outline: 0; 3082 } 3083 [role="treeitem"] .folder-label.focus { 3084 color: #124964; 3085 box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, .8); 3086 } 3087 [role="treeitem"].hover, 3088 [role="treeitem"] .folder-label.hover { 3089 background-color: #DDDDDD; 3090 } 3091 3092 .tree-folder { 3093 margin: 0; 3094 position: relative; 3095 } 3096 [role="treeitem"] li { 3097 position: relative; 3098 } 3099 3100 /* Styles for folder indicators/depth */ 3101 .tree-folder .tree-folder::after { 3102 content: ' '; 3103 display: block; 3104 position: absolute; 3105 left: 2px; 3106 border-left: 1px solid #ccc; 3107 top: -13px; 3108 bottom: 10px; 3109 } 3110 .tree-folder > li::before { 3111 content: ' '; 3112 position: absolute; 3113 display: block; 3114 border-left: 1px solid #ccc; 3115 left: 2px; 3116 top: -5px; 3117 height: 18px; 3118 width: 7px; 3119 border-bottom: 1px solid #ccc; 3120 } 3121 .tree-folder > li::after { 3122 content: ' '; 3123 position: absolute; 3124 display: block; 3125 border-left: 1px solid #ccc; 3126 left: 2px; 3127 bottom: -7px; 3128 top: 0; 3129 } 3130 3131 /* current-file needs to adjustment for .notice styles */ 3132 #templateside .current-file { 3133 margin: -4px 0 -2px; 3134 } 3135 .tree-folder > .current-file::before { 3136 left: 4px; 3137 height: 15px; 3138 width: 6px; 3139 border-left: none; 3140 top: 3px; 3141 } 3142 .tree-folder > .current-file::after { 3143 bottom: -4px; 3144 height: 7px; 3145 left: 2px; 3146 top: auto; 3147 } 3148 3149 /* Lines shouldn't continue on last item */ 3150 .tree-folder > li:last-child::after, 3151 .tree-folder li:last-child > .tree-folder::after { 3152 display: none; 3153 } 3154 3155 3044 3156 #theme-plugin-editor-label { 3045 3157 display: inline-block; 3046 3158 margin-bottom: 1em; … … img { 3653 3765 width: 100%; 3654 3766 } 3655 3767 3768 #templateside ul ul { 3769 padding-left: 1.5em; 3770 } 3771 [role="treeitem"] .folder-label { 3772 display: block; 3773 padding: 5px; 3774 } 3775 .tree-folder > li::before, 3776 .tree-folder > li::after, 3777 .tree-folder .tree-folder::after { 3778 left: -8px; 3779 } 3780 .tree-folder > li::before { 3781 top: 0px; 3782 height: 13px; 3783 } 3784 .tree-folder > .current-file::before { 3785 left: -5px; 3786 top: 7px; 3787 width: 4px; 3788 } 3789 .tree-folder > .current-file::after { 3790 height: 9px; 3791 left: -8px; 3792 } 3793 .wrap #templateside span.notice { 3794 margin-left: -14px; 3795 } 3796 3656 3797 .fileedit-sub .alignright { 3657 3798 margin-top: 15px; 3658 3799 } -
src/wp-admin/includes/misc.php
diff --git src/wp-admin/includes/misc.php src/wp-admin/includes/misc.php index c434b8d3ed..755d481cac 100644
function update_recently_edited( $file ) { 269 269 update_option( 'recently_edited', $oldfiles ); 270 270 } 271 271 272 /** 273 * Makes a tree structure for the Theme Editor's file list. 274 * 275 * @since 4.9.0 276 * @access private 277 * 278 * @param array $allowed_files List of theme file paths. 279 * @return array Tree structure for listing theme files. 280 */ 281 function wp_make_theme_file_tree( $allowed_files ) { 282 $tree_list = array(); 283 foreach ( $allowed_files as $file_name => $absolute_filename ) { 284 $list = explode( '/', $file_name ); 285 $last_dir = &$tree_list; 286 foreach ( $list as $dir ) { 287 $last_dir =& $last_dir[ $dir ]; 288 } 289 $last_dir = $file_name; 290 } 291 return $tree_list; 292 } 293 294 /** 295 * Outputs the formatted file list for the Theme Editor. 296 * 297 * @since 4.9.0 298 * @access private 299 * 300 * @param array $tree List of file/folder paths. 301 * @param string $label Name of file or folder to print. 302 * @param int $level Current level. 303 * @param int $index Current level. 304 * @param int $size Current level. 305 */ 306 function wp_print_theme_file_tree( $tree, $label = false, $level = 2, $index = 1, $size = 1 ) { 307 global $relative_file, $stylesheet; 308 309 if ( is_array( $tree ) ) { 310 $index = 0; 311 $size = count( $tree ); 312 foreach ( $tree as $label => $theme_file ) : 313 $index++; 314 if ( ! is_array( $theme_file ) ) { 315 wp_print_theme_file_tree( $theme_file, $label, $level, $index, $size ); 316 continue; 317 } 318 ?> 319 <li role="treeitem" aria-expanded="true" tabindex="-1" 320 aria-level="<?php echo esc_attr( $level ); ?>" 321 aria-setsize="<?php echo esc_attr( $size ); ?>" 322 aria-posinset="<?php echo esc_attr( $index ); ?>"> 323 <span class="folder-label"><?php echo esc_html( $label ); ?> <span class="screen-reader-text"><?php _e( 'folder' ); ?></span><span aria-hidden="true" class="icon"></span></span> 324 <ul role="group" class="tree-folder"><?php wp_print_theme_file_tree( $theme_file, false, $level + 1, $index, $size ); ?></ul> 325 </li> 326 <?php 327 endforeach; 328 } else { 329 $filename = $tree; 330 $url = add_query_arg( 331 array( 332 'file' => rawurlencode( $tree ), 333 'theme' => rawurlencode( $stylesheet ), 334 ), 335 admin_url( 'theme-editor.php' ) 336 ); 337 ?> 338 <li role="none" class="<?php echo esc_attr( $relative_file === $filename ? 'current-file' : '' ); ?>"> 339 <a role="treeitem" tabindex="<?php echo esc_attr( $relative_file === $filename ? '0' : '-1' ); ?>" 340 href="<?php echo esc_url( $url ); ?>" 341 aria-level="<?php echo esc_attr( $level ); ?>" 342 aria-setsize="<?php echo esc_attr( $size ); ?>" 343 aria-posinset="<?php echo esc_attr( $index ); ?>"> 344 <?php 345 $file_description = esc_html( get_file_description( $filename ) ); 346 if ( $file_description !== $filename && basename( $filename ) !== $file_description ) { 347 $file_description .= '<br /><span class="nonessential">(' . esc_html( $filename ) . ')</span>'; 348 } 349 350 if ( $relative_file === $filename ) { 351 echo '<span class="notice notice-info">' . $file_description . '</span>'; 352 } else { 353 echo $file_description; 354 } 355 ?> 356 </a> 357 </li> 358 <?php 359 } 360 } 361 362 /** 363 * Makes a tree structure for the Plugin Editor's file list. 364 * 365 * @since 4.9.0 366 * @access private 367 * 368 * @param string $plugin_editable_files List of plugin file paths. 369 * 370 * @return array Tree structure for listing plugin files. 371 */ 372 function wp_make_plugin_file_tree( $plugin_editable_files ) { 373 $tree_list = array(); 374 foreach ( $plugin_editable_files as $plugin_file ) { 375 $list = explode( '/', preg_replace( '#^.+?/#', '', $plugin_file ) ); 376 $last_dir = &$tree_list; 377 foreach ( $list as $dir ) { 378 $last_dir =& $last_dir[ $dir ]; 379 } 380 $last_dir = $plugin_file; 381 } 382 return $tree_list; 383 } 384 385 /** 386 * Outputs the formatted file list for the Plugin Editor. 387 * 388 * @since 4.9.0 389 * @access private 390 * 391 * @param array $tree List of file/folder paths. 392 * @param string $label Name of file or folder to print. 393 * @param int $level Current level. 394 * @param int $index Current level. 395 * @param int $size Current level. 396 */ 397 function wp_print_plugin_file_tree( $tree, $label = false, $level = 2, $index = 1, $size = 1 ) { 398 global $file, $plugin; 399 if ( is_array( $tree ) ) { 400 $index = 0; 401 $size = count( $tree ); 402 foreach ( $tree as $label => $plugin_file ) : 403 $index++; 404 if ( ! is_array( $plugin_file ) ) { 405 wp_print_plugin_file_tree( $plugin_file, $label, $level, $index, $size ); 406 continue; 407 } 408 ?> 409 <li role="treeitem" aria-expanded="true" tabindex="-1" 410 aria-level="<?php echo esc_attr( $level ); ?>" 411 aria-setsize="<?php echo esc_attr( $size ); ?>" 412 aria-posinset="<?php echo esc_attr( $index ); ?>"> 413 <span class="folder-label"><?php echo esc_html( $label ); ?> <span class="screen-reader-text"><?php _e( 'folder' ); ?></span><span aria-hidden="true" class="icon"></span></span> 414 <ul role="group" class="tree-folder"><?php wp_print_plugin_file_tree( $plugin_file, false, $level + 1, $index, $size ); ?></ul> 415 </li> 416 <?php 417 endforeach; 418 } else { 419 $url = add_query_arg( 420 array( 421 'file' => rawurlencode( $tree ), 422 'plugin' => rawurlencode( $plugin ), 423 ), 424 admin_url( 'plugin-editor.php' ) 425 ); 426 ?> 427 <li role="none" class="<?php echo esc_attr( $file === $tree ? 'current-file' : '' ); ?>"> 428 <a role="treeitem" tabindex="<?php echo esc_attr( $file === $tree ? '0' : '-1' ); ?>" 429 href="<?php echo esc_url( $url ); ?>" 430 aria-level="<?php echo esc_attr( $level ); ?>" 431 aria-setsize="<?php echo esc_attr( $size ); ?>" 432 aria-posinset="<?php echo esc_attr( $index ); ?>"> 433 <?php 434 if ( $file === $tree ) { 435 echo '<span class="notice notice-info">' . esc_html( $label ) . '</span>'; 436 } else { 437 echo esc_html( $label ); 438 } 439 ?> 440 </a> 441 </li> 442 <?php 443 } 444 } 445 272 446 /** 273 447 * Flushes rewrite rules if siteurl, home or page_on_front changed. 274 448 * -
new file src/wp-admin/js/tree-links.js
diff --git src/wp-admin/js/tree-links.js src/wp-admin/js/tree-links.js new file mode 100644 index 0000000000..ff9ce2359a
- + 1 /* 2 * This content is licensed according to the W3C Software License at 3 * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document 4 * 5 * File: TreeLinks.js 6 * 7 * Desc: Tree widget that implements ARIA Authoring Practices 8 * for a tree being used as a file viewer 9 * 10 * Author: Jon Gunderson, Ku Ja Eun and Nicholas Hoyt 11 */ 12 13 /** 14 * ARIA Treeview example 15 * @function onload 16 * @desc after page has loaded initializ all treeitems based on the role=treeitem 17 */ 18 19 window.addEventListener('load', function () { 20 21 var trees = document.querySelectorAll('[role="tree"]'); 22 23 for (var i = 0; i < trees.length; i++) { 24 var t = new TreeLinks(trees[i]); 25 t.init(); 26 } 27 28 }); 29 30 /* 31 * @constructor 32 * 33 * @desc 34 * Tree item object for representing the state and user interactions for a 35 * tree widget 36 * 37 * @param node 38 * An element with the role=tree attribute 39 */ 40 41 var TreeLinks = function (node) { 42 // Check whether node is a DOM element 43 if (typeof node !== 'object') { 44 return; 45 } 46 47 this.domNode = node; 48 49 this.treeitems = []; 50 this.firstChars = []; 51 52 this.firstTreeitem = null; 53 this.lastTreeitem = null; 54 55 }; 56 57 TreeLinks.prototype.init = function () { 58 59 function findTreeitems(node, tree, group) { 60 61 var elem = node.firstElementChild; 62 var ti = group; 63 64 while (elem) { 65 66 if ((elem.tagName.toLowerCase() === 'li' && elem.firstElementChild.tagName.toLowerCase() === 'span') || elem.tagName.toLowerCase() === 'a') { 67 ti = new TreeitemLink(elem, tree, group); 68 ti.init(); 69 tree.treeitems.push(ti); 70 tree.firstChars.push(ti.label.substring(0, 1).toLowerCase()); 71 } 72 73 if (elem.firstElementChild) { 74 findTreeitems(elem, tree, ti); 75 } 76 77 elem = elem.nextElementSibling; 78 } 79 } 80 81 // initialize pop up menus 82 if (!this.domNode.getAttribute('role')) { 83 this.domNode.setAttribute('role', 'tree'); 84 } 85 86 findTreeitems(this.domNode, this, false); 87 88 this.updateVisibleTreeitems(); 89 90 this.firstTreeitem.domNode.tabIndex = 0; 91 92 }; 93 94 TreeLinks.prototype.setFocusToItem = function (treeitem) { 95 96 for (var i = 0; i < this.treeitems.length; i++) { 97 var ti = this.treeitems[i]; 98 99 if (ti === treeitem) { 100 ti.domNode.tabIndex = 0; 101 ti.domNode.focus(); 102 } 103 else { 104 ti.domNode.tabIndex = -1; 105 } 106 } 107 108 }; 109 110 TreeLinks.prototype.setFocusToNextItem = function (currentItem) { 111 112 var nextItem = false; 113 114 for (var i = (this.treeitems.length - 1); i >= 0; i--) { 115 var ti = this.treeitems[i]; 116 if (ti === currentItem) { 117 break; 118 } 119 if (ti.isVisible) { 120 nextItem = ti; 121 } 122 } 123 124 if (nextItem) { 125 this.setFocusToItem(nextItem); 126 } 127 128 }; 129 130 TreeLinks.prototype.setFocusToPreviousItem = function (currentItem) { 131 132 var prevItem = false; 133 134 for (var i = 0; i < this.treeitems.length; i++) { 135 var ti = this.treeitems[i]; 136 if (ti === currentItem) { 137 break; 138 } 139 if (ti.isVisible) { 140 prevItem = ti; 141 } 142 } 143 144 if (prevItem) { 145 this.setFocusToItem(prevItem); 146 } 147 }; 148 149 TreeLinks.prototype.setFocusToParentItem = function (currentItem) { 150 151 if (currentItem.groupTreeitem) { 152 this.setFocusToItem(currentItem.groupTreeitem); 153 } 154 }; 155 156 TreeLinks.prototype.setFocusToFirstItem = function () { 157 this.setFocusToItem(this.firstTreeitem); 158 }; 159 160 TreeLinks.prototype.setFocusToLastItem = function () { 161 this.setFocusToItem(this.lastTreeitem); 162 }; 163 164 TreeLinks.prototype.expandTreeitem = function (currentItem) { 165 166 if (currentItem.isExpandable) { 167 currentItem.domNode.setAttribute('aria-expanded', true); 168 this.updateVisibleTreeitems(); 169 } 170 171 }; 172 173 TreeLinks.prototype.expandAllSiblingItems = function (currentItem) { 174 for (var i = 0; i < this.treeitems.length; i++) { 175 var ti = this.treeitems[i]; 176 177 if ((ti.groupTreeitem === currentItem.groupTreeitem) && ti.isExpandable) { 178 this.expandTreeitem(ti); 179 } 180 } 181 182 }; 183 184 TreeLinks.prototype.collapseTreeitem = function (currentItem) { 185 186 var groupTreeitem = false; 187 188 if (currentItem.isExpanded()) { 189 groupTreeitem = currentItem; 190 } 191 else { 192 groupTreeitem = currentItem.groupTreeitem; 193 } 194 195 if (groupTreeitem) { 196 groupTreeitem.domNode.setAttribute('aria-expanded', false); 197 this.updateVisibleTreeitems(); 198 this.setFocusToItem(groupTreeitem); 199 } 200 201 }; 202 203 TreeLinks.prototype.updateVisibleTreeitems = function () { 204 205 this.firstTreeitem = this.treeitems[0]; 206 207 for (var i = 0; i < this.treeitems.length; i++) { 208 var ti = this.treeitems[i]; 209 210 var parent = ti.domNode.parentNode; 211 212 ti.isVisible = true; 213 214 while (parent && (parent !== this.domNode)) { 215 216 if (parent.getAttribute('aria-expanded') == 'false') { 217 ti.isVisible = false; 218 } 219 parent = parent.parentNode; 220 } 221 222 if (ti.isVisible) { 223 this.lastTreeitem = ti; 224 } 225 } 226 227 }; 228 229 TreeLinks.prototype.setFocusByFirstCharacter = function (currentItem, char) { 230 var start, index, char = char.toLowerCase(); 231 232 // Get start index for search based on position of currentItem 233 start = this.treeitems.indexOf(currentItem) + 1; 234 if (start === this.treeitems.length) { 235 start = 0; 236 } 237 238 // Check remaining slots in the menu 239 index = this.getIndexFirstChars(start, char); 240 241 // If not found in remaining slots, check from beginning 242 if (index === -1) { 243 index = this.getIndexFirstChars(0, char); 244 } 245 246 // If match was found... 247 if (index > -1) { 248 this.setFocusToItem(this.treeitems[index]); 249 } 250 }; 251 252 TreeLinks.prototype.getIndexFirstChars = function (startIndex, char) { 253 for (var i = startIndex; i < this.firstChars.length; i++) { 254 if (this.treeitems[i].isVisible) { 255 if (char === this.firstChars[i]) { 256 return i; 257 } 258 } 259 } 260 return -1; 261 }; 262 No newline at end of file -
new file src/wp-admin/js/treeitem-links.js
diff --git src/wp-admin/js/treeitem-links.js src/wp-admin/js/treeitem-links.js new file mode 100644 index 0000000000..2d0831cde0
- + 1 /* 2 * This content is licensed according to the W3C Software License at 3 * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document 4 * 5 * File: TreeitemLink.js 6 * 7 * Desc: Treeitem widget that implements ARIA Authoring Practices 8 * for a tree being used as a file viewer 9 * 10 * Author: Jon Gunderson, Ku Ja Eun and Nicholas Hoyt 11 */ 12 13 /* 14 * @constructor 15 * 16 * @desc 17 * Treeitem object for representing the state and user interactions for a 18 * treeItem widget 19 * 20 * @param node 21 * An element with the role=tree attribute 22 */ 23 24 var TreeitemLink = function (node, treeObj, group) { 25 26 // Check whether node is a DOM element 27 if (typeof node !== 'object') { 28 return; 29 } 30 31 node.tabIndex = -1; 32 this.tree = treeObj; 33 this.groupTreeitem = group; 34 this.domNode = node; 35 this.label = node.textContent.trim(); 36 this.stopDefaultClick = false; 37 38 if (node.getAttribute('aria-label')) { 39 this.label = node.getAttribute('aria-label').trim(); 40 } 41 42 this.isExpandable = false; 43 this.isVisible = false; 44 this.inGroup = false; 45 46 if (group) { 47 this.inGroup = true; 48 } 49 50 var elem = node.firstElementChild; 51 52 while (elem) { 53 54 if (elem.tagName.toLowerCase() == 'ul') { 55 elem.setAttribute('role', 'group'); 56 this.isExpandable = true; 57 break; 58 } 59 60 elem = elem.nextElementSibling; 61 } 62 63 this.keyCode = Object.freeze({ 64 RETURN: 13, 65 SPACE: 32, 66 PAGEUP: 33, 67 PAGEDOWN: 34, 68 END: 35, 69 HOME: 36, 70 LEFT: 37, 71 UP: 38, 72 RIGHT: 39, 73 DOWN: 40 74 }); 75 }; 76 77 TreeitemLink.prototype.init = function () { 78 this.domNode.tabIndex = -1; 79 80 if (!this.domNode.getAttribute('role')) { 81 this.domNode.setAttribute('role', 'treeitem'); 82 } 83 84 this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); 85 this.domNode.addEventListener('click', this.handleClick.bind(this)); 86 this.domNode.addEventListener('focus', this.handleFocus.bind(this)); 87 this.domNode.addEventListener('blur', this.handleBlur.bind(this)); 88 89 if (this.isExpandable) { 90 this.domNode.firstElementChild.addEventListener('mouseover', this.handleMouseOver.bind(this)); 91 this.domNode.firstElementChild.addEventListener('mouseout', this.handleMouseOut.bind(this)); 92 } 93 else { 94 this.domNode.addEventListener('mouseover', this.handleMouseOver.bind(this)); 95 this.domNode.addEventListener('mouseout', this.handleMouseOut.bind(this)); 96 } 97 }; 98 99 TreeitemLink.prototype.isExpanded = function () { 100 101 if (this.isExpandable) { 102 return this.domNode.getAttribute('aria-expanded') === 'true'; 103 } 104 105 return false; 106 107 }; 108 109 /* EVENT HANDLERS */ 110 111 TreeitemLink.prototype.handleKeydown = function (event) { 112 var tgt = event.currentTarget, 113 flag = false, 114 char = event.key, 115 clickEvent; 116 117 function isPrintableCharacter(str) { 118 return str.length === 1 && str.match(/\S/); 119 } 120 121 function printableCharacter(item) { 122 if (char == '*') { 123 item.tree.expandAllSiblingItems(item); 124 flag = true; 125 } 126 else { 127 if (isPrintableCharacter(char)) { 128 item.tree.setFocusByFirstCharacter(item, char); 129 flag = true; 130 } 131 } 132 } 133 134 this.stopDefaultClick = false; 135 136 if (event.altKey || event.ctrlKey || event.metaKey) { 137 return; 138 } 139 140 if (event.shift) { 141 if (event.keyCode == this.keyCode.SPACE || event.keyCode == this.keyCode.RETURN) { 142 event.stopPropagation(); 143 this.stopDefaultClick = true; 144 } 145 else { 146 if (isPrintableCharacter(char)) { 147 printableCharacter(this); 148 } 149 } 150 } 151 else { 152 switch (event.keyCode) { 153 case this.keyCode.SPACE: 154 case this.keyCode.RETURN: 155 if (this.isExpandable) { 156 if (this.isExpanded()) { 157 this.tree.collapseTreeitem(this); 158 } 159 else { 160 this.tree.expandTreeitem(this); 161 } 162 flag = true; 163 } 164 else { 165 event.stopPropagation(); 166 this.stopDefaultClick = true; 167 } 168 break; 169 170 case this.keyCode.UP: 171 this.tree.setFocusToPreviousItem(this); 172 flag = true; 173 break; 174 175 case this.keyCode.DOWN: 176 this.tree.setFocusToNextItem(this); 177 flag = true; 178 break; 179 180 case this.keyCode.RIGHT: 181 if (this.isExpandable) { 182 if (this.isExpanded()) { 183 this.tree.setFocusToNextItem(this); 184 } 185 else { 186 this.tree.expandTreeitem(this); 187 } 188 } 189 flag = true; 190 break; 191 192 case this.keyCode.LEFT: 193 if (this.isExpandable && this.isExpanded()) { 194 this.tree.collapseTreeitem(this); 195 flag = true; 196 } 197 else { 198 if (this.inGroup) { 199 this.tree.setFocusToParentItem(this); 200 flag = true; 201 } 202 } 203 break; 204 205 case this.keyCode.HOME: 206 this.tree.setFocusToFirstItem(); 207 flag = true; 208 break; 209 210 case this.keyCode.END: 211 this.tree.setFocusToLastItem(); 212 flag = true; 213 break; 214 215 default: 216 if (isPrintableCharacter(char)) { 217 printableCharacter(this); 218 } 219 break; 220 } 221 } 222 223 if (flag) { 224 event.stopPropagation(); 225 event.preventDefault(); 226 } 227 }; 228 229 TreeitemLink.prototype.handleClick = function (event) { 230 231 // only process click events that directly happened on this treeitem 232 if (event.target !== this.domNode && event.target !== this.domNode.firstElementChild) { 233 return; 234 } 235 236 if (this.isExpandable) { 237 if (this.isExpanded()) { 238 this.tree.collapseTreeitem(this); 239 } 240 else { 241 this.tree.expandTreeitem(this); 242 } 243 event.stopPropagation(); 244 } 245 }; 246 247 TreeitemLink.prototype.handleFocus = function (event) { 248 var node = this.domNode; 249 if (this.isExpandable) { 250 node = node.firstElementChild; 251 } 252 node.classList.add('focus'); 253 }; 254 255 TreeitemLink.prototype.handleBlur = function (event) { 256 var node = this.domNode; 257 if (this.isExpandable) { 258 node = node.firstElementChild; 259 } 260 node.classList.remove('focus'); 261 }; 262 263 TreeitemLink.prototype.handleMouseOver = function (event) { 264 event.currentTarget.classList.add('hover'); 265 }; 266 267 TreeitemLink.prototype.handleMouseOut = function (event) { 268 event.currentTarget.classList.remove('hover'); 269 }; 270 No newline at end of file -
src/wp-admin/plugin-editor.php
diff --git src/wp-admin/plugin-editor.php src/wp-admin/plugin-editor.php index 807cd5e09d..1ed03286ca 100644
if ( 'POST' === $_SERVER['REQUEST_METHOD'] ) { 144 144 wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function( $ ) { wp.themePluginEditor.init( $( "#template" ), %s ); } )', wp_json_encode( $settings ) ) ); 145 145 wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'wp.themePluginEditor.themeOrPlugin = "plugin";' ) ); 146 146 147 wp_enqueue_script( 'tree-links' ); 148 wp_add_inline_script( 'tree-links', 'jQuery( function( $ ) { $(\'#templateside [role="group"]\').parent().attr(\'aria-expanded\', false); $(\'#templateside .notice\').parents(\'[aria-expanded]\').attr(\'aria-expanded\', true); } )' ); 149 147 150 require_once(ABSPATH . 'wp-admin/admin-header.php'); 148 151 149 152 update_recently_edited(WP_PLUGIN_DIR . '/' . $file); … … if ( 'POST' === $_SERVER['REQUEST_METHOD'] ) { 231 234 </div> 232 235 233 236 <div id="templateside"> 234 <h2 ><?php _e( 'Plugin Files' ); ?></h2>237 <h2 id="plugin-files-label"><?php _e( 'Plugin Files' ); ?></h2> 235 238 236 239 <?php 237 240 $plugin_editable_files = array(); … … if ( 'POST' === $_SERVER['REQUEST_METHOD'] ) { 241 244 } 242 245 } 243 246 ?> 244 <ul> 245 <?php foreach ( $plugin_editable_files as $plugin_file ) : ?> 246 <li class="<?php echo esc_attr( $file === $plugin_file ? 'notice notice-info' : '' ); ?>"> 247 <a href="plugin-editor.php?file=<?php echo urlencode( $plugin_file ); ?>&plugin=<?php echo urlencode( $plugin ); ?>"><?php echo esc_html( preg_replace( '#^.+?/#', '', $plugin_file ) ); ?></a> 248 </li> 249 <?php endforeach; ?> 247 <ul role="tree" aria-labelledby="plugin-files-label"> 248 <li role="treeitem" tabindex="-1" aria-expanded="true" 249 aria-level="1" 250 aria-posinset="1" 251 aria-setsize="1"> 252 <ul role="group" style="padding-left: 0;"> 253 <?php wp_print_plugin_file_tree( wp_make_plugin_file_tree( $plugin_editable_files ) ); ?> 254 </ul> 250 255 </ul> 251 256 </div> 252 257 <form name="template" id="template" action="plugin-editor.php" method="post"> -
src/wp-admin/theme-editor.php
diff --git src/wp-admin/theme-editor.php src/wp-admin/theme-editor.php index 70629d914d..06ed64c46a 100644
foreach ( $file_types as $type ) { 89 89 } 90 90 } 91 91 92 // Move functions.php and style.css to the top. 93 if ( isset( $allowed_files['functions.php'] ) ) { 94 $allowed_files = array( 'functions.php' => $allowed_files['functions.php'] ) + $allowed_files; 95 } 96 if ( isset( $allowed_files['style.css'] ) ) { 97 $allowed_files = array( 'style.css' => $allowed_files['style.css'] ) + $allowed_files; 98 } 99 92 100 if ( empty( $file ) ) { 93 101 $relative_file = 'style.css'; 94 102 $file = $allowed_files['style.css']; … … if ( 'POST' === $_SERVER['REQUEST_METHOD'] ) { 129 137 wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function( $ ) { wp.themePluginEditor.init( $( "#template" ), %s ); } )', wp_json_encode( $settings ) ) ); 130 138 wp_add_inline_script( 'wp-theme-plugin-editor', 'wp.themePluginEditor.themeOrPlugin = "theme";' ); 131 139 140 wp_enqueue_script( 'tree-links' ); 141 wp_add_inline_script( 'tree-links', 'jQuery( function( $ ) { $(\'#templateside [role="group"]\').parent().attr(\'aria-expanded\', false); $(\'#templateside .notice\').parents(\'[aria-expanded]\').attr(\'aria-expanded\', true); } )' ); 142 132 143 require_once( ABSPATH . 'wp-admin/admin-header.php' ); 133 144 134 145 update_recently_edited( $file ); … … foreach ( wp_get_themes( array( 'errors' => null ) ) as $a_stylesheet => $a_them 205 216 if ( $theme->errors() ) 206 217 echo '<div class="error"><p><strong>' . __( 'This theme is broken.' ) . '</strong> ' . $theme->errors()->get_error_message() . '</p></div>'; 207 218 ?> 208 <div id="templateside"> 209 <?php 210 if ( $allowed_files ) : 211 $previous_file_type = ''; 212 213 foreach ( $allowed_files as $filename => $absolute_filename ) : 214 $file_type = substr( $filename, strrpos( $filename, '.' ) ); 215 216 if ( $file_type !== $previous_file_type ) { 217 if ( '' !== $previous_file_type ) { 218 echo "\t</ul>\n"; 219 } 220 221 switch ( $file_type ) { 222 case '.php': 223 if ( $has_templates || $theme->parent() ) : 224 echo "\t<h2>" . __( 'Templates' ) . "</h2>\n"; 225 if ( $theme->parent() ) { 226 echo '<p class="howto">' . sprintf( __( 'This child theme inherits templates from a parent theme, %s.' ), 227 sprintf( '<a href="%s">%s</a>', 228 self_admin_url( 'theme-editor.php?theme=' . urlencode( $theme->get_template() ) ), 229 $theme->parent()->display( 'Name' ) 230 ) 231 ) . "</p>\n"; 232 } 233 endif; 234 break; 235 case '.css': 236 echo "\t<h2>" . _x( 'Styles', 'Theme stylesheets in theme editor' ) . "</h2>\n"; 237 break; 238 default: 239 /* translators: %s: file extension */ 240 echo "\t<h2>" . sprintf( __( '%s files' ), $file_type ) . "</h2>\n"; 241 break; 242 } 243 244 echo "\t<ul>\n"; 219 <div id="templateside"> 220 <h2 id="theme-files-label"><?php _e( 'Theme Files' ); ?></h2> 221 <?php 222 if ( $has_templates || $theme->parent() ) : 223 if ( $theme->parent() ) { 224 /* translators: %s: link to edit parent theme */ 225 echo '<p class="howto">' . sprintf( __( 'This child theme inherits templates from a parent theme, %s.' ), 226 sprintf( '<a href="%s">%s</a>', 227 self_admin_url( 'theme-editor.php?theme=' . urlencode( $theme->get_template() ) ), 228 $theme->parent()->display( 'Name' ) 229 ) 230 ) . "</p>\n"; 245 231 } 246 247 $file_description = esc_html( get_file_description( $filename ) ); 248 if ( $filename !== basename( $absolute_filename ) || $file_description !== $filename ) { 249 $file_description .= '<br /><span class="nonessential">(' . esc_html( $filename ) . ')</span>'; 250 } 251 252 if ( $absolute_filename === $file ) { 253 $file_description = '<span class="notice notice-info">' . $file_description . '</span>'; 254 } 255 256 $previous_file_type = $file_type; 257 ?> 258 <li><a href="theme-editor.php?file=<?php echo urlencode( $filename ) ?>&theme=<?php echo urlencode( $stylesheet ) ?>"><?php echo $file_description; ?></a></li> 259 <?php 260 endforeach; 261 ?> 262 </ul> 263 <?php endif; ?> 232 endif; 233 ?> 234 <ul role="tree" aria-labelledby="theme-files-label"> 235 <li role="treeitem" tabindex="-1" aria-expanded="true" 236 aria-level="1" 237 aria-posinset="1" 238 aria-setsize="1"> 239 <ul role="group" style="padding-left: 0;"> 240 <?php wp_print_theme_file_tree( wp_make_theme_file_tree( $allowed_files ) ); ?> 241 </ul> 242 </li> 243 </ul> 264 244 </div> 245 265 246 <?php if ( $error ) : 266 247 echo '<div class="error"><p>' . __('Oops, no such file exists! Double check the name and try again, merci.') . '</p></div>'; 267 248 else : ?> -
src/wp-includes/script-loader.php
diff --git src/wp-includes/script-loader.php src/wp-includes/script-loader.php index 067b941e6d..3b002044f9 100644
function wp_default_scripts( &$scripts ) { 886 886 $scripts->add( 'media-gallery', "/wp-admin/js/media-gallery$suffix.js", array('jquery'), false, 1 ); 887 887 888 888 $scripts->add( 'svg-painter', '/wp-admin/js/svg-painter.js', array( 'jquery' ), false, 1 ); 889 890 $scripts->add( 'treeitem-links', "/wp-admin/js/treeitem-links$suffix.js", array( 'jquery' ), false, 1 ); 891 $scripts->add( 'tree-links', "/wp-admin/js/tree-links$suffix.js", array( 'treeitem-links' ), false, 1 ); 889 892 } 890 893 } 891 894