Make WordPress Core

Ticket #24048: 24048.9.diff

File 24048.9.diff, 28.3 KB (added by WraithKenny, 7 years 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        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
    30443156#theme-plugin-editor-label {
    30453157        display: inline-block;
    30463158        margin-bottom: 1em;
     
    36533765                width: 100%;
    36543766        }
    36553767
     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
    36563797        .fileedit-sub .alignright {
    36573798                margin-top: 15px;
    36583799        }
  • 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 array $allowed_files List of theme file paths.
     279 * @return array Tree structure for listing theme files.
     280 */
     281function 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|string $tree  List of file/folder paths, or filename.
     301 * @param int          $level The aria-level for the current iteration.
     302 * @param int          $size  The aria-setsize for the current iteration.
     303 * @param int          $index The aria-posinset for the current iteration.
     304 */
     305function wp_print_theme_file_tree( $tree, $level = 2, $size = 1, $index = 1 ) {
     306        global $relative_file, $stylesheet;
     307
     308        if ( is_array( $tree ) ) {
     309                $index = 0;
     310                $size = count( $tree );
     311                foreach ( $tree as $label => $theme_file ) :
     312                        $index++;
     313                        if ( ! is_array( $theme_file ) ) {
     314                                wp_print_theme_file_tree( $theme_file, $level, $index, $size );
     315                                continue;
     316                        }
     317                        ?>
     318                        <li role="treeitem" aria-expanded="true" tabindex="-1"
     319                                aria-level="<?php echo esc_attr( $level ); ?>"
     320                                aria-setsize="<?php echo esc_attr( $size ); ?>"
     321                                aria-posinset="<?php echo esc_attr( $index ); ?>">
     322                                <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>
     323                                <ul role="group" class="tree-folder"><?php wp_print_theme_file_tree( $theme_file, $level + 1, $index, $size ); ?></ul>
     324                        </li>
     325                        <?php
     326                endforeach;
     327        } else {
     328                $filename = $tree;
     329                $url = add_query_arg(
     330                        array(
     331                                'file' => rawurlencode( $tree ),
     332                                'theme' => rawurlencode( $stylesheet ),
     333                        ),
     334                        admin_url( 'theme-editor.php' )
     335                );
     336                ?>
     337                <li role="none" class="<?php echo esc_attr( $relative_file === $filename ? 'current-file' : '' ); ?>">
     338                        <a role="treeitem" tabindex="<?php echo esc_attr( $relative_file === $filename ? '0' : '-1' ); ?>"
     339                                href="<?php echo esc_url( $url ); ?>"
     340                                aria-level="<?php echo esc_attr( $level ); ?>"
     341                                aria-setsize="<?php echo esc_attr( $size ); ?>"
     342                                aria-posinset="<?php echo esc_attr( $index ); ?>">
     343                                <?php
     344                                $file_description = esc_html( get_file_description( $filename ) );
     345                                if ( $file_description !== $filename && basename( $filename ) !== $file_description ) {
     346                                        $file_description .= '<br /><span class="nonessential">(' . esc_html( $filename ) . ')</span>';
     347                                }
     348
     349                                if ( $relative_file === $filename ) {
     350                                        echo '<span class="notice notice-info">' . $file_description . '</span>';
     351                                } else {
     352                                        echo $file_description;
     353                                }
     354                                ?>
     355                        </a>
     356                </li>
     357                <?php
     358        }
     359}
     360
     361/**
     362 * Makes a tree structure for the Plugin Editor's file list.
     363 *
     364 * @since 4.9.0
     365 * @access private
     366 *
     367 * @param string $plugin_editable_files List of plugin file paths.
     368 *
     369 * @return array Tree structure for listing plugin files.
     370 */
     371function wp_make_plugin_file_tree( $plugin_editable_files ) {
     372        $tree_list = array();
     373        foreach ( $plugin_editable_files as $plugin_file ) {
     374                $list = explode( '/', preg_replace( '#^.+?/#', '', $plugin_file ) );
     375                $last_dir = &$tree_list;
     376                foreach ( $list as $dir ) {
     377                        $last_dir =& $last_dir[ $dir ];
     378                }
     379                $last_dir = $plugin_file;
     380        }
     381        return $tree_list;
     382}
     383
     384/**
     385 * Outputs the formatted file list for the Plugin Editor.
     386 *
     387 * @since 4.9.0
     388 * @access private
     389 *
     390 * @param array|string $tree  List of file/folder paths, or filename.
     391 * @param string       $label Name of file or folder to print.
     392 * @param int          $level The aria-level for the current iteration.
     393 * @param int          $size  The aria-setsize for the current iteration.
     394 * @param int          $index The aria-posinset for the current iteration.
     395 */
     396function wp_print_plugin_file_tree( $tree, $label = '', $level = 2, $size = 1, $index = 1 ) {
     397        global $file, $plugin;
     398        if ( is_array( $tree ) ) {
     399                $index = 0;
     400                $size = count( $tree );
     401                foreach ( $tree as $label => $plugin_file ) :
     402                        $index++;
     403                        if ( ! is_array( $plugin_file ) ) {
     404                                wp_print_plugin_file_tree( $plugin_file, $label, $level, $index, $size );
     405                                continue;
     406                        }
     407                        ?>
     408                        <li role="treeitem" aria-expanded="true" tabindex="-1"
     409                                aria-level="<?php echo esc_attr( $level ); ?>"
     410                                aria-setsize="<?php echo esc_attr( $size ); ?>"
     411                                aria-posinset="<?php echo esc_attr( $index ); ?>">
     412                                <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>
     413                                <ul role="group" class="tree-folder"><?php wp_print_plugin_file_tree( $plugin_file, '', $level + 1, $index, $size ); ?></ul>
     414                        </li>
     415                        <?php
     416                endforeach;
     417        } else {
     418                $url = add_query_arg(
     419                        array(
     420                                'file' => rawurlencode( $tree ),
     421                                'plugin' => rawurlencode( $plugin ),
     422                        ),
     423                        admin_url( 'plugin-editor.php' )
     424                );
     425                ?>
     426                <li role="none" class="<?php echo esc_attr( $file === $tree ? 'current-file' : '' ); ?>">
     427                        <a role="treeitem" tabindex="<?php echo esc_attr( $file === $tree ? '0' : '-1' ); ?>"
     428                                href="<?php echo esc_url( $url ); ?>"
     429                                aria-level="<?php echo esc_attr( $level ); ?>"
     430                                aria-setsize="<?php echo esc_attr( $size ); ?>"
     431                                aria-posinset="<?php echo esc_attr( $index ); ?>">
     432                                <?php
     433                                if ( $file === $tree ) {
     434                                        echo '<span class="notice notice-info">' . esc_html( $label ) . '</span>';
     435                                } else {
     436                                        echo esc_html( $label );
     437                                }
     438                                ?>
     439                        </a>
     440                </li>
     441                <?php
     442        }
     443}
     444
     445/**
    273446 * Flushes rewrite rules if siteurl, home or page_on_front changed.
    274447 *
    275448 * @since 2.1.0
  • src/wp-admin/js/theme-plugin-editor.js

     
    6666                        } );
    6767                }
    6868
     69                $( component.initFileBrowser )
     70
    6971                $( window ).on( 'beforeunload', function() {
    7072                        if ( component.dirty ) {
    7173                                return component.l10n.saveAlert;
     
    350352                component.instance = editor;
    351353        };
    352354
     355        /**
     356         * Creates a new TreeitemLink.
     357         *
     358         * @since 4.9.0
     359         * @class
     360         * @private
     361         * @see {@link https://www.w3.org/TR/wai-aria-practices-1.1/examples/treeview/treeview-2/treeview-2b.html|W3C Treeview Example}
     362         * @license W3C-20150513
     363         */
     364        var TreeitemLink = (function () {
     365                /**
     366                 *   This content is licensed according to the W3C Software License at
     367                 *   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
     368                 *
     369                 *   File:   TreeitemLink.js
     370                 *
     371                 *   Desc:   Treeitem widget that implements ARIA Authoring Practices
     372                 *           for a tree being used as a file viewer
     373                 *
     374                 *   Author: Jon Gunderson, Ku Ja Eun and Nicholas Hoyt
     375                 */
     376
     377                /**
     378                 *   @constructor
     379                 *
     380                 *   @desc
     381                 *       Treeitem object for representing the state and user interactions for a
     382                 *       treeItem widget
     383                 *
     384                 *   @param node
     385                 *       An element with the role=tree attribute
     386                 */
     387
     388                var TreeitemLink = function (node, treeObj, group) {
     389
     390                        // Check whether node is a DOM element
     391                        if (typeof node !== 'object') {
     392                                return;
     393                        }
     394
     395                        node.tabIndex = -1;
     396                        this.tree = treeObj;
     397                        this.groupTreeitem = group;
     398                        this.domNode = node;
     399                        this.label = node.textContent.trim();
     400                        this.stopDefaultClick = false;
     401
     402                        if (node.getAttribute('aria-label')) {
     403                                this.label = node.getAttribute('aria-label').trim();
     404                        }
     405
     406                        this.isExpandable = false;
     407                        this.isVisible = false;
     408                        this.inGroup = false;
     409
     410                        if (group) {
     411                                this.inGroup = true;
     412                        }
     413
     414                        var elem = node.firstElementChild;
     415
     416                        while (elem) {
     417
     418                                if (elem.tagName.toLowerCase() == 'ul') {
     419                                        elem.setAttribute('role', 'group');
     420                                        this.isExpandable = true;
     421                                        break;
     422                                }
     423
     424                                elem = elem.nextElementSibling;
     425                        }
     426
     427                        this.keyCode = Object.freeze({
     428                                RETURN: 13,
     429                                SPACE: 32,
     430                                PAGEUP: 33,
     431                                PAGEDOWN: 34,
     432                                END: 35,
     433                                HOME: 36,
     434                                LEFT: 37,
     435                                UP: 38,
     436                                RIGHT: 39,
     437                                DOWN: 40
     438                        });
     439                };
     440
     441                TreeitemLink.prototype.init = function () {
     442                        this.domNode.tabIndex = -1;
     443
     444                        if (!this.domNode.getAttribute('role')) {
     445                                this.domNode.setAttribute('role', 'treeitem');
     446                        }
     447
     448                        this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
     449                        this.domNode.addEventListener('click', this.handleClick.bind(this));
     450                        this.domNode.addEventListener('focus', this.handleFocus.bind(this));
     451                        this.domNode.addEventListener('blur', this.handleBlur.bind(this));
     452
     453                        if (this.isExpandable) {
     454                                this.domNode.firstElementChild.addEventListener('mouseover', this.handleMouseOver.bind(this));
     455                                this.domNode.firstElementChild.addEventListener('mouseout', this.handleMouseOut.bind(this));
     456                        }
     457                        else {
     458                                this.domNode.addEventListener('mouseover', this.handleMouseOver.bind(this));
     459                                this.domNode.addEventListener('mouseout', this.handleMouseOut.bind(this));
     460                        }
     461                };
     462
     463                TreeitemLink.prototype.isExpanded = function () {
     464
     465                        if (this.isExpandable) {
     466                                return this.domNode.getAttribute('aria-expanded') === 'true';
     467                        }
     468
     469                        return false;
     470
     471                };
     472
     473                /* EVENT HANDLERS */
     474
     475                TreeitemLink.prototype.handleKeydown = function (event) {
     476                        var tgt = event.currentTarget,
     477                                flag = false,
     478                                char = event.key,
     479                                clickEvent;
     480
     481                        function isPrintableCharacter(str) {
     482                                return str.length === 1 && str.match(/\S/);
     483                        }
     484
     485                        function printableCharacter(item) {
     486                                if (char == '*') {
     487                                        item.tree.expandAllSiblingItems(item);
     488                                        flag = true;
     489                                }
     490                                else {
     491                                        if (isPrintableCharacter(char)) {
     492                                                item.tree.setFocusByFirstCharacter(item, char);
     493                                                flag = true;
     494                                        }
     495                                }
     496                        }
     497
     498                        this.stopDefaultClick = false;
     499
     500                        if (event.altKey || event.ctrlKey || event.metaKey) {
     501                                return;
     502                        }
     503
     504                        if (event.shift) {
     505                                if (event.keyCode == this.keyCode.SPACE || event.keyCode == this.keyCode.RETURN) {
     506                                        event.stopPropagation();
     507                                        this.stopDefaultClick = true;
     508                                }
     509                                else {
     510                                        if (isPrintableCharacter(char)) {
     511                                                printableCharacter(this);
     512                                        }
     513                                }
     514                        }
     515                        else {
     516                                switch (event.keyCode) {
     517                                        case this.keyCode.SPACE:
     518                                        case this.keyCode.RETURN:
     519                                                if (this.isExpandable) {
     520                                                        if (this.isExpanded()) {
     521                                                                this.tree.collapseTreeitem(this);
     522                                                        }
     523                                                        else {
     524                                                                this.tree.expandTreeitem(this);
     525                                                        }
     526                                                        flag = true;
     527                                                }
     528                                                else {
     529                                                        event.stopPropagation();
     530                                                        this.stopDefaultClick = true;
     531                                                }
     532                                                break;
     533
     534                                        case this.keyCode.UP:
     535                                                this.tree.setFocusToPreviousItem(this);
     536                                                flag = true;
     537                                                break;
     538
     539                                        case this.keyCode.DOWN:
     540                                                this.tree.setFocusToNextItem(this);
     541                                                flag = true;
     542                                                break;
     543
     544                                        case this.keyCode.RIGHT:
     545                                                if (this.isExpandable) {
     546                                                        if (this.isExpanded()) {
     547                                                                this.tree.setFocusToNextItem(this);
     548                                                        }
     549                                                        else {
     550                                                                this.tree.expandTreeitem(this);
     551                                                        }
     552                                                }
     553                                                flag = true;
     554                                                break;
     555
     556                                        case this.keyCode.LEFT:
     557                                                if (this.isExpandable && this.isExpanded()) {
     558                                                        this.tree.collapseTreeitem(this);
     559                                                        flag = true;
     560                                                }
     561                                                else {
     562                                                        if (this.inGroup) {
     563                                                                this.tree.setFocusToParentItem(this);
     564                                                                flag = true;
     565                                                        }
     566                                                }
     567                                                break;
     568
     569                                        case this.keyCode.HOME:
     570                                                this.tree.setFocusToFirstItem();
     571                                                flag = true;
     572                                                break;
     573
     574                                        case this.keyCode.END:
     575                                                this.tree.setFocusToLastItem();
     576                                                flag = true;
     577                                                break;
     578
     579                                        default:
     580                                                if (isPrintableCharacter(char)) {
     581                                                        printableCharacter(this);
     582                                                }
     583                                                break;
     584                                }
     585                        }
     586
     587                        if (flag) {
     588                                event.stopPropagation();
     589                                event.preventDefault();
     590                        }
     591                };
     592
     593                TreeitemLink.prototype.handleClick = function (event) {
     594
     595                        // only process click events that directly happened on this treeitem
     596                        if (event.target !== this.domNode && event.target !== this.domNode.firstElementChild) {
     597                                return;
     598                        }
     599
     600                        if (this.isExpandable) {
     601                                if (this.isExpanded()) {
     602                                        this.tree.collapseTreeitem(this);
     603                                }
     604                                else {
     605                                        this.tree.expandTreeitem(this);
     606                                }
     607                                event.stopPropagation();
     608                        }
     609                };
     610
     611                TreeitemLink.prototype.handleFocus = function (event) {
     612                        var node = this.domNode;
     613                        if (this.isExpandable) {
     614                                node = node.firstElementChild;
     615                        }
     616                        node.classList.add('focus');
     617                };
     618
     619                TreeitemLink.prototype.handleBlur = function (event) {
     620                        var node = this.domNode;
     621                        if (this.isExpandable) {
     622                                node = node.firstElementChild;
     623                        }
     624                        node.classList.remove('focus');
     625                };
     626
     627                TreeitemLink.prototype.handleMouseOver = function (event) {
     628                        event.currentTarget.classList.add('hover');
     629                };
     630
     631                TreeitemLink.prototype.handleMouseOut = function (event) {
     632                        event.currentTarget.classList.remove('hover');
     633                };
     634
     635                return TreeitemLink;
     636        })();
     637
     638        /**
     639         * Creates a new TreeLinks.
     640         *
     641         * @since 4.9.0
     642         * @class
     643         * @private
     644         * @see {@link https://www.w3.org/TR/wai-aria-practices-1.1/examples/treeview/treeview-2/treeview-2b.html|W3C Treeview Example}
     645         * @license W3C-20150513
     646         */
     647        var TreeLinks = (function () {
     648                /*
     649                 *   This content is licensed according to the W3C Software License at
     650                 *   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
     651                 *
     652                 *   File:   TreeLinks.js
     653                 *
     654                 *   Desc:   Tree widget that implements ARIA Authoring Practices
     655                 *           for a tree being used as a file viewer
     656                 *
     657                 *   Author: Jon Gunderson, Ku Ja Eun and Nicholas Hoyt
     658                 */
     659
     660                /*
     661                 *   @constructor
     662                 *
     663                 *   @desc
     664                 *       Tree item object for representing the state and user interactions for a
     665                 *       tree widget
     666                 *
     667                 *   @param node
     668                 *       An element with the role=tree attribute
     669                 */
     670
     671                var TreeLinks = function (node) {
     672                        // Check whether node is a DOM element
     673                        if (typeof node !== 'object') {
     674                                return;
     675                        }
     676
     677                        this.domNode = node;
     678
     679                        this.treeitems = [];
     680                        this.firstChars = [];
     681
     682                        this.firstTreeitem = null;
     683                        this.lastTreeitem = null;
     684
     685                };
     686
     687                TreeLinks.prototype.init = function () {
     688
     689                        function findTreeitems(node, tree, group) {
     690
     691                                var elem = node.firstElementChild;
     692                                var ti = group;
     693
     694                                while (elem) {
     695
     696                                        if ((elem.tagName.toLowerCase() === 'li' && elem.firstElementChild.tagName.toLowerCase() === 'span') || elem.tagName.toLowerCase() === 'a') {
     697                                                ti = new TreeitemLink(elem, tree, group);
     698                                                ti.init();
     699                                                tree.treeitems.push(ti);
     700                                                tree.firstChars.push(ti.label.substring(0, 1).toLowerCase());
     701                                        }
     702
     703                                        if (elem.firstElementChild) {
     704                                                findTreeitems(elem, tree, ti);
     705                                        }
     706
     707                                        elem = elem.nextElementSibling;
     708                                }
     709                        }
     710
     711                        // initialize pop up menus
     712                        if (!this.domNode.getAttribute('role')) {
     713                                this.domNode.setAttribute('role', 'tree');
     714                        }
     715
     716                        findTreeitems(this.domNode, this, false);
     717
     718                        this.updateVisibleTreeitems();
     719
     720                        this.firstTreeitem.domNode.tabIndex = 0;
     721
     722                };
     723
     724                TreeLinks.prototype.setFocusToItem = function (treeitem) {
     725
     726                        for (var i = 0; i < this.treeitems.length; i++) {
     727                                var ti = this.treeitems[i];
     728
     729                                if (ti === treeitem) {
     730                                        ti.domNode.tabIndex = 0;
     731                                        ti.domNode.focus();
     732                                }
     733                                else {
     734                                        ti.domNode.tabIndex = -1;
     735                                }
     736                        }
     737
     738                };
     739
     740                TreeLinks.prototype.setFocusToNextItem = function (currentItem) {
     741
     742                        var nextItem = false;
     743
     744                        for (var i = (this.treeitems.length - 1); i >= 0; i--) {
     745                                var ti = this.treeitems[i];
     746                                if (ti === currentItem) {
     747                                        break;
     748                                }
     749                                if (ti.isVisible) {
     750                                        nextItem = ti;
     751                                }
     752                        }
     753
     754                        if (nextItem) {
     755                                this.setFocusToItem(nextItem);
     756                        }
     757
     758                };
     759
     760                TreeLinks.prototype.setFocusToPreviousItem = function (currentItem) {
     761
     762                        var prevItem = false;
     763
     764                        for (var i = 0; i < this.treeitems.length; i++) {
     765                                var ti = this.treeitems[i];
     766                                if (ti === currentItem) {
     767                                        break;
     768                                }
     769                                if (ti.isVisible) {
     770                                        prevItem = ti;
     771                                }
     772                        }
     773
     774                        if (prevItem) {
     775                                this.setFocusToItem(prevItem);
     776                        }
     777                };
     778
     779                TreeLinks.prototype.setFocusToParentItem = function (currentItem) {
     780
     781                        if (currentItem.groupTreeitem) {
     782                                this.setFocusToItem(currentItem.groupTreeitem);
     783                        }
     784                };
     785
     786                TreeLinks.prototype.setFocusToFirstItem = function () {
     787                        this.setFocusToItem(this.firstTreeitem);
     788                };
     789
     790                TreeLinks.prototype.setFocusToLastItem = function () {
     791                        this.setFocusToItem(this.lastTreeitem);
     792                };
     793
     794                TreeLinks.prototype.expandTreeitem = function (currentItem) {
     795
     796                        if (currentItem.isExpandable) {
     797                                currentItem.domNode.setAttribute('aria-expanded', true);
     798                                this.updateVisibleTreeitems();
     799                        }
     800
     801                };
     802
     803                TreeLinks.prototype.expandAllSiblingItems = function (currentItem) {
     804                        for (var i = 0; i < this.treeitems.length; i++) {
     805                                var ti = this.treeitems[i];
     806
     807                                if ((ti.groupTreeitem === currentItem.groupTreeitem) && ti.isExpandable) {
     808                                        this.expandTreeitem(ti);
     809                                }
     810                        }
     811
     812                };
     813
     814                TreeLinks.prototype.collapseTreeitem = function (currentItem) {
     815
     816                        var groupTreeitem = false;
     817
     818                        if (currentItem.isExpanded()) {
     819                                groupTreeitem = currentItem;
     820                        }
     821                        else {
     822                                groupTreeitem = currentItem.groupTreeitem;
     823                        }
     824
     825                        if (groupTreeitem) {
     826                                groupTreeitem.domNode.setAttribute('aria-expanded', false);
     827                                this.updateVisibleTreeitems();
     828                                this.setFocusToItem(groupTreeitem);
     829                        }
     830
     831                };
     832
     833                TreeLinks.prototype.updateVisibleTreeitems = function () {
     834
     835                        this.firstTreeitem = this.treeitems[0];
     836
     837                        for (var i = 0; i < this.treeitems.length; i++) {
     838                                var ti = this.treeitems[i];
     839
     840                                var parent = ti.domNode.parentNode;
     841
     842                                ti.isVisible = true;
     843
     844                                while (parent && (parent !== this.domNode)) {
     845
     846                                        if (parent.getAttribute('aria-expanded') == 'false') {
     847                                                ti.isVisible = false;
     848                                        }
     849                                        parent = parent.parentNode;
     850                                }
     851
     852                                if (ti.isVisible) {
     853                                        this.lastTreeitem = ti;
     854                                }
     855                        }
     856
     857                };
     858
     859                TreeLinks.prototype.setFocusByFirstCharacter = function (currentItem, char) {
     860                        var start, index, char = char.toLowerCase();
     861
     862                        // Get start index for search based on position of currentItem
     863                        start = this.treeitems.indexOf(currentItem) + 1;
     864                        if (start === this.treeitems.length) {
     865                                start = 0;
     866                        }
     867
     868                        // Check remaining slots in the menu
     869                        index = this.getIndexFirstChars(start, char);
     870
     871                        // If not found in remaining slots, check from beginning
     872                        if (index === -1) {
     873                                index = this.getIndexFirstChars(0, char);
     874                        }
     875
     876                        // If match was found...
     877                        if (index > -1) {
     878                                this.setFocusToItem(this.treeitems[index]);
     879                        }
     880                };
     881
     882                TreeLinks.prototype.getIndexFirstChars = function (startIndex, char) {
     883                        for (var i = startIndex; i < this.firstChars.length; i++) {
     884                                if (this.treeitems[i].isVisible) {
     885                                        if (char === this.firstChars[i]) {
     886                                                return i;
     887                                        }
     888                                }
     889                        }
     890                        return -1;
     891                };
     892
     893                return TreeLinks;
     894        })();
     895
     896        /**
     897         * Initialization of the file browser's folder states.
     898         *
     899         * @since 4.9.0
     900         * @returns {void}
     901         */
     902        component.initFileBrowser = function initFileBrowser() {
     903                // Collapse all folders.
     904                $('#templateside [role="group"]').parent().attr('aria-expanded', false);
     905
     906                // Expand ancestors to the current file.
     907                $('#templateside .notice').parents('[aria-expanded]').attr('aria-expanded', true);
     908
     909                // Find Tree elements and enhance them.
     910                var trees = document.querySelectorAll('[role="tree"]');
     911
     912                for (var i = 0; i < trees.length; i++) {
     913                        var t = new TreeLinks(trees[i]);
     914                        t.init();
     915                }
     916        }
     917
    353918        return component;
    354919})( jQuery );
  • src/wp-admin/plugin-editor.php

     
    231231</div>
    232232
    233233<div id="templateside">
    234         <h2><?php _e( 'Plugin Files' ); ?></h2>
     234        <h2 id="plugin-files-label"><?php _e( 'Plugin Files' ); ?></h2>
    235235
    236236        <?php
    237237        $plugin_editable_files = array();
     
    241241                }
    242242        }
    243243        ?>
    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; ?>
     244        <ul role="tree" aria-labelledby="plugin-files-label">
     245        <li role="treeitem" tabindex="-1" aria-expanded="true"
     246                aria-level="1"
     247                aria-posinset="1"
     248                aria-setsize="1">
     249                <ul role="group" style="padding-left: 0;">
     250                        <?php wp_print_plugin_file_tree( wp_make_plugin_file_tree( $plugin_editable_files ) ); ?>
     251                </ul>
    250252        </ul>
    251253</div>
    252254<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'];
     
    205213if ( $theme->errors() )
    206214        echo '<div class="error"><p><strong>' . __( 'This theme is broken.' ) . '</strong> ' . $theme->errors()->get_error_message() . '</p></div>';
    207215?>
    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";
     216<div id="templateside">
     217        <h2 id="theme-files-label"><?php _e( 'Theme Files' ); ?></h2>
     218        <?php
     219        if ( $has_templates || $theme->parent() ) :
     220                if ( $theme->parent() ) {
     221                        /* translators: %s: link to edit parent theme */
     222                        echo '<p class="howto">' . sprintf( __( 'This child theme inherits templates from a parent theme, %s.' ),
     223                                sprintf( '<a href="%s">%s</a>',
     224                                        self_admin_url( 'theme-editor.php?theme=' . urlencode( $theme->get_template() ) ),
     225                                        $theme->parent()->display( 'Name' )
     226                                )
     227                        ) . "</p>\n";
    245228                }
     229        endif;
     230        ?>
     231        <ul role="tree" aria-labelledby="theme-files-label">
     232                <li role="treeitem" tabindex="-1" aria-expanded="true"
     233                        aria-level="1"
     234                        aria-posinset="1"
     235                        aria-setsize="1">
     236                        <ul role="group" style="padding-left: 0;">
     237                                <?php wp_print_theme_file_tree( wp_make_theme_file_tree( $allowed_files ) ); ?>
     238                        </ul>
     239                </li>
     240        </ul>
     241</div>
    246242
    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>
    265243<?php if ( $error ) :
    266244        echo '<div class="error"><p>' . __('Oops, no such file exists! Double check the name and try again, merci.') . '</p></div>';
    267245else : ?>