Browse Source

Site deployed via dual function render/static lambda function behind API Gateway.

Matt Clark 3 years ago
parent
commit
6dd9382e90

+ 14 - 78
sam.yml

@@ -15,21 +15,10 @@ Resources:
       CodeUri: target/lambda-1.0.0.jar
       MemorySize: 256
       ReservedConcurrentExecutions: 50
-      Handler: com.amazon.barcodes.lambda.RequestHandler::handleRequest
+      Handler: org.barcodeapi.LambdaHandler::handleRequest
       Runtime: java8
       Timeout: 15 
 
-  BarcodeApi:
-    Type: AWS::ApiGateway::RestApi
-    DependsOn: RenderFunction
-    Properties:
-      Name: !Sub "BarcodeAPI-Endpoint-${AWS::StackName}"
-      BinaryMediaTypes:
-        - "*/*"
-      EndpointConfiguration:
-        Types:
-          - REGIONAL
-
   ConfigLambdaPermission:
     Type: "AWS::Lambda::Permission"
     DependsOn:
@@ -40,6 +29,18 @@ Resources:
       FunctionName: !Ref RenderFunction
       Principal: apigateway.amazonaws.com
 
+  BarcodeApi:
+    Type: AWS::ApiGateway::RestApi
+    DependsOn:
+    - RenderFunction
+    Properties:
+      Name: !Sub "BarcodeAPI-Endpoint-${AWS::StackName}"
+      BinaryMediaTypes:
+        - "*/*"
+      EndpointConfiguration:
+        Types:
+          - REGIONAL
+
   BarcodeApiDeployment:
     Type: AWS::ApiGateway::Deployment
     DependsOn: BarcodeApiMethod
@@ -51,7 +52,7 @@ Resources:
     Properties:
       DeploymentId: !Ref BarcodeApiDeployment
       RestApiId: !Ref BarcodeApi
-      StageName: 'api'
+      StageName: 'web'
 
   BarcodeApiResource:
     Type: AWS::ApiGateway::Resource
@@ -72,68 +73,3 @@ Resources:
         IntegrationHttpMethod: POST
         Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RenderFunction.Arn}/invocations"
 
-  BarcodeWebBucket:
-    Type: AWS::S3::Bucket
-
-  BarcodeWebBucketPolicy:
-    Type: AWS::S3::BucketPolicy
-    Properties:
-      Bucket: !Ref BarcodeWebBucket
-      PolicyDocument:
-        Version: 2012-10-17
-        Statement:
-        - Sid: AddPerm
-          Effect: Allow
-          Principal: "*"
-          Action:
-          - "s3:GetObject"
-          Resource: !Sub "${BarcodeWebBucket.Arn}/*"
-
-  BarcodeServer:
-    Type: AWS::CloudFront::Distribution
-    DependsOn:
-    - BarcodeApi
-    - BarcodeWebBucket
-    Properties:
-      DistributionConfig:
-        DefaultRootObject: index.html
-        DefaultCacheBehavior:
-          ForwardedValues:
-            QueryString: true
-          TargetOriginId: Web
-          ViewerProtocolPolicy: redirect-to-https
-        CacheBehaviors:
-        - AllowedMethods:
-          - GET
-          - HEAD
-          - OPTIONS
-          CachedMethods:
-          - GET
-          - HEAD
-          Compress: true
-          ForwardedValues:
-            Headers:
-            - Accept
-            - Referer
-            - Athorization
-            - Content-Type
-            QueryString: true
-          PathPattern: "/api/*"
-          TargetOriginId: API
-          ViewerProtocolPolicy: redirect-to-https
-        Enabled: true
-        Aliases:
-        - "barcodeapi.org"
-        Origins:
-        - DomainName:
-            Fn::GetAtt: [ BarcodeWebBucket , "DomainName" ]
-          Id: Web
-          S3OriginConfig:
-            OriginAccessIdentity:
-              Ref: AWS::NoValue
-        - DomainName: !Sub "${BarcodeApi}.execute-api.${AWS::Region}.amazonaws.com"
-          Id: API
-          CustomOriginConfig:
-            OriginProtocolPolicy: match-viewer
-          OriginPath: /api
-

+ 60 - 0
src/main/java/org/barcodeapi/LambdaHandler.java

@@ -0,0 +1,60 @@
+package org.barcodeapi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLDecoder;
+
+import org.barcodeapi.lambda.ApiHandler;
+import org.barcodeapi.lambda.TypesHandler;
+import org.barcodeapi.lambda.WebHandler;
+import org.json.JSONObject;
+
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
+import com.amazonaws.util.IOUtils;
+
+public class LambdaHandler implements RequestStreamHandler {
+
+	/**
+	 * Called for each render request.
+	 */
+	@Override
+	public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
+
+		// read all bytes and parse as json
+		String raw = IOUtils.toString(input);
+		JSONObject request = new JSONObject(raw);
+
+		// parse the request
+		JSONObject response = parseRequest(request);
+
+		// write response and flush
+		output.write(response.toString().getBytes());
+		output.flush();
+	}
+
+	public JSONObject parseRequest(JSONObject request) throws IOException {
+
+		String path = request.optString("path");
+		path = URLDecoder.decode(path, "UTF-8");
+		String target = path.split("[/?]")[1];
+
+		System.out.println("Path: " + path);
+		System.out.println("Target: " + target);
+
+		switch (target) {
+
+		case "api":
+			String action = path.substring(path.indexOf(target) + target.length(), path.length());
+			System.out.println("Action: " + action);
+			return ApiHandler.handle(request, action);
+
+		case "types":
+			return TypesHandler.handle(request);
+
+		default:
+			return WebHandler.handle(request, path);
+		}
+	}
+}

