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 }