forked from j62/ctbrec
Add index and search to documentation app
This commit is contained in:
parent
482a7289b7
commit
f0bf6c5d7c
|
@ -0,0 +1,106 @@
|
|||
package ctbrec.docs;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class AbstractDocServlet extends HttpServlet {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(AbstractDocServlet.class);
|
||||
|
||||
String loadFile(String resource) throws IOException {
|
||||
InputStream resourceAsStream = getClass().getResourceAsStream(resource);
|
||||
if(resourceAsStream == null) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int length = 0;
|
||||
byte[] buffer = new byte[1024];
|
||||
while( (length = resourceAsStream.read(buffer)) >= 0 ) {
|
||||
out.write(buffer, 0, length);
|
||||
}
|
||||
return new String(out.toByteArray(), "utf-8");
|
||||
}
|
||||
|
||||
String getHeader() throws IOException {
|
||||
return loadFile("/html/docs/header.html");
|
||||
}
|
||||
|
||||
String getFooter() throws IOException {
|
||||
return loadFile("/html/docs/footer.html");
|
||||
}
|
||||
|
||||
List<String> getPages() throws IOException {
|
||||
List<String> pages = new ArrayList<>();
|
||||
URL resource = getClass().getResource("/html/docs");
|
||||
if(Objects.equals(resource.getProtocol(), "file")) {
|
||||
LOG.debug("FILE {}", resource.toString());
|
||||
indexDirectory(resource, pages);
|
||||
} else if(Objects.equals(resource.getProtocol(), "jar")) {
|
||||
LOG.debug("JAR {}", resource.toString());
|
||||
indexJar(resource, pages);
|
||||
}
|
||||
pages.add("index.md");
|
||||
Collections.sort(pages, (a, b) -> a.compareToIgnoreCase(b));
|
||||
return pages;
|
||||
}
|
||||
|
||||
private void indexJar(URL resource, List<String> pages) throws IOException {
|
||||
String fileUrl = resource.getFile();
|
||||
int colon = fileUrl.indexOf(':');
|
||||
int exclamation = fileUrl.indexOf('!');
|
||||
String jar = fileUrl.substring(colon + 1, exclamation);
|
||||
String internalFile = fileUrl.substring(exclamation + 2);
|
||||
try (JarFile jarFile = new JarFile(jar)) {
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry jarEntry = entries.nextElement();
|
||||
String name = jarEntry.getName();
|
||||
if (name.startsWith(internalFile) && name.toLowerCase().endsWith(".md")) {
|
||||
pages.add(name.substring(name.lastIndexOf('/') + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void indexDirectory(URL resource, List<String> pages) {
|
||||
File docs = new File(resource.getFile());
|
||||
String[] files = docs.list((dir, name) -> name.toLowerCase().endsWith(".md"));
|
||||
for (String file : files) {
|
||||
pages.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
String loadMarkdown(String path) throws IOException {
|
||||
String resource = "/html" + path;
|
||||
return loadFile(resource);
|
||||
}
|
||||
|
||||
protected void error(HttpServletResponse resp, int status, String message) throws IOException {
|
||||
resp.setStatus(status);
|
||||
resp.getWriter().println(getHeader());
|
||||
String html = loadFile("/html/docs/" + status + ".html");
|
||||
if(message == null || message.trim().isEmpty()) {
|
||||
message = "";
|
||||
}
|
||||
html = html.replace("{message}", message);
|
||||
resp.getWriter().println(html);
|
||||
resp.getWriter().println(getFooter());
|
||||
}
|
||||
}
|
|
@ -41,10 +41,16 @@ public class DocServer {
|
|||
MarkdownServlet markdownServlet = new MarkdownServlet();
|
||||
ServletHolder holder = new ServletHolder(markdownServlet);
|
||||
handler.addServletWithMapping(holder, "/docs/*");
|
||||
|
||||
AbstractDocServlet searchServlet = new SearchServlet();
|
||||
holder = new ServletHolder(searchServlet);
|
||||
handler.addServletWithMapping(holder, "/search/*");
|
||||
|
||||
StaticFileServlet staticFileServlet = new StaticFileServlet();
|
||||
holder = new ServletHolder(staticFileServlet);
|
||||
handler.addServletWithMapping(holder, "/static/*");
|
||||
|
||||
|
||||
try {
|
||||
server.start();
|
||||
started = true;
|
||||
|
|
|
@ -1,46 +1,59 @@
|
|||
package ctbrec.docs;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.vladsch.flexmark.html.HtmlRenderer;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
import com.vladsch.flexmark.util.options.MutableDataSet;
|
||||
|
||||
public class MarkdownServlet extends HttpServlet {
|
||||
public class MarkdownServlet extends AbstractDocServlet {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(MarkdownServlet.class);
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
String path = req.getRequestURI();
|
||||
LOG.debug("Path: [{}]", path);
|
||||
LOG.trace("Path: [{}]", path);
|
||||
try {
|
||||
String md = loadMarkdown(path);
|
||||
String html = markdownToHtml(md);
|
||||
resp.setStatus(HttpServletResponse.SC_OK);
|
||||
resp.getWriter().println(getHeader());
|
||||
resp.getWriter().println(html);
|
||||
resp.getWriter().println(getFooter());
|
||||
if(Objects.equal(path, "/docs/index.md")) {
|
||||
listPages(resp);
|
||||
} else {
|
||||
String md = loadMarkdown(path);
|
||||
String html = markdownToHtml(md);
|
||||
resp.setStatus(HttpServletResponse.SC_OK);
|
||||
resp.getWriter().println(getHeader());
|
||||
resp.getWriter().println(html);
|
||||
resp.getWriter().println(getFooter());
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
resp.setContentType("text/plain");
|
||||
resp.getWriter().println("Hö?");
|
||||
resp.getWriter().println();
|
||||
error(resp, HttpServletResponse.SC_NOT_FOUND, "");
|
||||
}
|
||||
}
|
||||
|
||||
private void listPages(HttpServletResponse resp) throws IOException {
|
||||
List<String> pages = getPages();
|
||||
String html = "<ul>";
|
||||
for (String page : pages) {
|
||||
html += "<li><a href=\"/docs/" + page + "\">" + page + "</a></li>";
|
||||
}
|
||||
html += "</ul>";
|
||||
resp.setStatus(HttpServletResponse.SC_OK);
|
||||
resp.getWriter().println(getHeader());
|
||||
resp.getWriter().println(html);
|
||||
resp.getWriter().println(getFooter());
|
||||
}
|
||||
|
||||
private String markdownToHtml(String markdown) {
|
||||
MutableDataSet options = new MutableDataSet();
|
||||
Parser parser = Parser.builder(options).build();
|
||||
|
@ -49,31 +62,4 @@ public class MarkdownServlet extends HttpServlet {
|
|||
String html = renderer.render(document);
|
||||
return html;
|
||||
}
|
||||
|
||||
private String loadMarkdown(String path) throws IOException {
|
||||
String resource = "/html" + path;
|
||||
return loadFile(resource);
|
||||
}
|
||||
|
||||
private String loadFile(String resource) throws IOException {
|
||||
InputStream resourceAsStream = getClass().getResourceAsStream(resource);
|
||||
if(resourceAsStream == null) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int length = 0;
|
||||
byte[] buffer = new byte[1024];
|
||||
while( (length = resourceAsStream.read(buffer)) >= 0 ) {
|
||||
out.write(buffer, 0, length);
|
||||
}
|
||||
return new String(out.toByteArray(), "utf-8");
|
||||
}
|
||||
|
||||
private String getHeader() throws IOException {
|
||||
return loadFile("/html/docs/header.html");
|
||||
}
|
||||
|
||||
private String getFooter() throws IOException {
|
||||
return loadFile("/html/docs/footer.html");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package ctbrec.docs;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
|
||||
public class SearchServlet extends AbstractDocServlet {
|
||||
private static final String Q = "term";
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
if(req.getParameter(Q) == null) {
|
||||
error(resp, HttpServletResponse.SC_BAD_REQUEST, "Parameter \""+Q+"\" is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
resp.setStatus(HttpServletResponse.SC_OK);
|
||||
resp.setContentType("application/json");
|
||||
JSONArray result = new JSONArray();
|
||||
List<String> pages = getPages();
|
||||
|
||||
String q = req.getParameter(Q).toLowerCase();
|
||||
String[] tokens = q.split("\\s+");
|
||||
for (String page : pages) {
|
||||
try {
|
||||
String content = loadMarkdown("/docs/" + page).toLowerCase();
|
||||
boolean allFound = true;
|
||||
for (String token : tokens) {
|
||||
if(!content.contains(token)) {
|
||||
allFound = false;
|
||||
}
|
||||
}
|
||||
if(allFound) {
|
||||
result.put(page);
|
||||
}
|
||||
} catch(FileNotFoundException e) {
|
||||
// virtual page like index.md -> ignore
|
||||
}
|
||||
}
|
||||
resp.getWriter().println(result.toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<h1>400 Bad Request</h1>
|
||||
<p>{message}</p>
|
||||
<a href="/docs/index.md">Try something else!</a>
|
|
@ -0,0 +1,2 @@
|
|||
<h1>404 File Not Found</h1>
|
||||
<a href="/docs/index.md">Try something else!</a>
|
|
@ -1,5 +1,5 @@
|
|||
How To Run The Server
|
||||
=======================
|
||||
------------
|
||||
The archive you downloaded contains a `server.bat` or `server.sh`, which can be used to start the server. On the first start, the server uses a default configuration. Once you terminate the server by pressing <kbd>ctrl</kbd> + <kbd>c</kbd>, the config is stored in your user home.
|
||||
|
||||
On Windows that is `C:\Users\{your user name}\AppData\Roaming\ctbrec\server.json`
|
||||
|
@ -8,4 +8,4 @@ On Linux it is `~/.config/ctbrec/server.json`
|
|||
|
||||
On macOS it is `/Users/{your user name}/Library/Preferences/ctbrec`
|
||||
|
||||
You can open this file in a text editor and change it to your likings. You probably only want to change httpPort and recordingsDir. Most of the other stuff is irrelevant since the server and CTB Recorder use the same config file format. When the server is running, you can connect to it with CTB Recorder by changing the setting "Record location" to "Remote".
|
||||
You can open this file in a text editor and change it to your likings. You probably only want to change `httpPort` and `recordingsDir`. Most of the other stuff is irrelevant since the server and CTB Recorder use the same config file format. When the server is running, you can connect to it with CTB Recorder by changing the setting "Record location" to "Remote".
|
|
@ -59,11 +59,22 @@
|
|||
<script src="/static/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Plugin JavaScript -->
|
||||
<script src="/static/vendor/jquery-ui/jquery-ui-1.12.1.js"></script>
|
||||
<script src="/static/vendor/jquery-easing/jquery.easing.min.js"></script>
|
||||
<script src="/static/vendor/magnific-popup/jquery.magnific-popup.min.js"></script>
|
||||
|
||||
<!-- Custom scripts for this template -->
|
||||
<script src="/static/freelancer.min.js"></script>
|
||||
|
||||
<script>
|
||||
$( "#search" ).autocomplete({
|
||||
source: "/search",
|
||||
minLength: 2,
|
||||
select: function( event, ui ) {
|
||||
location.href = "/docs/" + ui.item.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
|
|
|
@ -29,32 +29,47 @@
|
|||
|
||||
<!-- Plugin CSS -->
|
||||
<link href="/static/vendor/magnific-popup/magnific-popup.css" rel="stylesheet" type="text/css">
|
||||
<link href="/static/vendor/jquery-ui/jquery-ui-1.12.1.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="/static/freelancer.min.css" rel="stylesheet">
|
||||
<link href="/static/freelancer.css" rel="stylesheet">
|
||||
|
||||
<link rel="shortcut icon" href="/static/favicon.png" type="image/x-icon" />
|
||||
|
||||
<style>
|
||||
.ui-front {
|
||||
z-index: 2000;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body id="page-top">
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-expand-lg bg-secondary fixed-top text-uppercase" id="mainNav">
|
||||
<nav class="navbar navbar-expand-lg bg-secondary fixed-top text-uppercase" id="mainNav" style="padding-pottom: 3rem">
|
||||
<div class="container">
|
||||
<a class="navbar-brand js-scroll-trigger" href="#page-top"><img src="/static/icon64.png" alt="Logo"/>CTB Recorder</a>
|
||||
<a class="navbar-brand js-scroll-trigger" href="/docs/index.md"><img src="/static/icon64.png" alt="Logo"/>CTB Recorder</a>
|
||||
<button class="navbar-toggler navbar-toggler-right text-uppercase bg-primary text-white rounded" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
Menu
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item mx-0 mx-lg-1">
|
||||
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href="#about">Download</a>
|
||||
<div class="ui-widget">
|
||||
<label for="search">Search: </label>
|
||||
<input id="search" style="margin-top: .7rem" placeholder="Search">
|
||||
</div>
|
||||
<!--
|
||||
<div class="ui-widget"><input id="search" class="ui-autocomplete-input" autocomplete="off" placeholder="Search" style="margin-top: .7rem"></div>
|
||||
-->
|
||||
</li>
|
||||
<li class="nav-item mx-0 mx-lg-1">
|
||||
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href="#portfolio">Screenshots</a>
|
||||
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href="/docs/index.md">Index</a>
|
||||
</li>
|
||||
<!--
|
||||
<li class="nav-item mx-0 mx-lg-1">
|
||||
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href="#faq">FAQ</a>
|
||||
</li>
|
||||
|
@ -66,30 +81,27 @@
|
|||
<img src="/static/button-red.png" alt="Buy a coffee" style="width:150px; padding-top: 7px"/>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Header -->
|
||||
<!-- Header -
|
||||
<header class="masthead bg-primary text-white text-center">
|
||||
<div class="container">
|
||||
<!--
|
||||
<img class="img-fluid mb-5 d-block mx-auto" src="img/profile.png" alt="">
|
||||
-->
|
||||
<h1 class="text-uppercase mb-0">CTB Recorder</h1>
|
||||
<hr class="star-light">
|
||||
<h2 class="font-weight-light mb-0">A free recording software for different camsites.<br/>Currently supported: BongaCams, Cam4, CamSoda, Chaturbate, FC2Live, LiveJasmin, MyFreeCams, Streamate</h2>
|
||||
</div>
|
||||
</header>
|
||||
-->
|
||||
|
||||
|
||||
<!-- Source Code Section -->
|
||||
<section id="code">
|
||||
<div class="container">
|
||||
<h2 class="text-center text-uppercase text-secondary mb-0">Source Code</h2>
|
||||
<hr class="star-dark mb-5">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 mx-auto text-center">
|
||||
<div class="col-lg-10 mx-auto">
|
||||
<p class="lead">
|
||||
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
hallo
|
||||
================
|
||||
- sadf
|
||||
- bsdf
|
||||
- csdf
|
||||
|
||||
was geht?
|
||||
================
|
||||
```
|
||||
pre
|
||||
formatted
|
||||
text
|
||||
or what?
|
||||
```
|
|
@ -0,0 +1,382 @@
|
|||
body {
|
||||
color: #2C3E50;
|
||||
font-family: 'Lato';
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: #2C3E50;
|
||||
font-weight: 700;
|
||||
font-family: 'Montserrat';
|
||||
}
|
||||
|
||||
hr.star-light,
|
||||
hr.star-dark {
|
||||
max-width: 15rem;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
border: none;
|
||||
border-top: solid 0.25rem;
|
||||
margin-top: 2.5rem;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
hr.star-light:after,
|
||||
hr.star-dark:after {
|
||||
position: relative;
|
||||
top: -.8em;
|
||||
display: inline-block;
|
||||
padding: 0 0.25em;
|
||||
content: '\f005';
|
||||
font-family: FontAwesome;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
hr.star-light {
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
hr.star-light:after {
|
||||
color: #fff;
|
||||
background-color: #dc4444;
|
||||
}
|
||||
|
||||
hr.star-dark {
|
||||
border-color: #2C3E50;
|
||||
}
|
||||
|
||||
hr.star-dark:after {
|
||||
color: #2C3E50;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 9rem 6rem 0 6rem;
|
||||
}
|
||||
|
||||
section h2 {
|
||||
font-size: 2.25rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
section h2 {
|
||||
font-size: 3rem;
|
||||
line-height: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-xl {
|
||||
padding: 1rem 1.75rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.btn-social {
|
||||
width: 3.25rem;
|
||||
height: 3.25rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.scroll-to-top {
|
||||
z-index: 1042;
|
||||
right: 1rem;
|
||||
bottom: 1rem;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-to-top a {
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
background-color: rgba(33, 37, 41, 0.5);
|
||||
line-height: 3.1rem;
|
||||
}
|
||||
|
||||
#mainNav {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
font-weight: 700;
|
||||
font-family: 'Montserrat';
|
||||
}
|
||||
|
||||
#mainNav .navbar-brand {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav {
|
||||
margin-top: 1rem;
|
||||
letter-spacing: 0.0625rem;
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav li.nav-item a.nav-link {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav li.nav-item a.nav-link:hover {
|
||||
color: #dc4444;
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav li.nav-item a.nav-link:active, #mainNav .navbar-nav li.nav-item a.nav-link:focus {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav li.nav-item a.nav-link.active {
|
||||
color: #dc4444;
|
||||
}
|
||||
|
||||
#mainNav .navbar-toggler {
|
||||
font-size: 80%;
|
||||
padding: 0.8rem;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
#mainNav {
|
||||
padding-top: 1.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
-webkit-transition: padding-top 0.3s, padding-bottom 0.3s;
|
||||
-moz-transition: padding-top 0.3s, padding-bottom 0.3s;
|
||||
transition: padding-top 0.3s, padding-bottom 0.3s;
|
||||
}
|
||||
#mainNav .navbar-brand {
|
||||
font-size: 2em;
|
||||
-webkit-transition: font-size 0.3s;
|
||||
-moz-transition: font-size 0.3s;
|
||||
transition: font-size 0.3s;
|
||||
}
|
||||
#mainNav .navbar-nav {
|
||||
margin-top: 0;
|
||||
}
|
||||
#mainNav .navbar-nav > li.nav-item > a.nav-link.active {
|
||||
color: #fff;
|
||||
background: #dc4444;
|
||||
}
|
||||
#mainNav .navbar-nav > li.nav-item > a.nav-link.active:active, #mainNav .navbar-nav > li.nav-item > a.nav-link.active:focus, #mainNav .navbar-nav > li.nav-item > a.nav-link.active:hover {
|
||||
color: #fff;
|
||||
background: #dc4444;
|
||||
}
|
||||
#mainNav.navbar-shrink {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
#mainNav.navbar-shrink .navbar-brand {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
header.masthead {
|
||||
padding-top: calc(6rem + 72px);
|
||||
padding-bottom: 6rem;
|
||||
}
|
||||
|
||||
header.masthead h1 {
|
||||
font-size: 3rem;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
header.masthead h2 {
|
||||
font-size: 1.3rem;
|
||||
font-family: 'Lato';
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
header.masthead {
|
||||
padding-top: calc(6rem + 106px);
|
||||
padding-bottom: 6rem;
|
||||
}
|
||||
header.masthead h1 {
|
||||
font-size: 4.75em;
|
||||
line-height: 4rem;
|
||||
}
|
||||
header.masthead h2 {
|
||||
font-size: 1.75em;
|
||||
}
|
||||
}
|
||||
|
||||
.portfolio {
|
||||
margin-bottom: -15px;
|
||||
}
|
||||
|
||||
.portfolio .portfolio-item {
|
||||
position: relative;
|
||||
display: block;
|
||||
max-width: 25rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.portfolio .portfolio-item .portfolio-item-caption {
|
||||
-webkit-transition: all ease 0.5s;
|
||||
-moz-transition: all ease 0.5s;
|
||||
transition: all ease 0.5s;
|
||||
opacity: 0;
|
||||
background-color: rgba(220, 68, 68, 0.9);
|
||||
}
|
||||
|
||||
.portfolio .portfolio-item .portfolio-item-caption:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.portfolio .portfolio-item .portfolio-item-caption .portfolio-item-caption-content {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.portfolio {
|
||||
margin-bottom: -30px;
|
||||
}
|
||||
.portfolio .portfolio-item {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.portfolio-modal .portfolio-modal-dialog {
|
||||
padding: 3rem 1rem;
|
||||
min-height: calc(100vh - 2rem);
|
||||
margin: 1rem calc(1rem - 8px);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
-moz-box-shadow: 0 0 3rem 1rem rgba(0, 0, 0, 0.5);
|
||||
-webkit-box-shadow: 0 0 3rem 1rem rgba(0, 0, 0, 0.5);
|
||||
box-shadow: 0 0 3rem 1rem rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.portfolio-modal .portfolio-modal-dialog .close-button {
|
||||
position: absolute;
|
||||
top: 2rem;
|
||||
right: 2rem;
|
||||
}
|
||||
|
||||
.portfolio-modal .portfolio-modal-dialog .close-button i {
|
||||
line-height: 38px;
|
||||
}
|
||||
|
||||
.portfolio-modal .portfolio-modal-dialog h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.portfolio-modal .portfolio-modal-dialog {
|
||||
min-height: 100vh;
|
||||
padding: 5rem;
|
||||
margin: 3rem calc(3rem - 8px);
|
||||
}
|
||||
.portfolio-modal .portfolio-modal-dialog h2 {
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.floating-label-form-group {
|
||||
position: relative;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.floating-label-form-group input,
|
||||
.floating-label-form-group textarea {
|
||||
font-size: 1.5em;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
resize: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: none;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.floating-label-form-group label {
|
||||
font-size: 0.85em;
|
||||
line-height: 1.764705882em;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
top: 2em;
|
||||
display: block;
|
||||
margin: 0;
|
||||
-webkit-transition: top 0.3s ease, opacity 0.3s ease;
|
||||
-moz-transition: top 0.3s ease, opacity 0.3s ease;
|
||||
-ms-transition: top 0.3s ease, opacity 0.3s ease;
|
||||
transition: top 0.3s ease, opacity 0.3s ease;
|
||||
vertical-align: middle;
|
||||
vertical-align: baseline;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.floating-label-form-group:not(:first-child) {
|
||||
padding-left: 14px;
|
||||
border-left: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.floating-label-form-group-with-value label {
|
||||
top: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.floating-label-form-group-with-focus label {
|
||||
color: #dc4444;
|
||||
}
|
||||
|
||||
form .row:first-child .floating-label-form-group {
|
||||
border-top: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding-top: 5rem;
|
||||
padding-bottom: 5rem;
|
||||
background-color: #2C3E50;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
background-color: #1a252f;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #dc4444;
|
||||
}
|
||||
|
||||
a:focus, a:hover, a:active {
|
||||
color: #c82525;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.bg-primary {
|
||||
background-color: #dc4444 !important;
|
||||
}
|
||||
|
||||
.bg-secondary {
|
||||
background-color: #2C3E50 !important;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: #dc4444 !important;
|
||||
}
|
||||
|
||||
.text-secondary {
|
||||
color: #2C3E50 !important;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #dc4444;
|
||||
border-color: #dc4444;
|
||||
}
|
||||
|
||||
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
|
||||
background-color: #c82525;
|
||||
border-color: #c82525;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: #2C3E50;
|
||||
border-color: #2C3E50;
|
||||
}
|
||||
|
||||
.btn-secondary:hover, .btn-secondary:focus, .btn-secondary:active {
|
||||
background-color: #1a252f;
|
||||
border-color: #1a252f;
|
||||
}
|
File diff suppressed because one or more lines are too long
1311
client/src/main/resources/html/static/vendor/jquery-ui/jquery-ui-1.12.1.css
vendored
Normal file
1311
client/src/main/resources/html/static/vendor/jquery-ui/jquery-ui-1.12.1.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18706
client/src/main/resources/html/static/vendor/jquery-ui/jquery-ui-1.12.1.js
vendored
Normal file
18706
client/src/main/resources/html/static/vendor/jquery-ui/jquery-ui-1.12.1.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue