github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/webapp/components/path.js (about)

     1  /**
     2   * Copyright 2018 The WPT Dashboard Project. All rights reserved.
     3   * Use of this source code is governed by a BSD-style license that can be
     4   * found in the LICENSE file.
     5   */
     6  
     7  /*
     8  `<path-part>` is a stateless component for displaying part of a test path.
     9  */
    10  import '../node_modules/@polymer/paper-styles/color.js';
    11  import { html, PolymerElement } from '../node_modules/@polymer/polymer/polymer-element.js';
    12  
    13  const PathInfo = (superClass) => class extends superClass {
    14    static get properties() {
    15      return {
    16        path: {
    17          type: String,
    18        },
    19        encodedPath: {
    20          type: String,
    21          computed: 'encodeTestPath(path)'
    22        },
    23        scheme: {
    24          type: String,
    25          computed: 'computeTestScheme(path)'
    26        },
    27        pathIsATestFile: {
    28          type: Boolean,
    29          computed: 'computePathIsATestFile(path)'
    30        },
    31        pathIsASubfolder: {
    32          type: Boolean,
    33          computed: 'computePathIsASubfolder(path)'
    34        },
    35        pathIsRootDir: {
    36          type: Boolean,
    37          computed: 'computePathIsRootDir(path)'
    38        }
    39      };
    40    }
    41  
    42    encodeTestPath(path) {
    43      path = path || '/';
    44      console.assert(path.startsWith('/'));
    45      const url = new URL(path || '/', window.location);
    46      let parts = url.pathname.split('/');
    47      parts.pop();
    48      let lastPart = path.substr(parts.join('/').length + 1);
    49      parts.push(encodeURIComponent(lastPart));
    50      return parts.join('/');
    51    }
    52  
    53    computeTestScheme(path) {
    54      // This should (close enough) match up with the logic in:
    55      // https://github.com/web-platform-tests/wpt/blob/master/tools/manifest/item.py
    56      // https://github.com/web-platform-tests/wpt/blob/master/tools/wptrunner/wptrunner/wpttest.py
    57      path = path || '';
    58      return ['.https.', '.serviceworker.'].some(x => path.includes(x)) ? 'https' : 'http';
    59    }
    60  
    61    computePathIsASubfolder(path) {
    62      if (!path || this.computePathIsATestFile(path)) {
    63        return false;
    64      }
    65      // Strip out query params/anchors.
    66      path = new URL(path, window.location).pathname;
    67      return path.split('/').filter(p => p).length > 0;
    68    }
    69  
    70    computePathIsATestFile(path) {
    71      // Strip out query params/anchors.
    72      path = new URL(path || '', window.location).pathname;
    73      return /(\.(html|htm|py|svg|xhtml|xht|xml)(\?.*)?$)/.test(path);
    74    }
    75  
    76    computePathIsRootDir(path) {
    77      return path && path === '/';
    78    }
    79  
    80    isParentDir(path, dir) {
    81      if (dir.startsWith(path)) {
    82        const relativePath = dir.substring(path.length);
    83        return relativePath.split('/').filter(p => p).length === 1;
    84      }
    85      return false;
    86    }
    87  
    88    getDirname(path) {
    89      path = path || '/';
    90      console.assert(path.startsWith('/'));
    91      return path.substring(0, path.lastIndexOf('/'));
    92    }
    93  
    94    splitPathIntoLinkedParts(inputPath) {
    95      // Remove the leading slash.
    96      const encoded = this.encodeTestPath(inputPath).slice(1);
    97      if (encoded === '') {
    98        // Return an empty array instead of an array with an empty string.
    99        return [];
   100      }
   101      const parts = encoded.split('/');
   102      let path = '';
   103      const linkedParts = parts.map(part => {
   104        path += `/${part}`;
   105        return {
   106          name: part,
   107          path: path,
   108        };
   109      });
   110      // Decode the last part's name (in case it was escaped).
   111      let last = linkedParts.pop();
   112      last.name = decodeURIComponent(last.name);
   113      linkedParts.push(last);
   114      return linkedParts;
   115    }
   116  };
   117  
   118  class PathPart extends PathInfo(PolymerElement) {
   119    static get template() {
   120      return html`
   121      <style>
   122        a {
   123          text-decoration: none;
   124          color: var(--paper-blue-700);
   125          font-family: monospace;
   126        }
   127        a:hover {
   128          cursor: pointer;
   129          color: var(--paper-blue-900);
   130        }
   131        .dir-path {
   132          font-weight: bold;
   133        }
   134      </style>
   135  
   136      <a class\$="{{ styleClass }}" href="{{ href }}" onclick="{{ navigate }}">
   137        {{ relativePath }}
   138      </a>
   139  `;
   140    }
   141  
   142    static get is() {
   143      return 'path-part';
   144    }
   145  
   146    static get properties() {
   147      return {
   148        query: {
   149          type: String
   150        },
   151        // Domain path-prefix, e.g. '/result/'
   152        prefix: {
   153          type: String,
   154          default: '/'
   155        },
   156        isDir: {
   157          type: Boolean
   158        },
   159        navigate: {
   160          type: Function
   161        },
   162        relativePath: {
   163          type: String,
   164          computed: 'computeDisplayableRelativePath(path)'
   165        },
   166        href: {
   167          type: Location,
   168          computed: 'computeHref(prefix, path, query, isTriageMode)'
   169        },
   170        isTriageMode: {
   171          type: Boolean,
   172          value: false
   173        },
   174        styleClass: {
   175          type: String,
   176          computed: 'computePathClass(isDir)'
   177        }
   178      };
   179    }
   180  
   181    computeHref(prefix, path, query, isTriageMode) {
   182      // Disable the link if triage mode is enabled
   183      // and this cell is viable for triage (a test file).
   184      if (isTriageMode && this.pathIsATestFile) {
   185        return 'javascript:void(0)';
   186      }
   187      const encodedPath = this.encodeTestPath(path);
   188      const href = new URL(window.location);
   189      href.pathname = `${prefix || ''}${encodedPath}`;
   190      if (query) {
   191        href.search = query;
   192      }
   193      return href;
   194    }
   195  
   196    computeDisplayableRelativePath(path) {
   197      if (!this.isDir) {
   198        path = this.encodeTestPath(path || '');
   199        return decodeURIComponent(path.substr(path.lastIndexOf('/') + 1));
   200      }
   201      const windowPath = window.location.pathname.replace(`${this.prefix || ''}`, '');
   202      const pathPrefix = new RegExp(`^${windowPath}${windowPath.endsWith('/') ? '' : '/'}`);
   203      return `${path.replace(pathPrefix, '')}${this.isDir ? '/' : ''}`;
   204    }
   205  
   206    computePathClass(isDir) {
   207      return isDir ? 'dir-path' : 'file-path';
   208    }
   209  }
   210  
   211  window.customElements.define(PathPart.is, PathPart);
   212  
   213  export { PathPart, PathInfo };