1d8a7afe2SAdam Hornacek /* 2d8a7afe2SAdam Hornacek * CDDL HEADER START 3d8a7afe2SAdam Hornacek * 4d8a7afe2SAdam Hornacek * The contents of this file are subject to the terms of the 5d8a7afe2SAdam Hornacek * Common Development and Distribution License (the "License"). 6d8a7afe2SAdam Hornacek * You may not use this file except in compliance with the License. 7d8a7afe2SAdam Hornacek * 8d8a7afe2SAdam Hornacek * See LICENSE.txt included in this distribution for the specific 9d8a7afe2SAdam Hornacek * language governing permissions and limitations under the License. 10d8a7afe2SAdam Hornacek * 11d8a7afe2SAdam Hornacek * When distributing Covered Code, include this CDDL HEADER in each 12d8a7afe2SAdam Hornacek * file and include the License file at LICENSE.txt. 13d8a7afe2SAdam Hornacek * If applicable, add the following below this CDDL HEADER, with the 14d8a7afe2SAdam Hornacek * fields enclosed by brackets "[]" replaced with your own identifying 15d8a7afe2SAdam Hornacek * information: Portions Copyright [yyyy] [name of copyright owner] 16d8a7afe2SAdam Hornacek * 17d8a7afe2SAdam Hornacek * CDDL HEADER END 18d8a7afe2SAdam Hornacek */ 19d8a7afe2SAdam Hornacek 20d8a7afe2SAdam Hornacek /* 217c0a1357SAdam Hornacek * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. 22d8a7afe2SAdam Hornacek * Portions Copyright (c) 2017, 2020, Chris Fraire <cfraire@me.com>. 23*587e9867SKrystof Tulinger * Portions Copyright (c) 2022, Krystof Tulinger <k.tulinger@seznam.cz>. 24d8a7afe2SAdam Hornacek */ 25d8a7afe2SAdam Hornacek package org.opengrok.web; 26d8a7afe2SAdam Hornacek 27d8a7afe2SAdam Hornacek import java.util.Comparator; 28d8a7afe2SAdam Hornacek import java.util.Iterator; 29d8a7afe2SAdam Hornacek import java.util.List; 30d8a7afe2SAdam Hornacek import java.util.Map; 31d8a7afe2SAdam Hornacek import java.util.NavigableSet; 32d8a7afe2SAdam Hornacek import java.util.TreeMap; 33d8a7afe2SAdam Hornacek import java.util.TreeSet; 34d8a7afe2SAdam Hornacek 35d8a7afe2SAdam Hornacek import org.webjars.WebJarAssetLocator; 36d8a7afe2SAdam Hornacek 37d8a7afe2SAdam Hornacek /** 38cc6683c3SVladimir Kotal * A list-like container for JavasScript sources in JSP/HTML pages. 39d8a7afe2SAdam Hornacek * 40d8a7afe2SAdam Hornacek * @author Krystof Tulinger 41d8a7afe2SAdam Hornacek */ 42d8a7afe2SAdam Hornacek public class Scripts implements Iterable<Scripts.Script> { 43d8a7afe2SAdam Hornacek 44d8a7afe2SAdam Hornacek private static final String DEBUG_SUFFIX = "-debug"; 45d8a7afe2SAdam Hornacek private static final String WEBJAR_PATH_PREFIX = "META-INF/resources/"; 46d8a7afe2SAdam Hornacek 47d8a7afe2SAdam Hornacek enum Type { 48d8a7afe2SAdam Hornacek MINIFIED, DEBUG 49d8a7afe2SAdam Hornacek } 50d8a7afe2SAdam Hornacek 51d8a7afe2SAdam Hornacek /** 52d8a7afe2SAdam Hornacek * A script wrapper. 53d8a7afe2SAdam Hornacek */ 54d8a7afe2SAdam Hornacek public abstract static class Script { 55d8a7afe2SAdam Hornacek 56d8a7afe2SAdam Hornacek /** 57d8a7afe2SAdam Hornacek * Represents the script information. Either 58d8a7afe2SAdam Hornacek * <ul> 59d8a7afe2SAdam Hornacek * <li>script HTML src attribute for remote scripts</li> 60d8a7afe2SAdam Hornacek * <li>inline javascript code for inline scripts</li> 61d8a7afe2SAdam Hornacek * </ul> 62d8a7afe2SAdam Hornacek */ 63d8a7afe2SAdam Hornacek protected String scriptData; 64d8a7afe2SAdam Hornacek protected int priority; 65d8a7afe2SAdam Hornacek Script(String scriptData, int priority)66cc6683c3SVladimir Kotal protected Script(String scriptData, int priority) { 67d8a7afe2SAdam Hornacek this.scriptData = scriptData; 68d8a7afe2SAdam Hornacek this.priority = priority; 69d8a7afe2SAdam Hornacek } 70d8a7afe2SAdam Hornacek toHtml()71d8a7afe2SAdam Hornacek public abstract String toHtml(); 72d8a7afe2SAdam Hornacek getScriptData()73d8a7afe2SAdam Hornacek public String getScriptData() { 74d8a7afe2SAdam Hornacek return scriptData; 75d8a7afe2SAdam Hornacek } 76d8a7afe2SAdam Hornacek getPriority()77d8a7afe2SAdam Hornacek public int getPriority() { 78d8a7afe2SAdam Hornacek return priority; 79d8a7afe2SAdam Hornacek } 80d8a7afe2SAdam Hornacek } 81d8a7afe2SAdam Hornacek 82d8a7afe2SAdam Hornacek /** 83d8a7afe2SAdam Hornacek * Script implementing the toHtml() method as an external script resource. 84d8a7afe2SAdam Hornacek */ 85d8a7afe2SAdam Hornacek public static class FileScript extends Script { 86d8a7afe2SAdam Hornacek FileScript(String script, int priority)87d8a7afe2SAdam Hornacek public FileScript(String script, int priority) { 88d8a7afe2SAdam Hornacek super(script, priority); 89d8a7afe2SAdam Hornacek } 90d8a7afe2SAdam Hornacek 91d8a7afe2SAdam Hornacek @Override toHtml()92d8a7afe2SAdam Hornacek public String toHtml() { 93d8a7afe2SAdam Hornacek return "\t<script type=\"text/javascript\" src=\"" + 94d8a7afe2SAdam Hornacek this.getScriptData() + 95d8a7afe2SAdam Hornacek "\" data-priority=\"" + 96d8a7afe2SAdam Hornacek this.getPriority() + 97d8a7afe2SAdam Hornacek "\"></script>\n"; 98d8a7afe2SAdam Hornacek } 99d8a7afe2SAdam Hornacek 100d8a7afe2SAdam Hornacek } 101d8a7afe2SAdam Hornacek 102d8a7afe2SAdam Hornacek protected static final Map<String, Script> SCRIPTS = new TreeMap<>(); 103d8a7afe2SAdam Hornacek 104d8a7afe2SAdam Hornacek private static final WebJarAssetLocator assetLocator = new WebJarAssetLocator(); 105d8a7afe2SAdam Hornacek 106d8a7afe2SAdam Hornacek /** 107d8a7afe2SAdam Hornacek * Aliases for the page scripts. The path in the FileScript is relatively to 108d8a7afe2SAdam Hornacek * the request's context path. 109d8a7afe2SAdam Hornacek * 110d8a7afe2SAdam Hornacek * @see HttpServletRequest#getContextPath() 111d8a7afe2SAdam Hornacek */ 112d8a7afe2SAdam Hornacek static { 113d8a7afe2SAdam Hornacek putFromWebJar("jquery", "jquery.min.js", 10); 114d8a7afe2SAdam Hornacek putjs("jquery-ui", "js/jquery-ui-1.12.1-custom", 11); 11580df504bSAdam Hornacek putFromWebJar("jquery-tablesorter", "jquery.tablesorter.min.js", 12); 1167c0a1357SAdam Hornacek putjs("tablesorter-parsers", "js/tablesorter-parsers-0.0.3", 13, true); 11787e82791SAdam Hornacek putjs("searchable-option-list", "js/searchable-option-list-2.0.15", 14, true); 118d7d00a41SKrystof Tulinger putjs("utils", "js/utils-0.0.45", 15, true); 1197c0a1357SAdam Hornacek putjs("repos", "js/repos-0.0.3", 20, true); 1207c0a1357SAdam Hornacek putjs("diff", "js/diff-0.0.5", 20, true); 121d8a7afe2SAdam Hornacek putjs("jquery-caret", "js/jquery.caret-1.5.2", 25); 122d8a7afe2SAdam Hornacek } 123d8a7afe2SAdam Hornacek putjs(String key, String pathPrefix, int priority)124d8a7afe2SAdam Hornacek private static void putjs(String key, String pathPrefix, int priority) { 125d8a7afe2SAdam Hornacek putjs(key, pathPrefix, priority, false); 126d8a7afe2SAdam Hornacek } 127d8a7afe2SAdam Hornacek putjs(String key, String pathPrefix, int priority, boolean debug)128d8a7afe2SAdam Hornacek private static void putjs(String key, String pathPrefix, int priority, boolean debug) { 129d8a7afe2SAdam Hornacek SCRIPTS.put(key, new FileScript(pathPrefix + ".min.js", priority)); 130d8a7afe2SAdam Hornacek if (debug) { 131d8a7afe2SAdam Hornacek SCRIPTS.put(key + DEBUG_SUFFIX, new FileScript(pathPrefix + ".js", priority)); 132d8a7afe2SAdam Hornacek } 133d8a7afe2SAdam Hornacek } 134d8a7afe2SAdam Hornacek putFromWebJar(String key, String fileName, int priority)135d8a7afe2SAdam Hornacek private static void putFromWebJar(String key, String fileName, int priority) { 136d8a7afe2SAdam Hornacek String path = assetLocator.getFullPath(fileName); 137d8a7afe2SAdam Hornacek if (path.startsWith(WEBJAR_PATH_PREFIX)) { 138d8a7afe2SAdam Hornacek path = path.substring(WEBJAR_PATH_PREFIX.length()); 139d8a7afe2SAdam Hornacek } 140d8a7afe2SAdam Hornacek SCRIPTS.put(key, new FileScript(path, priority)); 141d8a7afe2SAdam Hornacek } 142d8a7afe2SAdam Hornacek 143d8a7afe2SAdam Hornacek private static final Comparator<Script> SCRIPTS_COMPARATOR = Comparator 144d8a7afe2SAdam Hornacek .comparingInt(Script::getPriority) 145d8a7afe2SAdam Hornacek .thenComparing(Script::getScriptData); 146d8a7afe2SAdam Hornacek 147d8a7afe2SAdam Hornacek /** 148d8a7afe2SAdam Hornacek * Scripts which will be written to the page. 149d8a7afe2SAdam Hornacek */ 150d8a7afe2SAdam Hornacek private final NavigableSet<Script> outputScripts = new TreeSet<>(SCRIPTS_COMPARATOR); 151d8a7afe2SAdam Hornacek 152d8a7afe2SAdam Hornacek /** 153d8a7afe2SAdam Hornacek * Convert the page scripts into HTML. 154d8a7afe2SAdam Hornacek * 155d8a7afe2SAdam Hornacek * @return the HTML 156d8a7afe2SAdam Hornacek */ toHtml()157d8a7afe2SAdam Hornacek public String toHtml() { 158d8a7afe2SAdam Hornacek StringBuilder builder = new StringBuilder(); 159d8a7afe2SAdam Hornacek for (Script entry : this) { 160d8a7afe2SAdam Hornacek builder.append(entry.toHtml()); 161d8a7afe2SAdam Hornacek } 162d8a7afe2SAdam Hornacek return builder.toString(); 163d8a7afe2SAdam Hornacek } 164d8a7afe2SAdam Hornacek 165d8a7afe2SAdam Hornacek /** 166d8a7afe2SAdam Hornacek * Return the HTML representation of the page scripts. 167d8a7afe2SAdam Hornacek * 168d8a7afe2SAdam Hornacek * @return the HTML 169d8a7afe2SAdam Hornacek * @see #toHtml() 170d8a7afe2SAdam Hornacek */ 171d8a7afe2SAdam Hornacek @Override toString()172d8a7afe2SAdam Hornacek public String toString() { 173d8a7afe2SAdam Hornacek return toHtml(); 174d8a7afe2SAdam Hornacek } 175d8a7afe2SAdam Hornacek 176d8a7afe2SAdam Hornacek /** 177d8a7afe2SAdam Hornacek * Return the size of the page scripts. 178d8a7afe2SAdam Hornacek * 179d8a7afe2SAdam Hornacek * @return the size 180d8a7afe2SAdam Hornacek * @see List#size() 181d8a7afe2SAdam Hornacek */ size()182d8a7afe2SAdam Hornacek public int size() { 183d8a7afe2SAdam Hornacek return outputScripts.size(); 184d8a7afe2SAdam Hornacek } 185d8a7afe2SAdam Hornacek 186d8a7afe2SAdam Hornacek /** 187d8a7afe2SAdam Hornacek * Check if there is any script for this page. 188d8a7afe2SAdam Hornacek * 189d8a7afe2SAdam Hornacek * @return true if there is not; false otherwise 190d8a7afe2SAdam Hornacek * @see List#isEmpty() 191d8a7afe2SAdam Hornacek */ isEmpty()192d8a7afe2SAdam Hornacek public boolean isEmpty() { 193d8a7afe2SAdam Hornacek return outputScripts.isEmpty(); 194d8a7afe2SAdam Hornacek } 195d8a7afe2SAdam Hornacek 196d8a7afe2SAdam Hornacek /** 197d8a7afe2SAdam Hornacek * Iterator over the page scripts. 198d8a7afe2SAdam Hornacek * 199d8a7afe2SAdam Hornacek * @return the iterator 200d8a7afe2SAdam Hornacek * @see List#iterator() 201d8a7afe2SAdam Hornacek */ 202d8a7afe2SAdam Hornacek @Override iterator()203d8a7afe2SAdam Hornacek public Iterator<Script> iterator() { 204d8a7afe2SAdam Hornacek return outputScripts.iterator(); 205d8a7afe2SAdam Hornacek } 206d8a7afe2SAdam Hornacek 207d8a7afe2SAdam Hornacek /** 208d8a7afe2SAdam Hornacek * Add a script which is identified by the name. 209d8a7afe2SAdam Hornacek * 210d8a7afe2SAdam Hornacek * @param contextPath given context path for the used URL 211d8a7afe2SAdam Hornacek * @param scriptName name of the script 212d8a7afe2SAdam Hornacek * @param type type of the script to add 213d8a7afe2SAdam Hornacek * @return true if script was added; false otherwise 214d8a7afe2SAdam Hornacek */ addScript(String contextPath, String scriptName, Type type)215d8a7afe2SAdam Hornacek public boolean addScript(String contextPath, String scriptName, Type type) { 216d8a7afe2SAdam Hornacek contextPath = contextPath == null || contextPath.isEmpty() ? "/" : contextPath + "/"; 217d8a7afe2SAdam Hornacek if (type == Type.DEBUG && SCRIPTS.containsKey(scriptName + DEBUG_SUFFIX)) { 218d8a7afe2SAdam Hornacek addScript(contextPath, scriptName + DEBUG_SUFFIX); 219d8a7afe2SAdam Hornacek return true; 220d8a7afe2SAdam Hornacek } else if (SCRIPTS.containsKey(scriptName)) { 221d8a7afe2SAdam Hornacek addScript(contextPath, scriptName); 222d8a7afe2SAdam Hornacek return true; 223d8a7afe2SAdam Hornacek } 224d8a7afe2SAdam Hornacek return false; 225d8a7afe2SAdam Hornacek } 226d8a7afe2SAdam Hornacek addScript(String contextPath, String scriptName)227d8a7afe2SAdam Hornacek private void addScript(String contextPath, String scriptName) { 228d8a7afe2SAdam Hornacek this.addScript(new FileScript(contextPath + SCRIPTS.get(scriptName).getScriptData(), 229d8a7afe2SAdam Hornacek SCRIPTS.get(scriptName).getPriority())); 230d8a7afe2SAdam Hornacek } 231d8a7afe2SAdam Hornacek 232d8a7afe2SAdam Hornacek /** 233d8a7afe2SAdam Hornacek * Add a script to the page, taking the script priority into account. 234d8a7afe2SAdam Hornacek * 235d8a7afe2SAdam Hornacek * @param script the script 236d8a7afe2SAdam Hornacek */ addScript(Script script)237d8a7afe2SAdam Hornacek public void addScript(Script script) { 238d8a7afe2SAdam Hornacek this.outputScripts.add(script); 239d8a7afe2SAdam Hornacek } 240d8a7afe2SAdam Hornacek } 241