github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/prow/cmd/deck/static/command-help-script.js (about)

     1  "use strict";
     2  
     3  function getParameterByName(name) {  // http://stackoverflow.com/a/5158301/3694
     4      const match = new RegExp('[?&]' + name + '=([^&/]*)').exec(window.location.search);
     5      return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
     6  }
     7  
     8  function redrawOptions() {
     9      const rs = allHelp.AllRepos.sort();
    10      const sel = document.getElementById("repo");
    11      while (sel.length > 1) {
    12          sel.removeChild(sel.lastChild);
    13      }
    14      const param = getParameterByName("repo");
    15      rs.forEach((opt) => {
    16          const o = document.createElement("option");
    17          o.text = opt;
    18          o.selected = (param && opt === param);
    19          sel.appendChild(o);
    20      });
    21  }
    22  
    23  window.onload = function () {
    24      // set dropdown based on options from query string
    25      const hash = window.location.hash;
    26      redrawOptions();
    27      redraw();
    28  
    29      // Register dialog
    30      const dialog = document.querySelector('dialog');
    31      dialogPolyfill.registerDialog(dialog);
    32      dialog.querySelector('.close').addEventListener('click', () => {
    33          dialog.close();
    34      });
    35  
    36      if (hash !== "") {
    37          const el = document.body.querySelector(hash);
    38          const mainContainer = document.body.querySelector(".mdl-layout__content");
    39          if (el && mainContainer) {
    40              setTimeout(() => {
    41                  mainContainer.scrollTop = el.getBoundingClientRect().top;
    42                  window.location.hash = hash;
    43              }, 32);
    44              el.querySelector(".mdl-button--primary").click();
    45          }
    46      }
    47  };
    48  
    49  document.addEventListener("DOMContentLoaded", () => {
    50      configure();
    51  });
    52  
    53  function configure() {
    54      if (!branding) {
    55          return;
    56      }
    57      if (branding.logo) {
    58          document.getElementById('img').src = branding.logo;
    59      }
    60      if (branding.favicon) {
    61          document.getElementById('favicon').href = branding.favicon;
    62      }
    63      if (branding.background_color) {
    64          document.body.style.background = branding.background_color;
    65      }
    66      if (branding.header_color) {
    67          document.getElementsByTagName('header')[0].style.backgroundColor = branding.header_color;
    68      }
    69  }
    70  
    71  function selectionText(sel) {
    72      return sel.selectedIndex === 0 ? "" : sel.options[sel.selectedIndex].text;
    73  }
    74  
    75  /**
    76   * Takes an org/repo string and a repo to plugin map and returns the plugins
    77   * that apply to the repo.
    78   * @param {string} repoSel repo name
    79   * @param {Map<string, PluginHelp>} repoPlugins maps plugin name to plugin
    80   * @return {Array<string>}
    81   */
    82  function applicablePlugins(repoSel, repoPlugins) {
    83      if (repoSel === "") {
    84          const all = repoPlugins[""];
    85          if (all) {
    86              return all.sort();
    87          }
    88          return [];
    89      }
    90      const parts = repoSel.split("/");
    91      const byOrg = repoPlugins[parts[0]];
    92      let plugins = [];
    93      if (byOrg && byOrg !== []) {
    94          plugins = plugins.concat(byOrg);
    95      }
    96      const pluginNames = repoPlugins[repoSel];
    97      if (pluginNames) {
    98          pluginNames.forEach((pluginName) => {
    99              if (!plugins.includes(pluginName)) {
   100                  plugins.push(pluginName);
   101              }
   102          });
   103      }
   104      return plugins.sort();
   105  }
   106  
   107  /**
   108   * Returns a normal cell for the command row.
   109   * @param {Array<string>|string} data content of the cell
   110   * @param {Array<string>} styles a list of styles applied to the cell.
   111   * @param {boolean} noWrap true if the content of the cell should be wrap.
   112   * @return {Element}
   113   */
   114  function createCommandCell(data, styles = [], noWrap = false) {
   115      const cell = document.createElement("TD");
   116      cell.classList.add("mdl-data-table__cell--non-numeric");
   117      if (!noWrap) {
   118          cell.classList.add("table-cell");
   119      }
   120      let content;
   121      if (Array.isArray((data))) {
   122          content = document.createElement("UL");
   123          content.classList = "command-example-list";
   124  
   125          data.forEach((item) => {
   126              const itemContainer = document.createElement("LI");
   127              const span = document.createElement("SPAN");
   128              span.innerHTML = item;
   129              span.classList.add(...styles);
   130              itemContainer.appendChild(span);
   131              content.appendChild(itemContainer);
   132          });
   133      } else {
   134          content = document.createElement("DIV");
   135          content.classList.add(...styles);
   136          content.innerHTML = data;
   137      }
   138  
   139      cell.appendChild(content);
   140  
   141      return cell;
   142  }
   143  
   144  /**
   145   * Returns an icon element.
   146   * @param {number} no no. command
   147   * @param {string} iconString icon name
   148   * @param {?Array<string>} styles list of styles of the icon
   149   * @param {string} tooltip tooltip string
   150   * @param {boolean} isButton true if icon is a button
   151   * @return {Element}
   152   */
   153  function createIcon(no, iconString, styles = [], tooltip = "", isButton = false) {
   154      const icon = document.createElement("I");
   155      icon.id = "icon-" + iconString + "-" + no;
   156      icon.classList.add("material-icons");
   157      icon.classList.add(...styles);
   158      icon.innerHTML = iconString;
   159  
   160      const container = isButton ? document.createElement("BUTTON") : document.createElement("DIV");
   161      container.appendChild(icon);
   162      if (isButton) {
   163          container.classList.add(...["mdl-button", "mdl-js-button", "mdl-button--icon"]);
   164      }
   165  
   166      if (tooltip === "") {
   167          return container;
   168      }
   169  
   170      const tooltipEl = document.createElement("DIV");
   171      tooltipEl.setAttribute("for", icon.id);
   172      tooltipEl.classList.add("mdl-tooltip");
   173      tooltipEl.innerHTML = tooltip;
   174      container.appendChild(tooltipEl);
   175  
   176      return container;
   177  }
   178  
   179  /**
   180   * Returns the feature cell for the command row.
   181   * @param {boolean} isFeatured true if the command is featured.
   182   * @param {boolean} isExternal true if the command is external.
   183   * @param {number} no no. command.
   184   * @return {Element}
   185   */
   186  function commandStatus(isFeatured, isExternal, no) {
   187      const status = document.createElement("TD");
   188      status.classList.add("mdl-data-table__cell--non-numeric");
   189      if (isFeatured) {
   190          status.appendChild(
   191              createIcon(no, "stars", ["featured-icon"], "Featured command"));
   192      }
   193      if (isExternal) {
   194          status.appendChild(
   195              createIcon(no, "open_in_new", ["external-icon"], "External plugin"));
   196      }
   197      return status;
   198  }
   199  
   200  /**
   201   * Returns a section to the content of the dialog
   202   * @param title title of the section
   203   * @param body body of the section
   204   * @return {Element}
   205   */
   206  function addDialogSection(title, body) {
   207      const container = document.createElement("DIV");
   208      const sectionTitle = document.createElement("h5");
   209      const sectionBody = document.createElement("p");
   210  
   211      sectionBody.classList.add("dialog-section-body");
   212      sectionBody.innerHTML = body;
   213  
   214      sectionTitle.classList.add("dialog-section-title");
   215      sectionTitle.innerHTML = title;
   216  
   217      container.classList.add("dialog-section");
   218      container.appendChild(sectionTitle);
   219      container.appendChild(sectionBody);
   220  
   221      return container;
   222  }
   223  
   224  /**
   225   * Returns a cell for the Plugin column.
   226   * @param {string}repo repo name
   227   * @param {string} pluginName plugin name.
   228   * @param {PluginHelp} plugin the plugin to which the command belong to
   229   * @return {Element}
   230   */
   231  function createPluginCell(repo, pluginName, plugin) {
   232      const pluginCell = document.createElement("TD");
   233      const button = document.createElement("button");
   234      pluginCell.classList.add("mdl-data-table__cell--non-numeric");
   235      button.classList.add("mdl-button", "mdl-button--js", "mdl-button--primary");
   236      button.innerHTML = pluginName;
   237  
   238      // Attach Event Handlers.
   239      const dialog = document.querySelector('dialog');
   240      button.addEventListener('click', (event) => {
   241          const title = dialog.querySelector(".mdl-dialog__title");
   242          const content = dialog.querySelector(".mdl-dialog__content");
   243  
   244          while (content.firstChild) {
   245              content.removeChild(content.firstChild);
   246          }
   247  
   248          title.innerHTML = pluginName;
   249          if (plugin.Description) {
   250              content.appendChild(addDialogSection("Description", plugin.Description));
   251          }
   252          if (plugin.Events) {
   253              const sectionContent = "[" + plugin.Events.sort().join(", ") + "]";
   254              content.appendChild(addDialogSection("Events handled", sectionContent));
   255          }
   256          if (plugin.Config) {
   257              let sectionContent = plugin.Config ? plugin.Config[repo] : "";
   258              let sectionTitle =
   259                  repo === "" ? "Configuration(global)" : "Configuration(" + repo + ")";
   260              if (sectionContent && sectionContent !== "") {
   261                  content.appendChild(addDialogSection(sectionTitle, sectionContent));
   262              }
   263          }
   264          dialog.showModal();
   265      });
   266  
   267      pluginCell.appendChild(button);
   268      return pluginCell;
   269  }
   270  
   271  /**
   272   * Creates a link that links to the command.
   273   * @param name
   274   * @param no
   275   * @return {Element}
   276   */
   277  function createCommandLink(name, no) {
   278      const link = document.createElement("TD");
   279      const iconButton = createIcon(no, "link", ["link-icon"], "", true);
   280  
   281      iconButton.addEventListener("click", () => {
   282          const tempInput = document.createElement("INPUT");
   283          let url = window.location.href;
   284          const hashIndex = url.indexOf("#");
   285          if (hashIndex !== -1) {
   286              url = url.slice(0, hashIndex);
   287          }
   288  
   289          url += "#" + name;
   290          tempInput.style.zIndex = "-99999";
   291          tempInput.style.background = "transparent";
   292          tempInput.value = url;
   293  
   294          document.body.appendChild(tempInput);
   295          tempInput.select();
   296          document.execCommand("copy");
   297          document.body.removeChild(tempInput);
   298  
   299          const toast = document.body.querySelector("#toast");
   300          toast.MaterialSnackbar.showSnackbar({message: "Copied to clipboard"});
   301      });
   302  
   303      link.appendChild(iconButton);
   304      link.classList.add("mdl-data-table__cell--non-numeric");
   305  
   306      return link;
   307  }
   308  
   309  
   310  /**
   311   * Creates a row for the Command table.
   312   * @param {string} repo repo name.
   313   * @param {string} pluginName plugin name.
   314   * @param {PluginHelp} plugin the plugin to which the command belongs.
   315   * @param {Command} command the command.
   316   * @param {boolean} isExternal true if the command belongs to an external
   317   * @param {number} no no. command
   318   * @return {Element}
   319   */
   320  function createCommandRow(repo, pluginName, plugin, command, isExternal, no) {
   321      const row = document.createElement("TR");
   322      const name = extractCommandName(command.Examples[0]);
   323      row.id = name;
   324  
   325      row.appendChild(commandStatus(command.Featured, isExternal, no));
   326      row.appendChild(createCommandCell(command.Usage, ["command-usage"]));
   327      row.appendChild(
   328          createCommandCell(command.Examples, ["command-examples"], true));
   329      row.appendChild(
   330          createCommandCell(command.Description, ["command-desc-text"]));
   331      row.appendChild(createCommandCell(command.WhoCanUse, ["command-desc-text"]));
   332      row.appendChild(createPluginCell(repo, pluginName, plugin));
   333      row.appendChild(createCommandLink(name, no));
   334  
   335      return row;
   336  }
   337  
   338  /**
   339   * Redraw a plugin table.
   340   * @param {string} repo repo name.
   341   * @param {Map<string, Object>} helpMap maps a plugin name to a plugin.
   342   */
   343  function redrawHelpTable(repo, helpMap) {
   344      const table = document.getElementById("command-table");
   345      const tableBody = document.querySelector("TBODY");
   346      if (helpMap.size === 0) {
   347          table.style.display = "none";
   348          return;
   349      }
   350      table.style.display = "table";
   351      while (tableBody.childElementCount !== 0) {
   352          tableBody.removeChild(tableBody.firstChild);
   353      }
   354      const names = helpMap.keys();
   355      const commandsWithPluginName = [];
   356      for (let name of names) {
   357          helpMap.get(name).plugin.Commands.forEach((command) => {
   358              commandsWithPluginName.push({
   359                  pluginName: name,
   360                  command: command
   361              });
   362          });
   363      }
   364      commandsWithPluginName
   365          .sort((command1, command2) => {
   366              return command1.command.Featured ? -1 : command2.command.Featured ? 1 : 0;
   367          })
   368          .forEach((command, index) => {
   369              const pluginName = command.pluginName;
   370              const {isExternal, plugin} = helpMap.get(pluginName);
   371              const commandRow = createCommandRow(
   372                  repo,
   373                  pluginName,
   374                  plugin,
   375                  command.command,
   376                  isExternal,
   377                  index);
   378              tableBody.appendChild(commandRow);
   379          });
   380  }
   381  
   382  /**
   383   * Redraws the content of the page.
   384   */
   385  function redraw() {
   386      const repoSel = selectionText(document.getElementById("repo"));
   387      if (window.history && window.history.replaceState !== undefined) {
   388          if (repoSel !== "") {
   389              history.replaceState(null, "", "/command-help?repo="
   390                  + encodeURIComponent(repoSel));
   391          } else {
   392              history.replaceState(null, "", "/command-help")
   393          }
   394      }
   395      redrawOptions();
   396  
   397      const pluginsWithCommands = new Map();
   398      applicablePlugins(repoSel, allHelp.RepoPlugins)
   399          .forEach((name) => {
   400              if (allHelp.PluginHelp[name] && allHelp.PluginHelp[name].Commands) {
   401                  pluginsWithCommands.set(
   402                      name,
   403                      {
   404                          isExternal: false,
   405                          plugin: allHelp.PluginHelp[name]
   406                      });
   407              }
   408          });
   409      applicablePlugins(repoSel, allHelp.RepoExternalPlugins)
   410          .forEach((name) => {
   411              if (allHelp.ExternalPluginHelp[name]
   412                  && allHelp.ExternalPluginHelp[name].Commands) {
   413                  pluginsWithCommands.set(
   414                      name,
   415                      {
   416                          isExternal: true,
   417                          plugin: allHelp.ExternalPluginHelp[name]
   418                      });
   419              }
   420          });
   421      redrawHelpTable(repoSel, pluginsWithCommands);
   422  }
   423  
   424  
   425  /**
   426   * Extracts a command name from a command example. It takes the first example,
   427   * with out the slash, as the name for the command. Also, any '-' character is
   428   * replaced by '_' to make the name valid in the address.
   429   * @param {string} commandExample
   430   * @return {string}
   431   */
   432  function extractCommandName(commandExample) {
   433      const command = commandExample.split(" ");
   434      if (!command || command.length === 0) {
   435          throw new Error("Cannot extract command name.");
   436      }
   437      return command[0].slice(1).split("-").join("_");
   438  }