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