xref: /OpenGrok/opengrok-web/src/main/java/org/opengrok/web/Scripts.java (revision 587e9867142902595bd3e8ebcd1e834ee5f73750)
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