+ 10 - 42
src/main/java/com/amazon/barcodes/lambda/RequestHandler.java → src/main/java/org/barcodeapi/lambda/ApiHandler.java

@@ -1,8 +1,6 @@
-package com.amazon.barcodes.lambda;
+package org.barcodeapi.lambda;
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 
 import org.barcodeapi.server.core.Barcode;
 import org.barcodeapi.server.core.CodeGenerators;
@@ -10,16 +8,12 @@ import org.barcodeapi.server.core.GenerationException;
 import org.barcodeapi.server.gen.BarcodeFactory;
 import org.json.JSONObject;
 
-import com.amazonaws.services.lambda.runtime.Context;
-import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
-import com.amazonaws.util.IOUtils;
-
-public class RequestHandler implements RequestStreamHandler {
+public class ApiHandler {
 
 	/**
 	 * Called when the lambda is created.
 	 */
-	public RequestHandler() {
+	public ApiHandler() {
 
 		// warm up the generators
 		CodeGenerators.getInstance();
@@ -27,36 +21,17 @@ public class RequestHandler implements RequestStreamHandler {
 
 	/**
 	 * Called for each render request.
-	 */
-	@Override
-	public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
-
-		// read all bytes and parse as json
-		String raw = IOUtils.toString(input);
-		JSONObject request = new JSONObject(raw);
-
-		// parse the request
-		JSONObject response = parseRequest(request);
-
-		// write response and flush
-		output.write(response.toString().getBytes());
-		output.flush();
-	}
-
-	/**
-	 * Parse the request body for a response object.
 	 * 
-	 * @param request
-	 * @return
+	 * @param action
 	 */
-	private JSONObject parseRequest(JSONObject request) {
+	public static JSONObject handle(JSONObject request, String action) throws IOException {
 
 		Barcode barcode;
 
 		try {
 
 			// render and log details
-			barcode = BarcodeFactory.requestBarcode(request.getString("path"));
+			barcode = BarcodeFactory.requestBarcode(action);
 			System.out.println(System.currentTimeMillis() + " : " + //
 					"Rendered [ " + barcode.getType() + " ] " + //
 					"with [ " + barcode.getData() + " ] " + //
@@ -65,13 +40,7 @@ public class RequestHandler implements RequestStreamHandler {
 					"for [ " + getCaller(request) + " ]");
 		} catch (GenerationException e) {
 
-			return new JSONObject()//
-					.put("statusCode", 400)//
-					.put("headers", new JSONObject())//
-					.put("body", new JSONObject()//
-							.put("error", e.getMessage())//
-							.put("details", e.getExceptionType()))//
-					.put("isBase64Encoded", false);
+			return LambdaResponseFactory.error(e);
 		}
 
 		return new JSONObject()//
@@ -83,13 +52,12 @@ public class RequestHandler implements RequestStreamHandler {
 						.put("Content-Type", "image/png")//
 						.put("Content-Disposition", "filename=" + barcode.getDataNice() + ".png")//
 						.put("Content-Length", barcode.getImageSize())//
-						.put("X-Barcode-Type", barcode.getType())//
-						.put("X-Barcode-Content", barcode.getDataEncoded()))//
+						.put("X-Barcode-Type", barcode.getType()))//
 				.put("isBase64Encoded", true)//
 				.put("body", barcode.getImage());
 	}
 
-	public String getSource(JSONObject request) {
+	public static String getSource(JSONObject request) {
 
 		// get source of the request
 		String source;
@@ -103,7 +71,7 @@ public class RequestHandler implements RequestStreamHandler {
 		return source;
 	}
 
-	public String getCaller(JSONObject request) {
+	public static String getCaller(JSONObject request) {
 
 		// get users IP
 		String caller;

+ 18 - 0
src/main/java/org/barcodeapi/lambda/LambdaResponseFactory.java

@@ -0,0 +1,18 @@
+package org.barcodeapi.lambda;
+
+import org.barcodeapi.server.core.GenerationException;
+import org.json.JSONObject;
+
+public class LambdaResponseFactory {
+
+	public static JSONObject error(GenerationException e) {
+
+		return new JSONObject()//
+				.put("statusCode", 400)//
+				.put("headers", new JSONObject())//
+				.put("body", new JSONObject()//
+						.put("error", e.getMessage())//
+						.put("details", e.getExceptionType()))//
+				.put("isBase64Encoded", false);
+	}
+}

+ 49 - 0
src/main/java/org/barcodeapi/lambda/TypesHandler.java

@@ -0,0 +1,49 @@
+package org.barcodeapi.lambda;
+
+import java.io.IOException;
+
+import org.barcodeapi.server.core.GenerationException;
+import org.barcodeapi.server.core.GenerationException.ExceptionType;
+import org.barcodeapi.server.gen.BarcodeType;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+public class TypesHandler {
+
+	/**
+	 * Called for each render request.
+	 * 
+	 * @param action
+	 */
+	public static JSONObject handle(JSONObject request) throws IOException {
+
+		try {
+
+			JSONArray types = new JSONArray();
+			for (BarcodeType codeType : BarcodeType.values()) {
+
+				types.put(new JSONObject()//
+						.put("name", codeType.getName())//
+						.put("example", codeType.getExample())//
+						.put("typeStrings", codeType.getTypeStrings())//
+						.put("formatPattern", codeType.getFormatPattern()));
+			}
+
+			return new JSONObject()//
+					.put("statusCode", 200)//
+					.put("headers", new JSONObject()//
+							.put("Server", "BarcodeAPI.org")//
+							.put("Accept-Charset", "utf-8")//
+							.put("Cache-Control", "max-age=86400, public")//
+							.put("Content-Type", "application/json")//
+							.put("Content-Length", types.toString().length()))//
+					.put("isBase64Encoded", false)//
+					.put("body", types.toString());
+
+		} catch (Exception e) {
+
+			return LambdaResponseFactory.error(//
+					new GenerationException(ExceptionType.FAILED, e));
+		}
+	}
+}

+ 43 - 0
src/main/java/org/barcodeapi/lambda/WebHandler.java

@@ -0,0 +1,43 @@
+package org.barcodeapi.lambda;
+
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.barcodeapi.server.core.GenerationException;
+import org.barcodeapi.server.core.GenerationException.ExceptionType;
+import org.json.JSONObject;
+
+public class WebHandler {
+
+	/**
+	 * Called for each render request.
+	 * 
+	 * @param action
+	 */
+	public static JSONObject handle(JSONObject request, String action) throws IOException {
+
+		try {
+
+			URL url = WebHandler.class.getResource(action);
+			byte[] bytes = Files.readAllBytes(Paths.get(url.toURI()));
+			String page = new String(bytes);
+
+			return new JSONObject()//
+					.put("statusCode", 200)//
+					.put("headers", new JSONObject()//
+							.put("Server", "BarcodeAPI.org")//
+							.put("Accept-Charset", "utf-8")//
+							.put("Cache-Control", "max-age=86400, public")//
+							.put("Content-Type", "text/html")//
+							.put("Content-Length", page.length()))//
+					.put("isBase64Encoded", false)//
+					.put("body", page);
+		} catch (Exception e) {
+
+			return LambdaResponseFactory.error(//
+					new GenerationException(ExceptionType.FAILED, e));
+		}
+	}
+}

+ 2 - 2
src/main/java/org/barcodeapi/server/core/Barcode.java

@@ -47,7 +47,8 @@ public class Barcode {
 
 	public String getDataNice() {
 
-		return data.replaceAll("[!@#$%^&*\\(\\)\\[\\]\\{\\};:\\',\\<\\>\\\"]", "");
+		String nice = data.replaceAll("[^\\x20-\\x7F]", "");
+		return nice.replaceAll("[!@#$%^&*\\(\\)\\[\\]\\{\\};:\\',\\<\\>\\\"]", "");
 	}
 
 	public String getDataEncoded() {
@@ -60,5 +61,4 @@ public class Barcode {
 			return null;
 		}
 	}
-
 }

+ 5 - 4
src/main/java/org/barcodeapi/server/core/Blacklist.java

@@ -1,6 +1,8 @@
 package org.barcodeapi.server.core;
 
-import java.util.ArrayList;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.List;
 
 public class Blacklist {
@@ -11,9 +13,8 @@ public class Blacklist {
 
 		try {
 
-			// URL file = Blacklist.class.getResource("blacklist.conf");
-			// blacklist = Files.readAllLines(Paths.get(file.toURI()));
-			blacklist = new ArrayList<String>();
+			URL file = Blacklist.class.getResource("/blacklist.conf");
+			blacklist = Files.readAllLines(Paths.get(file.toURI()));
 		} catch (Exception e) {
 
 			throw new RuntimeException("Failed to initialize blacklist.");

+ 1 - 1
src/main/java/org/barcodeapi/server/core/GenerationException.java

@@ -5,7 +5,7 @@ public class GenerationException extends Exception {
 	private static final long serialVersionUID = 1L;
 
 	public enum ExceptionType {
-		EMPTY, BLACKLIST, FAILED, INVALID;
+		PATH, ACTION, EMPTY, BLACKLIST, FAILED, INVALID;
 	}
 
 	private final ExceptionType type;

+ 37 - 13
src/main/java/org/barcodeapi/server/gen/BarcodeType.java

@@ -5,14 +5,16 @@ public enum BarcodeType {
 	/**
 	 * UPC-E type UPC code;
 	 */
-	UPC_E(new String[] { "e", "upc-e", "upce" }, //
+	UPC_E("UPC-E", "01306034", //
+			new String[] { "e", "upc-e", "upce" }, //
 			"^(?=.*0)[0-9]{8}$", //
 			"^(?=.*0)[0-9]{7,8}$"),
 
 	/**
 	 * UPC-A type UPC code;
 	 */
-	UPC_A(new String[] { "a", "upc-a", "upca", "upc" }, //
+	UPC_A("UPC-A", "455003400782", //
+			new String[] { "a", "upc-a", "upca", "upc" }, //
 			"^(?=.*0)[0-9]{12}$", //
 			"^(?=.*0)[0-9]{11,12}$"),
 
@@ -21,7 +23,8 @@ public enum BarcodeType {
 	 * 
 	 * 7 numerical digits followed by a single checksum digit.
 	 */
-	EAN8(new String[] { "8", "ean-8", "ean8" }, //
+	EAN8("EAN-8", "00000000", //
+			new String[] { "8", "ean-8", "ean8" }, //
 			"^[0-9]{8}$", //
 			"^[0-9]{7,8}$"),
 
@@ -30,14 +33,16 @@ public enum BarcodeType {
 	 * 
 	 * 12 numerical digits followed by a single checksum digit.
 	 */
-	EAN13(new String[] { "13", "ean-13", "ean13" }, //
+	EAN13("EAN-13", "0000000000000", //
+			new String[] { "13", "ean-13", "ean13" }, //
 			"^[0-9]{13}$", //
 			"^[0-9]{12,13}$"),
 
 	/**
 	 * Codabar type code;
 	 */
-	CODABAR(new String[] { "codabar" }, //
+	CODABAR("Codabar", "1257903", //
+			new String[] { "codabar" }, //
 			"^[0-9:$]{4,12}$", //
 			"^[0-9-:$\\/.+]+$"), //
 
@@ -46,8 +51,9 @@ public enum BarcodeType {
 	 * 
 	 * Variable length consisting of only numbers and upper-case characters.
 	 */
-	Code39(new String[] { "39", "code-39", "code39" }, //
-			"^[A-Z0-9 $.\\/]{1,20}$", //
+	Code39("Code39", "CODE 39", //
+			new String[] { "39", "code-39", "code39" }, //
+			"^[A-Z0-9 $.\\/]{1,10}$", //
 			"^[A-Z*0-9 -$%.\\/+]+$"),
 
 	/**
@@ -55,8 +61,9 @@ public enum BarcodeType {
 	 * 
 	 * Variable length consisting of numbers, letters, and symbols.
 	 */
-	Code128(new String[] { "128", "code-128", "code128" }, //
-			"^[ !#$()*.\\/0-9=?A-Z_a-z~]{1,24}$", //
+	Code128("Code128", "code_128", //
+			new String[] { "128", "code-128", "code128" }, //
+			"^[ !#$()*.\\/0-9=?A-Z_a-z~]{1,16}$", //
 			"^[ !\"#$%&'()*+,-.\\/0-9:;<=>?@A-Z\\[\\\\\\]^_`a-z{|}~]+$"),
 
 	/**
@@ -64,7 +71,8 @@ public enum BarcodeType {
 	 * 
 	 * A high density data code with error correction.
 	 */
-	QRCode(new String[] { "qr", "qr-code", "qrcode" }, //
+	QRCode("QR Code", "This is a QR Code.", //
+			new String[] { "qr", "qr-code", "qrcode" }, //
 			"^.{1,64}$", //
 			"^.{1,65535}$"),
 
@@ -73,7 +81,8 @@ public enum BarcodeType {
 	 * 
 	 * A high density data code with error correction.
 	 */
-	DataMatrix(new String[] { "dm", "data-matrix", "datamatrix", "matrix", "data" }, //
+	DataMatrix("Data Matrix", "This is a Data Matrix.", //
+			new String[] { "dm", "data-matrix", "datamatrix", "matrix", "data" }, //
 			"^[ !\"#$%&'()*+,-.\\/0-9:;<=>?@A-Z\\[\\\\\\]^_`a-z{|}~]{1,2335}$", //
 			"^[ !\"#$%&'()*+,-.\\/0-9:;<=>?@A-Z\\[\\\\\\]^_`a-z{|}~]{1,2335}$"),
 
@@ -82,13 +91,16 @@ public enum BarcodeType {
 	 * 
 	 * 
 	 */
-	PDF417(new String[] { "417", "pdf417", "pdf" }, //
+	PDF417("PDF 417", "This is a PDF - 417.", //
+			new String[] { "417", "pdf417", "pdf" }, //
 			"^[ !\"#$%&'()*+,-.\\/0-9:;<=>?@A-Z\\[\\\\\\]^_`a-z{|}~]{1,2335}$", //
 			"^[ !\"#$%&'()*+,-.\\/0-9:;<=>?@A-Z\\[\\\\\\]^_`a-z{|}~]{1,2335}$");
 
 	/**
 	 * Local Variables
 	 */
+	private final String name;
+	private final String example;
 	private final String[] types;
 
 	private final String autoPattern;
@@ -99,14 +111,26 @@ public enum BarcodeType {
 	 * 
 	 * @param typeStrings
 	 */
-	BarcodeType(String[] typeStrings, String automatchPattern, String extendedPattern) {
+	BarcodeType(String name, String example, String[] typeStrings, String automatchPattern, String extendedPattern) {
 
+		this.name = name;
+		this.example = example;
 		this.types = typeStrings;
 
 		this.autoPattern = automatchPattern;
 		this.formatPattern = extendedPattern;
 	}
 
+	public String getName() {
+
+		return name;
+	}
+
+	public String getExample() {
+
+		return example;
+	}
+
 	/**
 	 * Get a list of all IDs associated with a CodeType.
 	 * 

+ 60 - 0
src/main/resources/about.html

@@ -0,0 +1,60 @@
+<html>
+<head>
+<title>BarcodeAPI.org</title>
+
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+<meta name="keywords"
+	content="barcode, barcode app, barcode generator, barcode api, qr code, qr code generator, code 128, code128, ean8, eac-8, ean13, ean-13, codabar, download barcode, barcodes, pdf, pdf 417">
+<meta name="description"
+	content="Generate barcodes of nearly any type, scan it in the web browser, or download the generated images for free.">
+
+<link rel="icon" type="image/png" href="favicon.png" />
+
+<script src="js/functions.js"></script>
+<script src="js/help.js"></script>
+
+<link rel="stylesheet" type="text/css" href="css/help.css" />
+<link rel="stylesheet" type="text/css" href="css/header.css" />
+</head>
+<body>
+	<div id="global_wrapper">
+
+		<div id="content_wrapper">
+
+			<div id="header">
+				<div id="header_left" onclick="back();">
+					<img class="header_option" src="api/qr/..b" />
+					<br />
+					Back
+				</div>
+
+				<div id="header_title">
+					<img id="header_image" src="api/128/$$@formats.html$$@">
+				</div>
+
+				<div id="header_right" onclick="go('index.html#auto');">
+					<img class="header_option" src="api/qr/..h" />
+					<br />
+					Home
+				</div>
+			</div>
+
+			<div id="help_details">
+				<span>BarcodeAPI.org, Free & Open Source, 2017-2019</span>
+			</div>
+
+			<div id="page_details">
+
+				Generate barcodes of nearly any type, scan it in the web browser, or
+				download the generated images for free. BarcodeAPI.org is an open
+				source barcode server capable of generating a wide range of barcodes
+				over a RESTful HTTP server.
+				<br />
+				<a href="https://git.mclarkdev.com/BarcodeAPI.org/server">Source</a>
+			</div>
+		</div>
+	</div>
+</body>
+
+</html>

+ 48 - 0
src/main/resources/css/header.css

@@ -0,0 +1,48 @@
+#header {
+	position: relative;
+	top: 0px;
+	height: 20%;
+	width: 90%;
+	margin-left: 5%;
+	margin-right: 5%;
+}
+
+#header_right {
+	margin: 3%;
+	position: absolute;
+	top: 0px;
+	right: 0px;
+	width: 12%;
+	height: 12%;
+	margin-left: 2%;
+	text-align: center;
+}
+
+#header_title {
+	position: absolute;
+	left: 16%;
+	top: 0px;
+	height: 100%;
+	width: 68%;
+}
+
+#header_image {
+	postition: absolute;
+	width: 100%;
+	height: 100%;
+}
+
+#header_left {
+	margin: 3%;
+	position: absolute;
+	top: 0px;
+	left: 0px;
+	width: 12%;
+	height: 12%;
+	margin-left: 2%;
+	text-align: center;
+}
+
+.header_option {
+	width: 100%;
+}

+ 77 - 0
src/main/resources/css/help.css

@@ -0,0 +1,77 @@
+#global_wrapper {
+	position: absolute;
+	width: 94%;
+	height: 88%;
+	top: 0px;
+	left: 0px;
+	margin: 3%;
+	min-width: 320px;
+	min-height: 320px;
+}
+
+#content_wrapper {
+	position: relative;
+	max-width: 1024px;
+	margin-left: auto;
+	margin-right: auto;
+	height: 100%;
+	width: auto;
+	overflow: hidden;
+}
+
+#help_details {
+	width: 350px;
+	height: 25px;
+	vertical-align: middle;
+	line-height: 25px;
+	bottom: 0px;
+	position: absolute;
+	text-align: center;
+	bottom: 0px;
+	left: 50%;
+	margin-left: -150px;
+}
+
+#page_details {
+	position: relative;
+	margin-left: 2%;
+	margin-right: 2%;
+	margin-top: 5%;
+	max-height: 65%;
+	overflow-y: scroll;
+}
+
+.type_wrapper {
+	position:relative;
+	height: 150px;
+	float: left;
+	width: 42%;
+	margin: 2%;
+}
+
+.type_name {
+	position: relative;
+	height: 25%;
+	width: 90%;
+	margin-left: auto;
+	margin-right: auto;
+	text-align: center;
+}
+
+.type_barcode {
+	display: block;
+	margin-left: auto;
+	margin-right: auto;
+	max-width: 90%;
+	height: 75%;
+}
+
+::-webkit-scrollbar {
+	display: none;
+}
+
+.row:after {
+	content: "";
+	display: table;
+	clear: both;
+}

+ 90 - 0
src/main/resources/css/index.css

@@ -0,0 +1,90 @@
+#global_wrapper {
+	position: absolute;
+	width: 94%;
+	height: 88%;
+	top: 0px;
+	left: 0px;
+	margin: 3%;
+	min-width: 320px;
+	min-height: 320px;
+}
+
+#content_wrapper {
+	position: relative;
+	max-width: 1024px;
+	margin-left: auto;
+	margin-right: auto;
+	height: 100%;
+	width: auto;
+}
+
+#help_details {
+	width: 350px;
+	height: 25px;
+	vertical-align: middle;
+	line-height: 25px;
+	bottom: 0px;
+	position: fixed;
+	text-align: center;
+	bottom: 0px;
+	left: 50%;
+	margin-left: -150px;
+}
+
+#generator {
+	position: relative;
+	margin-left: auto;
+	margin-right: auto;
+	max-width: 64%;
+	margin-top: 5%;
+}
+
+#generator_type {
+	
+}
+
+#generator_selector {
+	
+}
+
+#generator_response {
+	
+}
+
+#generator_details {
+	
+}
+
+#generator_barcode {
+	position: relative;
+	width: 100%;
+	height: auto;
+}
+
+#generator_image {
+	display: block;
+	position: relative;
+	margin-left: auto;
+	margin-right: auto;
+	max-width: 90%;
+	max-height: 250px;
+	position: relative;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+#generator_image[src=""] {
+	display: none;
+}
+
+#generator_input {
+	margin-left: 5%;
+	margin-right: 5%;
+}
+
+#generator_text {
+	width: 100%;
+	height: 45px;
+	font-size: 18;
+	text-align: center;
+}

+ 67 - 0
src/main/resources/formats.html

@@ -0,0 +1,67 @@
+<html>
+<head>
+<title>BarcodeAPI.org</title>
+
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+<meta name="keywords"
+	content="barcode, barcode app, barcode generator, barcode api, qr code, qr code generator, code 128, code128, ean8, eac-8, ean13, ean-13, codabar, download barcode, barcodes, pdf, pdf 417">
+<meta name="description"
+	content="Generate barcodes of nearly any type, scan it in the web browser, or download the generated images for free.">
+
+<link rel="icon" type="image/png" href="favicon.png" />
+
+<script src="js/functions.js"></script>
+<script src="js/help.js"></script>
+
+<link rel="stylesheet" type="text/css" href="css/help.css" />
+<link rel="stylesheet" type="text/css" href="css/header.css" />
+</head>
+<body>
+	<div id="global_wrapper">
+
+		<div id="content_wrapper">
+
+			<div id="header">
+				<div id="header_left" onclick="back();">
+					<img class="header_option" src="api/qr/..b" />
+					<br />
+					Back
+				</div>
+
+				<div id="header_title">
+					<img id="header_image" src="api/128/$$@formats.html$$@">
+				</div>
+
+				<div id="header_right" onclick="go('about.html');">
+					<img class="header_option" src="api/qr/..a" />
+					<br />
+					About
+				</div>
+			</div>
+
+			<div id="help_details">
+				<span>BarcodeAPI.org, Free & Open Source, 2017-2019</span>
+			</div>
+
+			<div id="page_details">
+
+				<script>
+					get("types/", function(data) {
+
+						data = JSON.parse(data);
+
+						for ( var x in data) {
+
+							renderType(data[x].name, data[x].typeStrings[0],
+									data[x].example);
+						}
+					});
+				</script>
+			</div>
+		</div>
+	</div>
+</body>
+
+
+</html>

+ 0 - 1
src/main/resources/help.html

@@ -1 +0,0 @@
-https://barcodeapi.org/api/Code_Generation

+ 0 - 136
src/main/resources/index.css

@@ -1,136 +0,0 @@
-#global_wrapper {
-	position: absolute;
-	width: 100%;
-	height: 100%;
-	top: 0px;
-	left: 0px;
-}
-
-#content_wrapper {
-	width: auto;
-	max-width: 1024px;
-	margin-left: auto;
-	margin-right: auto;
-}
-
-#header {
-	background-image: url("logo.png");
-	background-size: cover;
-	background-position: 50% 50%;
-	max-width: 1024px;
-	height: 75px;
-}
-
-#input_wrapper {
-	width: 100%;
-}
-
-#text {
-	width: 100%;
-	height: 45px;
-	font-size: 18px;
-	border-radius: 5px;
-	text-align: center;
-}
-
-#frame {
-	border: solid 1px;
-}
-
-#barcode_output {
-	max-width: 100%;
-	margin-left: auto;
-	margin-right: auto;
-	display: block;
-	padding-top: 8px;
-	padding-bottom: 8px;
-}
-
-#barcode_outout[src=""] {
-	display: none;
-}
-
-#footer_wrapper {
-	position: absolute;
-	bottom: 0px;
-	width: 100%;
-}
-
-#footer_help {
-	font-size: 18px;
-	max-width: 650px;
-	position: absolute;
-	bottom: 0px;
-	left: 0px;
-	max-width: 650px;
-	max-height: 350px;
-	max-width: 650px;
-	padding: 16px;
-}
-
-#footer_link {
-	padding: 8px;
-	font-size: 14px;
-	position: absolute;
-	bottom: 0px;
-	right: 0px;
-}
-
-a:link, a:visited {
-	text-decoration: none;
-	color: blue;
-	cursor: pointer;
-}
-
-.topnav {
-	background-color: #333;
-	overflow: hidden;
-}
-
-.topnav a {
-	float: left;
-	color: #f2f2f2;
-	text-align: center;
-	padding: 14px 16px;
-	text-decoration: none;
-	font-size: 17px;
-}
-
-/* Change the color of links on hover */
-.topnav a:hover {
-	background-color: #ddd;
-	color: black;
-}
-
-/* Add a color to the active/current link */
-.topnav a.active {
-	background-color: #4CAF50;
-	color: white;
-}
-
-#barcode_options {
-	height: 24px;
-	width: 100%;
-}
-
-#barcode_print_button {
-	width: 50%;
-	text-align: center;
-	float: left;
-}
-
-#barcode_print_content {
-	display: block;
-	height: 24px;
-}
-
-#barcode_download_button {
-	width: 50%;
-	text-align: center;
-	float: right;
-}
-
-#barcode_print_content {
-	display: block;
-	height: 24px;
-}

+ 87 - 37
src/main/resources/index.html

@@ -9,46 +9,99 @@
 <meta name="description"
 	content="Generate barcodes of nearly any type, scan it in the web browser, or download the generated images for free.">
 
-<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="icon" type="image/png" href="favicon.png" />
 
-<script src="/index.js"></script>
+<script src="js/functions.js"></script>
+<script src="js/master.js"></script>
 
-<link rel="stylesheet" type="text/css" href="/index.css" />
+<link rel="stylesheet" type="text/css" href="css/index.css" />
+<link rel="stylesheet" type="text/css" href="css/header.css" />
 </head>
-<body onload="loadHash()">
+<body onload="window.onhashchange()" onkeypress="onKeyEvent(event)"
+	onkeyup="onBackspace(event)">
 	<div id="global_wrapper">
 
 		<div id="content_wrapper">
 
-			<div id="header"></div>
-
-			<div id="input_wrapper">
-				<div id="topnav" class="topnav">
-					<a id="type-auto" onclick="location.hash='auto'">Auto</a>
-					<a id="type-e" onclick="location.hash='e'">UPC-E</a>
-					<a id="type-a" onclick="location.hash='a'">UPC-A</a>
-					<a id="type-8" onclick="location.hash='8'">EAN-8</a>
-					<a id="type-13" onclick="location.hash='13'">EAN-13</a>
-					<a id="type-codabar" onclick="location.hash='codabar'">Codabar</a>
-					<a id="type-39" onclick="location.hash='39'">Code39</a>
-					<a id="type-128" onclick="location.hash='128'">Code128</a>
-					<a id="type-qr" onclick="location.hash='qr'">QR Code</a>
-					<a id="type-dm" onclick="location.hash='dm'">Data Matrix</a>
-					<a id="type-417" onclick="location.hash='417'">PDF 417</a>
+			<div id="header">
+
+				<div id="header_left" onclick="back();">
+					<img class="header_option" src="api/qr/..b" /> <br /> Back
+				</div>
+
+				<div id="header_title">
+					<img id="header_image" src="api/128/%24%24%40index.html%24%24%40">
 				</div>
 
-				<div id="frame">
-					<form onsubmit="return false;">
+				<div id="header_right" onclick="go('formats.html');">
+					<img class="header_option" src="api/qr/..f" /> <br /> Formats
+				</div>
+			</div>
+
+			<div id="help_details">BarcodeAPI.org, Free & Open Source,
+				2017-2019</div>
+
+			<div id="generator">
+				<div id="generator_type">
+					<select id="generator_selector" onchange="onTypeChanged();">
+					</select>
+
+					<script>
+						get("types/",
+								function(data) {
+
+									data = JSON.parse(data);
+
+									for ( var x in data) {
+
+										renderOption(data[x].name,
+												data[x].typeStrings[0],
+												data[x].example);
+									}
+								});
+					</script>
+				</div>
 
-						<input id="text" onkeyup="genCode()" type="text"
-							placeholder="a code for anything..." autocomplete="off"
-							maxlength="200" />
-					</form>
-					<div id="barcode_wrapper">
-						<img id="barcode_output" />
+				<div id="generator_response">
+					<div id="generator_details"></div>
+					<div id="generator_barcode">
+						<img id="generator_image" />
 					</div>
+				</div>
+				<div id="generator_input">
+
+					<input id="generator_text" type="text" autocomplete="off"
+						maxlength="200" readonly /> <img class="generator_clear"
+						src="api/qr/..c" />
+				</div>
+			</div>
+		</div>
+	</div>
+</body>
+
+
+
+
+
 
-					<div id="barcode_options">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<!-- 	
+		
 
 						<div id="barcode_print">
 							<a id="barcode_print_button" onclick="printCode()">
@@ -61,24 +114,21 @@
 								<span id="barcode_download_content">Download!</span>
 							</a>
 						</div>
-					</div>
-				</div>
-			</div>
-		</div>
-
-		<div id="footer_wrapper">
-			<div id="footer_help">
+				
+				
 				<p>Generate barcodes of nearly any type, scan it in the web
 					browser, or download the generated images for free. BarcodeAPI.org
 					is an open source barcode server capable of generating a wide range
 					of barcodes over a RESTful HTTP server.</p>
 			</div>
 			<div id="footer_link">
-				<a href="https://git.mclarkdev.com/BarcodeAPI.org/server">Free,
-					Open Source</a> <br /> BarcodeAPI.org, 2018
 			</div>
 		</div>
 	</div>
 </body>
 </html>
 
+-->
+
+
+</html>

+ 0 - 84
src/main/resources/index.js

@@ -1,84 +0,0 @@
-/**
- * Call our method when the URL hash changes.
- */
-window.onhashchange = loadHash;
-
-/**
- * Called each time we should read the hash of the URL.
- * 
- * @returns
- */
-function loadHash() {
-
-	// Get current hash ( minus # )
-	var hash = location.hash.substring(1);
-
-	// Remove the 'active' class from all topnav objects
-	var topnav = document.getElementById("topnav");
-	for ( var x in topnav.childNodes) {
-		var classList = topnav.childNodes[x].classList;
-		if (classList != null) {
-			classList.remove("active");
-		}
-	}
-
-	// Get element for selected hash
-	var selected = document.getElementById("type-" + hash);
-
-	// If no element for type
-	if (selected == null) {
-
-		// Default to AUTO
-		location.hash = "auto";
-		return;
-	}
-
-	// Mark selected item as active
-	selected.setAttribute("class", "active");
-
-	// Regenerate the code
-	genCode();
-}
-
-/**
- * Called each time we should generate a new barcode.
- * 
- * @returns
- */
-function genCode() {
-
-	// The API target
-	var url = location.origin + "/api";
-
-	// Get the requested type
-	var type = location.hash.substring(1);
-
-	// Get the requested text
-	var text = document.getElementById("text").value;
-	if (text == "") {
-
-		text = "Try Me!";
-	}
-
-	// Build URL with type
-	url = url + "/" + type;
-
-	// Build URL with encoded request
-	url += "/" + encodeURIComponent(text);
-
-	// Update download button
-	document.getElementById("barcode_download_button").setAttribute("href", url);
-
-	// Update IMG element source
-	document.getElementById("barcode_output").src = url;
-}
-
-function printCode() {
-
-	var content = document.getElementById("barcode_wrapper").innerHTML;
-
-	w = window.open();
-	w.document.write(content);
-	w.print();
-	w.close();
-}

+ 25 - 0
src/main/resources/js/functions.js

@@ -0,0 +1,25 @@
+/**
+ * BarcodeAPI - functions.js
+ * 
+ */
+
+function go(to) {
+
+	window.location.href = to;
+}
+
+function back() {
+
+	history.go(-1);
+}
+
+function get(theUrl, callback) {
+	
+	var xmlHttp = new XMLHttpRequest();
+	xmlHttp.onreadystatechange = function() {
+		if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
+			callback(xmlHttp.responseText);
+	}
+	xmlHttp.open("GET", theUrl, true);
+	xmlHttp.send(null);
+}

+ 20 - 0
src/main/resources/js/help.js

@@ -0,0 +1,20 @@
+function renderType(codeName, codeType, defValue) {
+
+	var wrapper = document.createElement("div");
+	wrapper.classList.add("type_wrapper");
+	wrapper.onclick = function() {
+		go("index.html#" + codeType);
+	};
+
+	var name = document.createElement("div");
+	name.classList.add("type_name");
+	name.innerHTML = codeName;
+	wrapper.appendChild(name);
+
+	var img = document.createElement("img");
+	img.classList.add("type_barcode");
+	img.src = "api/" + codeType + "/" + defValue;
+	wrapper.appendChild(img);
+
+	document.getElementById("page_details").appendChild(wrapper);
+}

+ 155 - 0
src/main/resources/js/master.js

@@ -0,0 +1,155 @@
+/**
+ * BarcodeAPI - generator.js
+ * 
+ */
+
+// placeholder for user input
+document.codeValue = "Try Me!";
+
+// reload when hash changes
+window.onhashchange = function() {
+
+	// get the requested type
+	var type = location.hash.substring(1);
+
+	if (type == "") {
+		location.hash = "auto";
+		return;
+	}
+
+	// get and update selection
+	var generator = document.getElementById("generator_selector");
+	generator.value = type;
+
+	var option = document.getElementById("code_" + type);
+
+	if (option == null) {
+		location.hash = "auto";
+		return;
+	}
+
+	option.selected;
+	onTypeChanged();
+}
+
+function onTypeChanged() {
+
+	var select = document.getElementById("generator_selector");
+	console.log("Loading Type:" + select.value);
+	var option = select.options[select.selectedIndex];
+	document.getElementById("generator_text").placeholder = option.dataset.code;
+	location.hash = select.value;
+	document.getElementById("generator_text").focus();
+	onInputChanged();
+}
+
+function onBackspace(event) {
+
+	if (event.keyCode == 8) {
+
+		console.log(event);
+		val = document.codeValue;
+		document.codeValue = val.substring(0, val.length - 1);
+		onInputChanged();
+	}
+}
+
+function onKeyEvent(event) {
+
+	if (event.keyCode == 13) {
+		return;
+	}
+
+	document.codeValue += event.key;
+	onInputChanged();
+}
+
+function onInputChanged() {
+
+	var existing = document.codeValue;
+	document.getElementById("generator_text").value = existing;
+	setTimeout(function() {
+
+		// render only if unchanged
+		var current = document.codeValue;
+		if (current != existing) {
+			console.log("Skipping: " + current);
+			return;
+		}
+
+		if (current.endsWith("..b")) {
+
+			back();
+		}
+
+		if (current.endsWith("..f")) {
+
+			go("formats.html");
+		}
+
+		if (current.endsWith("..c")) {
+
+			document.codeValue = "";
+			onInputChanged();
+		}
+
+		renderCode();
+	}, 350)
+}
+
+/**
+ * Called each time we should generate a new barcode.
+ * 
+ * @returns
+ */
+function renderCode() {
+
+	// The API target
+	var url = location.origin + "api";
+
+	// Get the requested type
+	var type = location.hash.substring(1);
+
+	// Get the requested text
+	var text = document.codeValue;
+	if (text == "") {
+
+		var defCode = document.getElementById("code_" + type).dataset.code;
+
+		text = defCode;
+	}
+
+	// Build URL with type
+	url = url + "/" + type;
+
+	// Build URL with encoded request
+	url += "/" + encodeURIComponent(text);
+
+	// Update download button
+	// document.getElementById("barcode_download_button")
+	// .setAttribute("href", url);
+
+	// Update IMG element source
+	console.log("Rendering: " + url);
+	document.getElementById("generator_image").src = url;
+}
+
+function printCode() {
+
+	var content = document.getElementById("barcode_wrapper").innerHTML;
+
+	w = window.open();
+	w.document.write(content);
+	w.print();
+	w.close();
+}
+
+function renderOption(codeName, codeType, defValue) {
+
+	var option = document.createElement("option");
+	option.id = "code_" + codeType;
+	option.innerHTML = codeName;
+	option.value = codeType;
+	option.dataset.code = defValue;
+	document.getElementById("generator_selector").appendChild(option);
+}

+ 1 - 0
src/main/resources/ping

@@ -0,0 +1 @@
+All is well.

+ 3 - 7
src/main/resources/robots.txt

@@ -1,9 +1,5 @@
 User-agent: *
-Allow: /index.html
-Allow: /help.html
-Allow: /index.css
-Allow: /index.js
-Allow: /logo.png
-Allow: /ping
 Allow: /robots.txt
-Allow: /api/
+Allow: /index.html
+Allow: /about.html
+Allow: /formats.html