WordPress.org

Make WordPress Core

Ticket #24048: 24048.5.diff

File 24048.5.diff, 30.2 KB (added by WraithKenny, 4 months ago)
  • src/wp-admin/css/common.css

     
    14661466.wrap #templateside .notice { 
    14671467        display: block; 
    14681468        margin: 0; 
    1469         padding: 5px 12px; 
     1469        padding: 5px 8px; 
    14701470        font-weight: 600; 
    14711471        text-decoration: none; 
    14721472} 
    14731473 
    14741474.wrap #templateside span.notice { 
    1475   margin-left: -12px; 
     1475        margin-left: -12px; 
    14761476} 
    14771477 
    14781478#templateside li.notice a { 
    1479   padding: 0; 
     1479        padding: 0; 
    14801480} 
    14811481 
    14821482/* Update icon. */ 
     
    30363036        width: 97%; 
    30373037        height: calc( 100vh - 280px ); 
    30383038} 
     3039 
    30393040#templateside { 
    30403041        margin-top: 31px; 
    3041         overflow: scroll; 
     3042        overflow: auto; 
     3043        padding: 2px; 
     3044        height: calc(100vh - 280px); 
    30423045} 
     3046#templateside ul ul { 
     3047        padding-left: 12px; 
     3048} 
    30433049 
     3050/* 
     3051 * Styles for Theme and Plugin editors. 
     3052 */ 
     3053 
     3054/* Hide collapsed items. */ 
     3055[role="treeitem"][aria-expanded="false"] > ul { 
     3056        display: none; 
     3057} 
     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} 
     3077 
     3078/* Remove outline, and create our own focus and hover styles */ 
     3079[role="treeitem"] { 
     3080        outline: 0; 
     3081} 
     3082[role="treeitem"] .folder-label.focus { 
     3083        color: #124964; 
     3084        box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, .8); 
     3085} 
     3086[role="treeitem"].hover, 
     3087[role="treeitem"] .folder-label.hover { 
     3088        background-color: #DDDDDD; 
     3089} 
     3090 
     3091.tree-folder { 
     3092        margin: 0; 
     3093        position: relative; 
     3094} 
     3095[role="treeitem"] li { 
     3096        position: relative; 
     3097} 
     3098 
     3099/* Styles for folder indicators/depth */ 
     3100.tree-folder .tree-folder::after { 
     3101        content: ' '; 
     3102        display: block; 
     3103        position: absolute; 
     3104        left: 2px; 
     3105        border-left: 1px solid #ccc; 
     3106        top: -13px; 
     3107        bottom: 10px; 
     3108} 
     3109.tree-folder > li::before { 
     3110        content: ' '; 
     3111        position: absolute; 
     3112        display: block; 
     3113        border-left: 1px solid #ccc; 
     3114        left: 2px; 
     3115        top: -5px; 
     3116        height: 18px; 
     3117        width: 7px; 
     3118        border-bottom: 1px solid #ccc; 
     3119} 
     3120.tree-folder > li::after { 
     3121        content: ' '; 
     3122        position: absolute; 
     3123        display: block; 
     3124        border-left: 1px solid #ccc; 
     3125        left: 2px; 
     3126        bottom: -7px; 
     3127        top: 0; 
     3128} 
     3129 
     3130/* current-file needs to adjustment for .notice styles */ 
     3131#templateside .current-file { 
     3132        margin: -4px 0 -2px; 
     3133} 
     3134.tree-folder > .current-file::before { 
     3135        left: 4px; 
     3136        height: 15px; 
     3137        width: 6px; 
     3138        border-left: none; 
     3139        top: 3px; 
     3140} 
     3141.tree-folder > .current-file::after { 
     3142        bottom: -4px; 
     3143        height: 7px; 
     3144        left: 2px; 
     3145        top: auto; 
     3146} 
     3147 
     3148/* Lines shouldn't continue on last item */ 
     3149.tree-folder > li:last-child::after, 
     3150.tree-folder li:last-child > .tree-folder::after { 
     3151        display: none; 
     3152} 
     3153 
     3154 
    30443155#theme-plugin-editor-label { 
    30453156        display: inline-block; 
    30463157        margin-bottom: 1em; 
     
    36533764                width: 100%; 
    36543765        } 
    36553766 
     3767        #templateside ul ul { 
     3768                padding-left: 1.5em; 
     3769        } 
     3770        [role="treeitem"] .folder-label { 
     3771                display: block; 
     3772                padding: 5px; 
     3773        } 
     3774        .tree-folder > li::before, 
     3775        .tree-folder > li::after, 
     3776        .tree-folder .tree-folder::after { 
     3777                left: -8px; 
     3778        } 
     3779        .tree-folder > li::before { 
     3780                top: 0px; 
     3781                height: 13px; 
     3782        } 
     3783        .tree-folder > .current-file::before { 
     3784                left: -5px; 
     3785                top: 7px; 
     3786                width: 4px; 
     3787        } 
     3788        .tree-folder > .current-file::after { 
     3789                height: 9px; 
     3790                left: -8px; 
     3791        } 
     3792        .wrap #templateside span.notice { 
     3793                margin-left: -14px; 
     3794        } 
     3795 
    36563796        .fileedit-sub .alignright { 
    36573797                margin-top: 15px; 
    36583798        } 
  • src/wp-admin/includes/misc.php

     
    270270} 
    271271 
    272272/** 
     273 * Makes a tree structure for the Theme Editor's file list. 
     274 * 
     275 * @since 4.9.0 
     276 * @access private 
     277 * 
     278 * @param string $allowed_files List of theme file paths. 
     279 * 
     280 * @return array Tree structure for listing theme files. 
     281 */ 
     282function wp_make_theme_file_tree( $allowed_files ) { 
     283        $tree_list = array(); 
     284        foreach ( $allowed_files as $file_name => $absolute_filename ) { 
     285                $list = explode( '/', $file_name ); 
     286                $last_dir = &$tree_list; 
     287                foreach ( $list as $dir ) { 
     288                        $last_dir =& $last_dir[ $dir ]; 
     289                } 
     290                $last_dir = $file_name; 
     291        } 
     292        return $tree_list; 
     293} 
     294 
     295/** 
     296 * Outputs the formatted file list for the Theme Editor. 
     297 * 
     298 * @since 4.9.0 
     299 * @access private 
     300 * 
     301 * @param array  $tree  List of file/folder paths. 
     302 * @param string $label Name of file or folder to print. 
     303 * @param int    $level Current level. 
     304 * @param int    $index Current level. 
     305 * @param int    $size  Current level. 
     306 */ 
     307function wp_print_theme_file_tree( $tree, $label = false, $level = 2, $index = 1, $size = 1 ) { 
     308        global $relative_file, $stylesheet; 
     309 
     310        if ( is_array( $tree ) ) { 
     311                $index = 0; 
     312                $size = count( $tree ); 
     313                foreach ( $tree as $label => $theme_file ) : 
     314                        $index++; 
     315                        if ( ! is_array( $theme_file ) ) { 
     316                                wp_print_theme_file_tree( $theme_file, $label, $level, $index, $size ); 
     317                                continue; 
     318                        } 
     319                        ?> 
     320                        <li role="treeitem" aria-expanded="true" tabindex="-1" 
     321                                aria-level="<?php echo (string) $level; ?>" 
     322                                aria-setsize="<?php echo (string) $size; ?>" 
     323                                aria-posinset="<?php echo (string) $index; ?>"> 
     324                                <span class="folder-label"><?php echo esc_html( $label ); ?> <span class="screen-reader-text">folder</span><span aria-hidden="true" class="icon"></span></span> 
     325                                <ul role="group" class="tree-folder"><?php wp_print_theme_file_tree( $theme_file, false, $level + 1, $index, $size ); ?></ul> 
     326                        </li> 
     327                        <?php 
     328                endforeach; 
     329        } else { 
     330                $filename = $tree; 
     331                ?> 
     332                <li role="none" class="<?php echo $relative_file === $filename ? 'current-file' : ''; ?>"> 
     333                        <a role="treeitem" tabindex="<?php echo $relative_file === $filename ? '0' : '-1'; ?>" 
     334                                href="theme-editor.php?file=<?php echo urlencode( $tree ); ?>&amp;theme=<?php echo urlencode( $stylesheet ); ?>" 
     335                                aria-level="<?php echo (string) $level; ?>" 
     336                                aria-setsize="<?php echo (string) $size; ?>" 
     337                                aria-posinset="<?php echo (string) $index; ?>"> 
     338                                <?php 
     339                                $file_description = esc_html( get_file_description( $filename ) ); 
     340                                if ( $file_description !== $filename && $file_description !== basename($filename) ) { 
     341                                        $file_description .= '<br /><span class="nonessential">(' . esc_html( $filename ) . ')</span>'; 
     342                                } 
     343 
     344                                if ( $relative_file === $filename ) { 
     345                                        echo '<span class="notice notice-info">' . $file_description . '</span>'; 
     346                                } else { 
     347                                        echo $file_description; 
     348                                } 
     349                                ?> 
     350                        </a> 
     351                </li> 
     352                <?php 
     353        } 
     354} 
     355 
     356/** 
     357 * Makes a tree structure for the Plugin Editor's file list. 
     358 * 
     359 * @since 4.9.0 
     360 * @access private 
     361 * 
     362 * @param string $plugin_editable_files List of plugin file paths. 
     363 * 
     364 * @return array Tree structure for listing plugin files. 
     365 */ 
     366function wp_make_plugin_file_tree( $plugin_editable_files ) { 
     367        $tree_list = array(); 
     368        foreach ( $plugin_editable_files as $plugin_file ) { 
     369                $list = explode( '/', preg_replace( '#^.+?/#', '', $plugin_file ) ); 
     370                $last_dir = &$tree_list; 
     371                foreach ( $list as $dir ) { 
     372                        $last_dir =& $last_dir[ $dir ]; 
     373                } 
     374                $last_dir = $plugin_file; 
     375        } 
     376        return $tree_list; 
     377} 
     378 
     379/** 
     380 * Outputs the formatted file list for the Plugin Editor. 
     381 * 
     382 * @since 4.9.0 
     383 * @access private 
     384 * 
     385 * @param array  $tree  List of file/folder paths. 
     386 * @param string $label Name of file or folder to print. 
     387 * @param int    $level Current level. 
     388 * @param int    $index Current level. 
     389 * @param int    $size  Current level. 
     390 */ 
     391function wp_print_plugin_file_tree( $tree, $label = false, $level = 2, $index = 1, $size = 1 ) { 
     392        global $file, $plugin; 
     393        if ( is_array( $tree ) ) { 
     394                $index = 0; 
     395                $size = count( $tree ); 
     396                foreach ( $tree as $label => $plugin_file ) : 
     397                        $index++; 
     398                        if ( ! is_array( $plugin_file ) ) { 
     399                                wp_print_plugin_file_tree( $plugin_file, $label, $level, $index, $size ); 
     400                                continue; 
     401                        } 
     402                        ?> 
     403                        <li role="treeitem" aria-expanded="true" tabindex="-1" 
     404                                aria-level="<?php echo (string) $level; ?>" 
     405                                aria-setsize="<?php echo (string) $size; ?>" 
     406                                aria-posinset="<?php echo (string) $index; ?>"> 
     407                                <span class="folder-label"><?php echo esc_html( $label ); ?> <span class="screen-reader-text">folder</span><span aria-hidden="true" class="icon"></span></span> 
     408                                <ul role="group" class="tree-folder"><?php wp_print_plugin_file_tree( $plugin_file, false, $level + 1, $index, $size ); ?></ul> 
     409                        </li> 
     410                        <?php 
     411                endforeach; 
     412        } else { 
     413                ?> 
     414                <li role="none" class="<?php echo $file === $tree ? 'current-file' : ''; ?>"> 
     415                        <a role="treeitem" tabindex="<?php echo $file === $tree ? '0' : '-1'; ?>" 
     416                                href="plugin-editor.php?file=<?php echo urlencode( $tree ); ?>&amp;plugin=<?php echo urlencode( $plugin ); ?>" 
     417                                aria-level="<?php echo (string) $level; ?>" 
     418                                aria-setsize="<?php echo (string) $size; ?>" 
     419                                aria-posinset="<?php echo (string) $index; ?>"> 
     420                                <?php 
     421                                if ( $file === $tree ) { 
     422                                        echo '<span class="notice notice-info">' . esc_html( $label ) . '</span>'; 
     423                                } else { 
     424                                        echo esc_html( $label ); 
     425                                } 
     426                                ?> 
     427                        </a> 
     428                </li> 
     429                <?php 
     430        } 
     431} 
     432 
     433/** 
    273434 * Flushes rewrite rules if siteurl, home or page_on_front changed. 
    274435 * 
    275436 * @since 2.1.0 
  • src/wp-admin/js/tree-links.js

     
     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 
     19window.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 
     41var 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 
     57TreeLinks.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 
     94TreeLinks.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 
     110TreeLinks.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 
     130TreeLinks.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 
     149TreeLinks.prototype.setFocusToParentItem = function (currentItem) { 
     150 
     151    if (currentItem.groupTreeitem) { 
     152        this.setFocusToItem(currentItem.groupTreeitem); 
     153    } 
     154}; 
     155 
     156TreeLinks.prototype.setFocusToFirstItem = function () { 
     157    this.setFocusToItem(this.firstTreeitem); 
     158}; 
     159 
     160TreeLinks.prototype.setFocusToLastItem = function () { 
     161    this.setFocusToItem(this.lastTreeitem); 
     162}; 
     163 
     164TreeLinks.prototype.expandTreeitem = function (currentItem) { 
     165 
     166    if (currentItem.isExpandable) { 
     167        currentItem.domNode.setAttribute('aria-expanded', true); 
     168        this.updateVisibleTreeitems(); 
     169    } 
     170 
     171}; 
     172 
     173TreeLinks.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 
     184TreeLinks.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 
     203TreeLinks.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 
     229TreeLinks.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 
     252TreeLinks.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 
  • src/wp-admin/js/treeitem-links.js

     
     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 
     24var 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 
     77TreeitemLink.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 
     99TreeitemLink.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 
     111TreeitemLink.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 
     229TreeitemLink.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 
     247TreeitemLink.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 
     255TreeitemLink.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 
     263TreeitemLink.prototype.handleMouseOver = function (event) { 
     264    event.currentTarget.classList.add('hover'); 
     265}; 
     266 
     267TreeitemLink.prototype.handleMouseOut = function (event) { 
     268    event.currentTarget.classList.remove('hover'); 
     269}; 
     270 No newline at end of file 
  • src/wp-admin/plugin-editor.php

     
    144144        wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function( $ ) { wp.themePluginEditor.init( $( "#template" ), %s ); } )', wp_json_encode( $settings ) ) ); 
    145145        wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'wp.themePluginEditor.themeOrPlugin = "plugin";' ) ); 
    146146 
     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 
    147150        require_once(ABSPATH . 'wp-admin/admin-header.php'); 
    148151 
    149152        update_recently_edited(WP_PLUGIN_DIR . '/' . $file); 
     
    231234</div> 
    232235 
    233236<div id="templateside"> 
    234         <h2><?php _e( 'Plugin Files' ); ?></h2> 
     237        <h2 id="plugin-files-label"><?php _e( 'Plugin Files' ); ?></h2> 
    235238 
    236239        <?php 
    237240        $plugin_editable_files = array(); 
     
    241244                } 
    242245        } 
    243246        ?> 
    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 ); ?>&amp;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> 
    250255        </ul> 
    251256</div> 
    252257<form name="template" id="template" action="plugin-editor.php" method="post"> 
  • src/wp-admin/theme-editor.php

     
    8989        } 
    9090} 
    9191 
     92// Move functions.php and style.css to the top. 
     93if ( isset( $allowed_files['functions.php'] ) ) { 
     94        $allowed_files = array( 'functions.php' => $allowed_files['functions.php'] ) + $allowed_files; 
     95} 
     96if ( isset( $allowed_files['style.css'] ) ) { 
     97        $allowed_files = array( 'style.css' => $allowed_files['style.css'] ) + $allowed_files; 
     98} 
     99 
    92100if ( empty( $file ) ) { 
    93101        $relative_file = 'style.css'; 
    94102        $file = $allowed_files['style.css']; 
     
    129137        wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function( $ ) { wp.themePluginEditor.init( $( "#template" ), %s ); } )', wp_json_encode( $settings ) ) ); 
    130138        wp_add_inline_script( 'wp-theme-plugin-editor', 'wp.themePluginEditor.themeOrPlugin = "theme";' ); 
    131139 
     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 
    132143        require_once( ABSPATH . 'wp-admin/admin-header.php' ); 
    133144 
    134145        update_recently_edited( $file ); 
     
    205216if ( $theme->errors() ) 
    206217        echo '<div class="error"><p><strong>' . __( 'This theme is broken.' ) . '</strong> ' . $theme->errors()->get_error_message() . '</p></div>'; 
    207218?> 
    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"; 
    245231                } 
     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> 
     244</div> 
    246245 
    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 ) ?>&amp;theme=<?php echo urlencode( $stylesheet ) ?>"><?php echo $file_description; ?></a></li> 
    259 <?php 
    260         endforeach; 
    261 ?> 
    262 </ul> 
    263 <?php endif; ?> 
    264 </div> 
    265246<?php if ( $error ) : 
    266247        echo '<div class="error"><p>' . __('Oops, no such file exists! Double check the name and try again, merci.') . '</p></div>'; 
    267248else : ?> 
  • src/wp-includes/script-loader.php

     
    886886                $scripts->add( 'media-gallery', "/wp-admin/js/media-gallery$suffix.js", array('jquery'), false, 1 ); 
    887887 
    888888                $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 ); 
    889892        } 
    890893} 
    891894