Move source code to src directory

This commit is contained in:
Robert Marshall 2020-04-13 07:35:48 +01:00
parent 49782b0325
commit c9649000ea
109 changed files with 5 additions and 4 deletions

View file

@ -0,0 +1,55 @@
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Website.Data;
using Website.Models.Auth;
using Website.ViewModels;
namespace Website.Controllers {
public class AccountController:Controller {
private readonly IAuthenticationProvider _authenticationProvider;
public AccountController(IAuthenticationProvider authenticationProvider) => _authenticationProvider = authenticationProvider;
[Authorize]
public IActionResult Index() => View();
[HttpGet]
public IActionResult Login(string returnUrl, bool failedAttempt = false) {
var model = new LoginViewModel {
ReturnUrl = returnUrl,
FailedAttempt = failedAttempt
};
return View(model);
}
[HttpPost]
public async Task<IActionResult> Login(LoginRequest request) {
try {
var user = await _authenticationProvider.Authenticate(request);
return user != null
? await SetIdentityAndRedirect(request.ReturnUrl, user)
: Login(request.ReturnUrl, true);
}
catch {
return Login(request.ReturnUrl, true);
}
}
private async Task<IActionResult> SetIdentityAndRedirect(string returnUrl, User user) {
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Name, user.Username));
identity.AddClaim(new Claim(ClaimTypes.Email, user.Username));
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
return string.IsNullOrEmpty(returnUrl)
? (IActionResult) RedirectToAction(nameof(Index))
: Redirect(returnUrl);
}
}
}

View file

@ -0,0 +1,86 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Website.Data;
using Website.Models.Blog;
using Website.ViewModels.Blog;
namespace Website.Controllers {
public class BlogController:Controller {
private const int MaxPostsPerPage = 10;
private readonly IBlogApi _api;
public BlogController(IBlogApi api) => _api = api;
public async Task<IActionResult> Page(int page) {
var offset = (page - 1) * MaxPostsPerPage;
var posts = (await _api.GetLatestPostsAsync(MaxPostsPerPage, offset)).Select(p => new BlogPostSnippetViewModel(p));
var maxPages = (await _api.GetCountAsync()) / MaxPostsPerPage;
var model = new BlogViewModel(posts, page, maxPages);
return View(model);
}
public async Task<IActionResult> Entry(string url, bool preview = false) {
try {
var post = await _api.GetPostByUrlAsync(url);
if (!preview && string.IsNullOrEmpty(post.Content))
return NotFound();
var model = new BlogPostViewModel(post, preview);
return View(model);
} catch (InvalidOperationException) {
return NotFound();
}
}
[Authorize]
public async Task<IActionResult> Edit(int? id) {
if (!id.HasValue)
return View();
try {
var post = await _api.GetPostByIdAsync(id.Value);
var model = new BlogPostSubmission {
Id = post.Id,
Title = post.Title,
Content = post.Draft
};
return View(model);
} catch (InvalidOperationException) {
return NotFound();
}
}
[Authorize]
[HttpPost]
public async Task<IActionResult> Save(BlogPostSubmission submission) {
var savedPost = await _api.SavePost(submission);
return RedirectToAction(nameof(Edit), new { savedPost.Id });
}
[Authorize]
public async Task<IActionResult> Manage() {
var posts = await _api.GetAllPostsAsync();
var models = posts.OrderByDescending(post => post.Timestamp).Select(post => new BlogPostViewModel(post, false));
return View(models);
}
[Authorize]
public async Task<IActionResult> Publish(int id) {
await _api.PublishPostAsync(id);
return RedirectToAction(nameof(Manage));
}
[Authorize]
public async Task<IActionResult> Delete(int id) {
await _api.DeletePostAsync(id);
return RedirectToAction(nameof(Manage));
}
}
}

View file

@ -0,0 +1,27 @@
using System;
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Website.ViewModels;
namespace Website.Controllers
{
public class ErrorController : Controller
{
public IActionResult BlowUp() {
throw new Exception();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult ServerError()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult PageNotFound()
{
return View();
}
}
}

View file

@ -0,0 +1,32 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Website.Data;
using Website.ViewModels;
using System.Linq;
using Website.ViewModels.Blog;
namespace Website.Controllers {
public class HomeController : Controller {
private readonly IBlogApi _blogApi;
private readonly IGitApi _api;
public HomeController(IBlogApi blogApi, IGitApi api) {
_api = api;
_blogApi = blogApi;
}
public async Task<IActionResult> Index() {
var post = await _blogApi.GetLatestPostAsync();
var repo = (await _api.GetRepositories("rob")).First();
var branch = (await _api.GetBranches("rob", repo.Name)).First();
var commit = await _api.GetCommit("rob", repo.Name, branch.Commit.Id);
var model = new HomeViewModel {
BlogPost = new BlogPostViewModel(post, false),
GitCommit = new GitCommitViewModel(repo, branch, commit)
};
return View(model);
}
}
}

View file

@ -0,0 +1,12 @@
using System;
using System.Net.Http;
namespace Website.Data {
public class ApiCallException : Exception {
private readonly HttpResponseMessage Response;
public ApiCallException(HttpResponseMessage response):base($"Error calling API {response.RequestMessage.RequestUri}: {(int)response.StatusCode}, {response.ReasonPhrase}") {
Response = response;
}
}
}

View file

@ -0,0 +1,48 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.WebUtilities;
using Newtonsoft.Json;
namespace Website.Data {
public abstract class ApiClient {
private readonly HttpClient _client;
protected ApiClient(HttpClient client) {
_client = client;
}
private IDictionary<string, string> ParseQueryParameters(object query) {
var type = query.GetType();
var props = type.GetProperties();
return props.ToDictionary(info => info.Name, info => info.GetValue(query, null).ToString());
}
private async Task<T> Send<T>(HttpMethod method, string url, object query, HttpContent content = null) {
if (query != null)
url = QueryHelpers.AddQueryString(url, ParseQueryParameters(query));
using var httpRequest = new HttpRequestMessage(method, url) { Content = content };
var response = await _client.SendAsync(httpRequest);
if (!response.IsSuccessStatusCode)
throw new ApiCallException(response);
var json = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(json);
}
protected async Task<T> Post<T>(string url, object value, object query = null) {
var json = JsonConvert.SerializeObject(value);
using var requestBody = new StringContent(json, Encoding.UTF8, "application/json");
return await Send<T>(HttpMethod.Post, url, query, requestBody);
}
protected async Task<T> Get<T>(string url, object query = null) {
return await Send<T>(HttpMethod.Get, url, query);
}
}
}

View file

@ -0,0 +1,19 @@
using System.Net.Http;
using System.Threading.Tasks;
using Website.Models.Auth;
namespace Website.Data {
public class AuthenticationProvider:ApiClient, IAuthenticationProvider {
public AuthenticationProvider(HttpClient client) : base(client) {
}
public async Task<User> Authenticate(LoginRequest request) {
try {
return await Post<User>("authenticate", request);
}
catch (ApiCallException) {
return null;
}
}
}
}

View file

@ -0,0 +1,22 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Net.Http;
using Website.Models.Blog;
namespace Website.Data
{
public class BlogApi : ApiClient, IBlogApi {
public BlogApi(HttpClient client) : base(client) {
}
public async Task<BlogPost> GetPostByUrlAsync(string url) => await Get<BlogPost>("get/" + url);
public async Task<IEnumerable<BlogPost>> GetLatestPostsAsync(int count = 0, int offset = 0) => await Get<IEnumerable<BlogPost>>("getlatestposts", new{count, offset});
public async Task<BlogPost> GetLatestPostAsync() => await Get<BlogPost>("getlatestpost");
public async Task<int> GetCountAsync() => await Get<int>("getcount");
public async Task<BlogPost> GetPostByIdAsync(int id) => await Get<BlogPost>("get/" + id);
public async Task<BlogPost> SavePost(BlogPostSubmission post) => await Post<BlogPost>("savepost", post);
public async Task<IEnumerable<BlogPost>> GetAllPostsAsync() => await Get<IEnumerable<BlogPost>>("getallposts");
public async Task DeletePostAsync(int id) => await Post<object>("deletepost", id);
public async Task PublishPostAsync(int id) => await Post<object>("publishpost", id);
}
}

View file

@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Website.Models.Git;
namespace Website.Data {
public class GitApi : ApiClient, IGitApi {
public GitApi(HttpClient client) : base(client) {
}
public async Task<IEnumerable<Repository>> GetRepositories(string user) => await Get<IEnumerable<Repository>>("repositories", new { user });
public async Task<IEnumerable<Branch>> GetBranches(string user, string repository) => await Get<IEnumerable<Branch>>("branches", new {user, repository});
public async Task<Commit> GetCommit(string user, string repository, string hash) => await Get<Commit>("commit", new {user, repository, hash});
}
}

View file

@ -0,0 +1,8 @@
using System.Threading.Tasks;
using Website.Models.Auth;
namespace Website.Data {
public interface IAuthenticationProvider {
Task<User> Authenticate(LoginRequest request);
}
}

View file

@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Website.Models.Blog;
namespace Website.Data {
public interface IBlogApi {
Task<BlogPost> GetPostByUrlAsync(string url);
Task<IEnumerable<BlogPost>> GetLatestPostsAsync(int count = 0, int offset = 0);
Task<BlogPost> GetLatestPostAsync();
Task<int> GetCountAsync();
Task<BlogPost> GetPostByIdAsync(int id);
Task<BlogPost> SavePost(BlogPostSubmission post);
Task<IEnumerable<BlogPost>> GetAllPostsAsync();
Task DeletePostAsync(int id);
Task PublishPostAsync(int id);
}
}

View file

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Website.Models.Git;
namespace Website.Data {
public interface IGitApi {
Task<IEnumerable<Repository>> GetRepositories(string user);
Task<IEnumerable<Branch>> GetBranches(string user, string repository);
Task<Commit> GetCommit(string user, string repository, string hash);
}
}

View file

@ -0,0 +1,25 @@
using System;
namespace Website.Extensions {
public static class DateTimeExtensions {
private static string GetDaySuffix(int day) {
switch (day) {
case 1:
case 21:
case 31:
return @"\s\t";
case 2:
case 22:
return @"\n\d";
case 3:
case 23:
return @"\r\d";
default:
return @"\t\h";
}
}
public static string GetDaySuffix(this DateTime dateTime) => GetDaySuffix(dateTime.Day);
public static string GetDaySuffix(this DateTimeOffset dateTimeOffset) => GetDaySuffix(dateTimeOffset.Day);
}
}

View file

@ -0,0 +1,7 @@
namespace Website.Models.Auth {
public class LoginRequest {
public string Username { get; set; }
public string Password { get; set; }
public string ReturnUrl { get; set; }
}
}

View file

@ -0,0 +1,6 @@
namespace Website.Models.Auth {
public class User {
public string Username { get; set; }
public string Password { get; set; }
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace Website.Models.Blog
{
public class BlogPost
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime Timestamp { get; set; }
public string Draft { get; set; }
public string Url { get; set; }
public int UserId { get; set; }
}
}

View file

@ -0,0 +1,7 @@
namespace Website.Models.Blog {
public class BlogPostSubmission {
public int? Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
}

View file

@ -0,0 +1,6 @@
namespace Website.Models.Git {
public class Branch {
public string Name { get; set; }
public Commit Commit { get; set; }
}
}

View file

@ -0,0 +1,10 @@
using System;
namespace Website.Models.Git {
public class Commit {
public string Id { get; set; }
public string Message { get; set; }
public DateTimeOffset Timestamp { get; set; }
public string Url { get; set; }
}
}

View file

@ -0,0 +1,6 @@
namespace Website.Models.Git {
public class Repository {
public string Name { get; set; }
public string Url { get; set; }
}
}

17
src/Website/Program.cs Normal file
View file

@ -0,0 +1,17 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace Website
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}

View file

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:12894",
"sslPort": 44394
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Website": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

86
src/Website/Startup.cs Normal file
View file

@ -0,0 +1,86 @@
using System;
using System.Net.Http;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Website.Data;
namespace Website
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddSingleton(Configuration);
services.AddHttpClient<IGitApi, GitApi>(client => client.BaseAddress = new Uri(Configuration["gitApiEndpoint"]))
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler {ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true});
services.AddHttpClient<IBlogApi, BlogApi>(client => client.BaseAddress = new Uri(Configuration["blogApiEndpoint"]))
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler {ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true});
services.AddHttpClient<IAuthenticationProvider, AuthenticationProvider>(client => client.BaseAddress = new Uri(Configuration["authApiEndpoint"]))
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler {ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
services.AddMvc(options => options.EnableEndpointRouting = false)
#if DEBUG
.AddRazorRuntimeCompilation()
#endif
;
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error/ServerError");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/Error/PageNotFound")
.UseHttpsRedirection()
.UseStaticFiles()
.UseAuthentication()
.UseMvc(routes => {
routes.MapRoute(
name: "blogPages",
template: "blog/{action}/{page:int}",
defaults: new {controller = "Blog", action = "Page", page = 1});
routes.MapRoute(
name: "blogView",
template: "blog/view/{url}",
defaults: new {controller = "Blog", action = "Entry"});
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}

View file

@ -0,0 +1,18 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Website.Data;
namespace Website.ViewComponents {
public class BlogNavigationViewComponent:ViewComponent {
private readonly IBlogApi _repo;
public BlogNavigationViewComponent(IBlogApi repo) {
_repo = repo;
}
public async Task<IViewComponentResult> InvokeAsync() {
var posts = await _repo.GetLatestPostsAsync(5);
return View(posts);
}
}
}

View file

@ -0,0 +1,19 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Website.Data;
namespace Website.ViewComponents {
public class ProjectNavigationViewComponent :ViewComponent {
private readonly IGitApi _api;
public ProjectNavigationViewComponent(IGitApi api) {
_api = api;
}
public async Task<IViewComponentResult> InvokeAsync() {
var repositories = await _api.GetRepositories("rob");
return View(repositories.Take(5));
}
}
}

View file

@ -0,0 +1,9 @@
using Website.Models.Blog;
namespace Website.ViewModels.Blog
{
public class BlogPostSnippetViewModel:BlogPostViewModel
{
public BlogPostSnippetViewModel(BlogPost blogPost) : base(blogPost, false) => Content = Content.Length < 1000 ? Content : Content.Substring(0, 1000);
}
}

View file

@ -0,0 +1,37 @@
using System;
using Markdig;
using Pek.Markdig.HighlightJs;
using Website.Extensions;
using Website.Markdig.Extensions;
using Website.Models.Blog;
namespace Website.ViewModels.Blog
{
public class BlogPostViewModel
{
public BlogPostViewModel(BlogPost blogPost, bool preview) {
Id = blogPost.Id;
Title = blogPost.Title;
Timestamp = FormatTimestamp(blogPost.Timestamp);
Url = blogPost.Url;
Content = preview ? blogPost.Draft : blogPost.Content;
}
private static string FormatTimestamp(DateTime timestamp) => timestamp.ToString($@"dddd \t\h\e d{timestamp.GetDaySuffix()} \o\f MMMM yyyy");
private static MarkdownPipeline GetPipeline() => new MarkdownPipelineBuilder()
//.UseAdvancedExtensions()
.UseAutoLinks()
.UseSoftlineBreakAsHardlineBreak()
.UseHighlightJs()
.UseBlogRenderer()
.Build();
public int Id { get; }
public string Title { get; }
public string Content { get; protected set; }
public string Timestamp { get; }
public string Url { get; }
public string ContentHtml => Markdown.ToHtml(Content, GetPipeline()).Trim();
}
}

View file

@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace Website.ViewModels.Blog
{
public class BlogViewModel
{
public BlogViewModel(IEnumerable<BlogPostViewModel> posts, int page, int maxPages)
{
Posts = posts;
Page = page;
MaxPages = maxPages;
}
public IEnumerable<BlogPostViewModel> Posts { get; }
public int Page { get; }
public int MaxPages { get; }
}
}

View file

@ -0,0 +1,9 @@
namespace Website.ViewModels
{
public class ErrorViewModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}

View file

@ -0,0 +1,27 @@
using System;
using Website.Extensions;
using Website.Models.Git;
namespace Website.ViewModels {
public class GitCommitViewModel {
public GitCommitViewModel(Repository repo, Branch branch, Commit commit) {
RepositoryName = repo.Name;
RepositoryUrl = repo.Url;
BranchName = branch.Name;
Hash = commit.Id;
CommitUrl = commit.Url;
CommitMessage = commit.Message;
Timestamp = FormatTimestamp(commit.Timestamp);
}
private static string FormatTimestamp(DateTimeOffset timestamp) => timestamp.ToString($@"HH:mm:ss on dddd \t\h\e d{timestamp.GetDaySuffix()} \o\f MMMM yyyy");
public string RepositoryName { get; }
public string RepositoryUrl { get; }
public string BranchName { get; }
public string Hash { get; }
public string CommitUrl { get; }
public string CommitMessage { get; }
public string Timestamp { get; }
}
}

View file

@ -0,0 +1,8 @@
using Website.ViewModels.Blog;
namespace Website.ViewModels {
public class HomeViewModel {
public BlogPostViewModel BlogPost { get; set; }
public GitCommitViewModel GitCommit { get; internal set; }
}
}

View file

@ -0,0 +1,6 @@
namespace Website.ViewModels {
public class LoginViewModel {
public string ReturnUrl { get; set; }
public bool FailedAttempt { get; set; }
}
}

View file

@ -0,0 +1,8 @@
@model object
@{
ViewBag.Title = "title";
Layout = "_Layout";
}
<h2>title</h2>

View file

@ -0,0 +1,40 @@
@model LoginViewModel
@{
ViewBag.Title = "Login";
}
@if (!string.IsNullOrEmpty(Model.ReturnUrl)) {
<div>Please log in to perform that action</div>
}
@if (Model.FailedAttempt) {
<div>Could not log in with those credentials</div>
}
<form method="post" action="login">
<input type="hidden" name="returnUrl" value="@Model.ReturnUrl" />
<table>
<tr class="form-row">
<td>
<label for="username">Username: </label>
</td>
<td>
<input name="username" id="username" />
</td>
</tr>
<tr class="form-row">
<td>
<label for="password">Password: </label>
</td>
<td>
<input name="password" type="password" id="password" />
</td>
</tr>
<tr class="form-row">
<td colspan="2">
<button type="submit">Login</button>
</td>
</tr>
</table>
</form>

View file

@ -0,0 +1,21 @@
@model Website.Models.Blog.BlogPostSubmission
@{
ViewBag.Title = Model != null ? "Edit Post" : "New Post";
}
<form method="post" action="/blog/save" class="edit-form">
<input type="hidden" name="id" value="@Model?.Id"/>
<div class="edit-form__title form-row">
<label for="postTitle" class="edit-form__title__label">Title: </label>
<input name="title" id="postTitle" value="@Model?.Title" class="edit-form__title__input"/>
</div>
<textarea id="postContent" name="content">@Model?.Content</textarea>
<button type="submit">Save</button>
</form>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
<script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
<script>
var simpleMde = new SimpleMDE({ element: document.getElementById("postContent"), tabSize: 4 });
</script>

View file

@ -0,0 +1,11 @@
@model Website.ViewModels.Blog.BlogPostViewModel
@{
ViewData["Title"] = Model.Title;
}
<article class="blog-post">
@Html.Raw(Model.ContentHtml)
</article>
<small>Posted on @Model.Timestamp</small>

View file

@ -0,0 +1,19 @@
@model IEnumerable<Website.ViewModels.Blog.BlogPostViewModel>
@{
ViewBag.Title = "Manage posts";
}
<a href="/blog/edit">New Post</a>
<table class="post-list">
@foreach (var post in Model) {
<tr class="post-list__post">
<td><a href="/blog/view/@post.Url">@post.Title</a></td>
<td><a href="edit?id=@post.Id"><img src="/images/edit.svg" alt="Edit" title="Edit"/></a></td>
<td><a href="/blog/view/@post.Url?preview=true"><img src="/images/blog.svg" alt="Preview" title="Preview"/></a></td>
<td><a href="publish?id=@post.Id"><img src="/images/send.svg" alt="Publish" title="Publish" onclick="return confirm('Are you sure?')"/></a></td>
<td><a href="delete?id=@post.Id"><img src="/images/delete.svg" alt="Delete" title="Delete" onclick="return confirm('Are you sure?')"/></a></td>
</tr>
}
</table>

View file

@ -0,0 +1,23 @@
@model Website.ViewModels.Blog.BlogViewModel
@{
ViewData["Title"] = "Blog";
}
@foreach (var post in Model.Posts) {
<article class="blog-post">
<h2><a href="/blog/view/@post.Url">@post.Title</a></h2>
@Html.Raw(post.ContentHtml)
<small>Posted on @post.Timestamp</small>
<p><a href="/blog/view/@post.Url">Read more...</a></p>
</article>
}
<div class="blog-navigation">
@if (Model.Page > 1) {
<a href="/blog/page/@(Model.Page - 1)" class="round-button blog-navigation__previous"><img src="/images/previous.svg" alt="Previous"/></a>
}
@if (Model.Page < Model.MaxPages) {
<a href="/blog/page/@(Model.Page + 1)" class="round-button blog-navigation__next"><img src="/images/next.svg" alt="Next"/></a>
}
</div>

View file

@ -0,0 +1,7 @@
@model ErrorViewModel
@{
ViewData["Title"] = "404 Not Found";
}
<h1>404 Not Found - page unavailable.</h1>
This page cannot be found.

View file

@ -0,0 +1,25 @@
@model ErrorViewModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>

View file

@ -0,0 +1,23 @@
@model HomeViewModel
@{
ViewData["Title"] = "Welcome";
}
<p>Hello, I'm Rob. I'm a senior software engineer at <a href="https://www.codecomputerlove.com/" target="_blank">Code Computerlove</a> where I focus on back end development primarily using C#. In my spare time I spend my time riding bikes or making stuff, typically involving an Arduino.</p>
<p>This website is primarily an outlet for me to write about things which have been technically challenging, either in a professional or personal capacity, though not limited to that.</p>
<p>If you wish to get in contact, then get in touch via my <a href="http://uk.linkedin.com/in/robertmarshall/" target="_blank"><img alt="LinkedIn profile" src="https://static.licdn.com/scds/common/u/img/webpromo/btn_liprofile_blue_80x15.png" title=""></a>.</p>
<h2>Latest code commit</h2>
<table class="commit-table">
<tr><td>Repository</td><td><a href="@Model.GitCommit.RepositoryUrl">@Model.GitCommit.RepositoryName</a></td></tr>
<tr><td>Branch</td><td>@Model.GitCommit.BranchName</td></tr>
<tr><td>SHA</td><td><a href="@Model.GitCommit.CommitUrl">@Model.GitCommit.Hash</a></td></tr>
<tr><td>Message</td><td>@Model.GitCommit.CommitMessage</td></tr>
<tr><td>Timestamp</td><td>@Model.GitCommit.Timestamp</td></tr>
</table>
<h2>Latest Blog Post</h2>
<h3><a href="/blog/view/@Model.BlogPost.Url">@Model.BlogPost.Title</a></h3>
<partial name="~/Views/Blog/Entry.cshtml" , model="Model.BlogPost" />
<p><a href="/blog">View more</a></p>

View file

@ -0,0 +1,13 @@
@model System.Collections.Generic.IEnumerable<Website.Models.Blog.BlogPost>
<h5>Blog:</h5>
<ul class="nav-list">
@foreach (var post in Model) {
<li class="nav-list__link"><a href="/blog/view/@post.Url">@post.Title</a></li>
}
<li class="nav-list__link"><a href="/blog">View All</a></li>
@if (User.Identity.IsAuthenticated) {
<li class="nav-list__link"><a href="/blog/manage">Manage</a></li>
}
</ul>

View file

@ -0,0 +1,9 @@
@model IEnumerable<Website.Models.Git.Repository>
<h5>Projects:</h5>
<ul class="nav-list">
@foreach (var repository in Model) {
<li class="nav-list__link"><a href="@repository.Url" target="_blank">@repository.Name</a></li>
}
<li class="nav-list__link"><a href="//git.robware.uk" target="_blank">View All</a></li>
</ul>

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] | Robware</title>
<link href="/css/style.css" rel="stylesheet" defer/>
<script type="text/javascript" src="/js/javascript.min.js"></script>
</head>
<body>
<header class="header">
<a class="header__blurb" href="/">
<span class="header__blurb__title">Robware</span>
<small class="header__blurb__tagline">Software by Rob</small>
</a>
<div class="header__headshot"></div>
</header>
<div id="content">
<main>
<h1>@ViewData["Title"]</h1>
@RenderBody()
</main>
<nav>
<label for="menu-toggle"><img class="nav-open" src="/images/menu.svg" /></label>
<input type="checkbox" id="menu-toggle" />
<div class="nav-container">
@await Component.InvokeAsync("BlogNavigation")
@await Component.InvokeAsync("ProjectNavigation")
</div>
<div class="strava">
<iframe height='160' width='250' frameborder='0' allowtransparency='true' scrolling='no' src='https://www.strava.com/athletes/8892667/activity-summary/df1a6e21a9947dbe7a9dd36f86b1c17185002d22'></iframe>
<iframe height='454' width='250' frameborder='0' allowtransparency='true' scrolling='no' src='https://www.strava.com/athletes/8892667/latest-rides/df1a6e21a9947dbe7a9dd36f86b1c17185002d22'></iframe>
</div>
</nav>
</div>
<footer>
<div class="logo" onclick="window.location='/account'"></div>
</footer>
</body>
</html>

View file

@ -0,0 +1,18 @@
<environment include="Development">
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</environment>
<environment exclude="Development">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"
asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator"
crossorigin="anonymous"
integrity="sha256-F6h55Qw6sweK+t7SiOJX+2bpSAa3b/fnlrVCJvmEj1A=">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
crossorigin="anonymous"
integrity="sha256-9GycpJnliUjJDVDqP0UEu/bsm9U+3dnQUH8+3W10vkY=">
</script>
</environment>

View file

@ -0,0 +1,3 @@
@using Website
@using Website.ViewModels
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View file

@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}

View file

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BuildBundlerMinifier" Version="3.2.435" />
<PackageReference Include="markdig" Version="0.18.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Pek.Markdig.HighlightJs" Version="0.5.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Website.Markdig.Extensions\Website.Markdig.Extensions.csproj" />
</ItemGroup>
<Target Name="BuildCss" BeforeTargets="Build">
<Exec WorkingDirectory="$(ProjectDir)" Command="npm install" />
<Exec WorkingDirectory="$(ProjectDir)" Command="npx gulp compile_scss" />
</Target>
</Project>

View file

@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"blogApiEndpoint": "",
"gitApiEndpoint": "",
"authApiEndpoint": ""
}

View file

@ -0,0 +1,18 @@
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"blogApiEndpoint": "<BlogEndpoint>",
"gitApiEndpoint": "<GitEndpoint>",
"authApiEndpoint": "<AuthEndpoint>",
"AllowedHosts": "*",
"Kestrel": {
"EndPoints": {
"Http": {
"Url": "http://0.0.0.0:5000"
}
}
}
}

View file

@ -0,0 +1,13 @@
[
{
"outputFileName": "wwwroot/js/javascript.min.js",
"inputFiles": [
"wwwroot/js/javascript.js"
],
"minify": {
"enabled": true,
"renameLocals": true
},
"sourceMap": false
}
]

14
src/Website/gulpfile.js Normal file
View file

@ -0,0 +1,14 @@
var gulp = require("gulp"),
sass = require("gulp-sass"),
cleanCss = require("gulp-clean-css");
gulp.task("compile_scss", function () {
return gulp.src('wwwroot/css/**/*.scss')
.pipe(sass())
.pipe(cleanCss())
.pipe(gulp.dest('wwwroot/css'));
});
gulp.task("watch", function () {
gulp.watch("wwwroot/css/**/*.scss", gulp.series("compile_scss"));
});

4886
src/Website/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

21
src/Website/package.json Normal file
View file

@ -0,0 +1,21 @@
{
"name": "website",
"version": "1.0.0",
"description": "",
"main": "index.js",
"devDependencies": {
"gulp": "^4.0.0",
"gulp-clean-css": "^4.0.0",
"gulp-less": "^4.0.1",
"gulp-sass": "^4.0.2"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.12.1"
},
"scripts": {
"gulp": "gulp"
},
"author": "Robert Marshall",
"license": "ISC",
"private": true
}

View file

@ -0,0 +1,157 @@
@import "colours.scss";
.blog-post {
&__image-container {
max-width: 100%;
padding: 10px;
background: $Grey-200;
display: inline-block;
margin: auto;
text-align: center;
border-radius: 4px;
&--container {
text-align: center;
}
&--image {
max-width: 100%;
}
&--tagline {
margin-top: 10px;
display: block;
}
}
:not(pre)>code {
padding: 2px;
background: $Grey-200;
border-radius: 2px;
border: 1px solid $Grey-400;
}
pre>code {
border-left: 4px solid $primary;
margin-bottom: 10px;
overflow: auto;
padding: 6px;
padding-left: 10px;
tab-size: 4;
-moz-tab-size: 4;
/**
* Obsidian style
* ported by Alexander Marenin (http://github.com/ioncreature)
*/
display: block;
overflow-x: auto;
background: #282b2e;
color: #e0e2e4;
.hljs-keyword,
.hljs-selector-tag,
.hljs-literal,
.hljs-selector-id {
color: #93c763;
}
.hljs-number {
color: #ffcd22;
}
.hljs-attribute {
color: #668bb0;
}
.hljs-code,
.hljs-class .hljs-title,
.hljs-section {
color: white;
}
.hljs-regexp,
.hljs-link {
color: #d39745;
}
.hljs-meta {
color: #557182;
}
.hljs-tag,
.hljs-name,
.hljs-bullet,
.hljs-subst,
.hljs-emphasis,
.hljs-type,
.hljs-built_in,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-addition,
.hljs-variable,
.hljs-template-tag,
.hljs-template-variable {
color: #8cbbad;
}
.hljs-string,
.hljs-symbol {
color: #ec7600;
}
.hljs-comment,
.hljs-quote,
.hljs-deletion {
color: #818e96;
}
.hljs-selector-class {
color: #A082BD
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-literal,
.hljs-doctag,
.hljs-title,
.hljs-section,
.hljs-type,
.hljs-name,
.hljs-strong {
font-weight: bold;
}
}
}
.blog-navigation {
display: flex;
justify-content: space-between;
&__next {
margin-left: auto;
}
}
.edit-form {
&__title {
display: flex;
&__label {
flex: 0 0 auto;
margin-right: 5px;
}
&__input {
flex: 1 1 auto;
}
}
}
.post-list {
border-collapse: collapse;
&__post:hover {
background: $Grey-300;
}
}

View file

@ -0,0 +1,23 @@
@import "material_colours.scss";
$background:$Grey-50;
$primary-100:$Red-100;
$primary-200:$Red-200;
$primary-300:$Red-300;
$primary-400:$Red-400;
$primary-500:$Red-500;
$primary-600:$Red-600;
$primary-700:$Red-700;
$primary-800:$Red-800;
$primary-900:$Red-900;
$accent-100:$Teal-100;
$accent-200:$Teal-200;
$accent-400:$Teal-400;
$accent-700:$Teal-700;
$primary: #e32c28; //$primary-600;
$accent:$accent-400;
$control:$Grey-200;
$control2:$Grey-300;

View file

@ -0,0 +1,5 @@
.commit-table {
td:first-child(){
font-weight: bold;
}
}

View file

@ -0,0 +1,272 @@
$Red-50:#ffebee;
$Red-100:#ffcdd2;
$Red-200:#ef9a9a;
$Red-300:#e57373;
$Red-400:#ef5350;
$Red-500:#f44336;
$Red-600:#e53935;
$Red-700:#d32f2f;
$Red-800:#c62828;
$Red-900:#b71c1c;
$Red-A100:#ff8a80;
$Red-A200:#ff5252;
$Red-A400:#ff1744;
$Red-A700:#d50000;
$Pink-50:#fce4ec;
$Pink-100:#f8bbd0;
$Pink-200:#f48fb1;
$Pink-300:#f06292;
$Pink-400:#ec407a;
$Pink-500:#e91e63;
$Pink-600:#d81b60;
$Pink-700:#c2185b;
$Pink-800:#ad1457;
$Pink-900:#880e4f;
$Pink-A100:#ff80ab;
$Pink-A200:#ff4081;
$Pink-A400:#f50057;
$Pink-A700:#c51162;
$Purple-50:#f3e5f5;
$Purple-100:#e1bee7;
$Purple-200:#ce93d8;
$Purple-300:#ba68c8;
$Purple-400:#ab47bc;
$Purple-500:#9c27b0;
$Purple-600:#8e24aa;
$Purple-700:#7b1fa2;
$Purple-800:#6a1b9a;
$Purple-900:#4a148c;
$Purple-A100:#ea80fc;
$Purple-A200:#e040fb;
$Purple-A400:#d500f9;
$Purple-A700:#aa00ff;
$DeepPurple-50:#ede7f6;
$DeepPurple-50100:#d1c4e9;
$DeepPurple-50200:#b39ddb;
$DeepPurple-50300:#9575cd;
$DeepPurple-50400:#7e57c2;
$DeepPurple-50500:#673ab7;
$DeepPurple-50600:#5e35b1;
$DeepPurple-50700:#512da8;
$DeepPurple-50800:#4527a0;
$DeepPurple-50900:#311b92;
$DeepPurple-50A100:#b388ff;
$DeepPurple-50A200:#7c4dff;
$DeepPurple-50A400:#651fff;
$DeepPurple-50A700:#6200ea;
$Indigo-50:#e8eaf6;
$Indigo-100:#c5cae9;
$Indigo-200:#9fa8da;
$Indigo-300:#7986cb;
$Indigo-400:#5c6bc0;
$Indigo-500:#3f51b5;
$Indigo-600:#3949ab;
$Indigo-700:#303f9f;
$Indigo-800:#283593;
$Indigo-900:#1a237e;
$Indigo-A100:#8c9eff;
$Indigo-A200:#536dfe;
$Indigo-A400:#3d5afe;
$Indigo-A700:#304ffe;
$Blue-50:#e3f2fd;
$Blue-100:#bbdefb;
$Blue-200:#90caf9;
$Blue-300:#64b5f6;
$Blue-400:#42a5f5;
$Blue-500:#2196f3;
$Blue-600:#1e88e5;
$Blue-700:#1976d2;
$Blue-800:#1565c0;
$Blue-900:#0d47a1;
$Blue-A100:#82b1ff;
$Blue-A200:#448aff;
$Blue-A400:#2979ff;
$Blue-A700:#2962ff;
$LightBlue-50:#e1f5fe;
$LightBlue-100:#b3e5fc;
$LightBlue-200:#81d4fa;
$LightBlue-300:#4fc3f7;
$LightBlue-400:#29b6f6;
$LightBlue-500:#03a9f4;
$LightBlue-600:#039be5;
$LightBlue-700:#0288d1;
$LightBlue-800:#0277bd;
$LightBlue-900:#01579b;
$LightBlue-A100:#80d8ff;
$LightBlue-A200:#40c4ff;
$LightBlue-A400:#00b0ff;
$LightBlue-A700:#0091ea;
$Cyan-50:#e0f7fa;
$Cyan-100:#b2ebf2;
$Cyan-200:#80deea;
$Cyan-300:#4dd0e1;
$Cyan-400:#26c6da;
$Cyan-500:#00bcd4;
$Cyan-600:#00acc1;
$Cyan-700:#0097a7;
$Cyan-800:#00838f;
$Cyan-900:#006064;
$Cyan-A100:#84ffff;
$Cyan-A200:#18ffff;
$Cyan-A400:#00e5ff;
$Cyan-A700:#00b8d4;
$Teal-50:#e0f2f1;
$Teal-100:#b2dfdb;
$Teal-200:#80cbc4;
$Teal-300:#4db6ac;
$Teal-400:#26a69a;
$Teal-500:#009688;
$Teal-600:#00897b;
$Teal-700:#00796b;
$Teal-800:#00695c;
$Teal-900:#004d40;
$Teal-A100:#a7ffeb;
$Teal-A200:#64ffda;
$Teal-A400:#1de9b6;
$Teal-A700:#00bfa5;
$Green-50:#e8f5e9;
$Green-100:#c8e6c9;
$Green-200:#a5d6a7;
$Green-300:#81c784;
$Green-400:#66bb6a;
$Green-500:#4caf50;
$Green-600:#43a047;
$Green-700:#388e3c;
$Green-800:#2e7d32;
$Green-900:#1b5e20;
$Green-A100:#b9f6ca;
$Green-A200:#69f0ae;
$Green-A400:#00e676;
$Green-A700:#00c853;
$LightGreen-50:#f1f8e9;
$LightGreen-100:#dcedc8;
$LightGreen-200:#c5e1a5;
$LightGreen-300:#aed581;
$LightGreen-400:#9ccc65;
$LightGreen-500:#8bc34a;
$LightGreen-600:#7cb342;
$LightGreen-700:#689f38;
$LightGreen-800:#558b2f;
$LightGreen-900:#33691e;
$LightGreen-A100:#ccff90;
$LightGreen-A200:#b2ff59;
$LightGreen-A400:#76ff03;
$LightGreen-A700:#64dd17;
$Lime-50:#f9fbe7;
$Lime-100:#f0f4c3;
$Lime-200:#e6ee9c;
$Lime-300:#dce775;
$Lime-400:#d4e157;
$Lime-500:#cddc39;
$Lime-600:#c0ca33;
$Lime-700:#afb42b;
$Lime-800:#9e9d24;
$Lime-900:#827717;
$Lime-A100:#f4ff81;
$Lime-A200:#eeff41;
$Lime-A400:#c6ff00;
$Lime-A700:#aeea00;
$Yellow-50:#fffde7;
$Yellow-100:#fff9c4;
$Yellow-200:#fff59d;
$Yellow-300:#fff176;
$Yellow-400:#ffee58;
$Yellow-500:#ffeb3b;
$Yellow-600:#fdd835;
$Yellow-700:#fbc02d;
$Yellow-800:#f9a825;
$Yellow-900:#f57f17;
$Yellow-A100:#ffff8d;
$Yellow-A200:#ffff00;
$Yellow-A400:#ffea00;
$Yellow-A700:#ffd600;
$Amber-50:#fff8e1;
$Amber-100:#ffecb3;
$Amber-200:#ffe082;
$Amber-300:#ffd54f;
$Amber-400:#ffca28;
$Amber-500:#ffc107;
$Amber-600:#ffb300;
$Amber-700:#ffa000;
$Amber-800:#ff8f00;
$Amber-900:#ff6f00;
$Amber-A100:#ffe57f;
$Amber-A200:#ffd740;
$Amber-A400:#ffc400;
$Amber-A700:#ffab00;
$Orange-50:#fff3e0;
$Orange-100:#ffe0b2;
$Orange-200:#ffcc80;
$Orange-300:#ffb74d;
$Orange-400:#ffa726;
$Orange-500:#ff9800;
$Orange-600:#fb8c00;
$Orange-700:#f57c00;
$Orange-800:#ef6c00;
$Orange-900:#e65100;
$Orange-A100:#ffd180;
$Orange-A200:#ffab40;
$Orange-A400:#ff9100;
$Orange-A700:#ff6d00;
$DeepOrange-50:#fbe9e7;
$DeepOrange-100:#ffccbc;
$DeepOrange-200:#ffab91;
$DeepOrange-300:#ff8a65;
$DeepOrange-400:#ff7043;
$DeepOrange-500:#ff5722;
$DeepOrange-600:#f4511e;
$DeepOrange-700:#e64a19;
$DeepOrange-800:#d84315;
$DeepOrange-900:#bf360c;
$DeepOrange-A100:#ff9e80;
$DeepOrange-A200:#ff6e40;
$DeepOrange-A400:#ff3d00;
$DeepOrange-A700:#dd2c00;
$Brown-50:#efebe9;
$Brown-100:#d7ccc8;
$Brown-200:#bcaaa4;
$Brown-300:#a1887f;
$Brown-400:#8d6e63;
$Brown-500:#795548;
$Brown-600:#6d4c41;
$Brown-700:#5d4037;
$Brown-800:#4e342e;
$Brown-900:#3e2723;
$Grey-50:#fafafa;
$Grey-100:#f5f5f5;
$Grey-200:#eeeeee;
$Grey-300:#e0e0e0;
$Grey-400:#bdbdbd;
$Grey-500:#9e9e9e;
$Grey-600:#757575;
$Grey-700:#616161;
$Grey-800:#424242;
$Grey-900:#212121;
$BlueGrey-50:#eceff1;
$BlueGrey-100:#cfd8dc;
$BlueGrey-200:#b0bec5;
$BlueGrey-300:#90a4ae;
$BlueGrey-400:#78909c;
$BlueGrey-500:#607d8b;
$BlueGrey-600:#546e7a;
$BlueGrey-700:#455a64;
$BlueGrey-800:#37474f;
$BlueGrey-900:#263238;

View file

@ -0,0 +1,277 @@
@import url('https://fonts.googleapis.com/css?family=Public+Sans&display=swap');
@import "colours.scss";
$fa-font-path: "../webfonts";
@import "../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss";
@import "../../node_modules/@fortawesome/fontawesome-free/scss/solid.scss";
* {
box-sizing: border-box;
outline: none;
transition: all 0.2s ease;
}
html {
width: 100%;
height: 100%;
background: url(/images/background.png);
}
html,
body {
padding: 0;
margin: 0;
font-family: 'Public Sans', sans-serif;
color: #333;
}
::selection {
background: $primary;
color: white
}
body {
max-width: 1280px;
min-height: 100%;
margin: auto;
background: white;
display: flex;
flex-direction: column;
}
a {
color: $primary;
position: relative;
text-decoration: none;
background: linear-gradient(to bottom, $primary 0%, $primary 0%);
background-position: 0 100%;
background-size: 0px 0px;
background-repeat: repeat-y;
transition: all 0.3s ease-in-out 0s;
&:hover {
color: white;
background-size: 100% 100%;
}
&[href^="http"] {
&:after {
@extend %fa-icon;
@extend .fas;
content: fa-content($fa-var-external-link-alt);
margin-left: 5px;
}
}
}
h1,
h2,
h3,
h4,
h5,
h6 {
&:first-child {
margin-top: 0;
}
a {
color: inherit;
text-decoration: underline;
&:hover {
text-decoration: none;
}
}
}
.header {
padding: 40px 30px;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
background-color: black;
overflow: hidden;
&:before {
content: "";
position: absolute;
background: url(/images/header.jpg) no-repeat;
background-size: cover;
background-position: center;
top: -2px;
bottom: -2px;
left: -2px;
right: -2px;
filter: blur(2px);
}
&__blurb {
background-color: rgba(white, 0.8);
color: black;
padding: 30px;
width: 50%;
z-index: 0;
display: block;
&__title {
font-size: 32px;
text-decoration: none;
font-weight: bold;
}
&__tagline {
display: block;
}
}
&__headshot {
flex: 0 0 auto;
background: url(/images/headshot.jpg) center no-repeat;
background-size: cover;
width: 200px;
height: 200px;
border: 5px solid white;
border-radius: 50%;
box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 1);
z-index: 0;
}
}
#content {
display: flex;
width: 100%;
main {
flex: 1 1 auto;
padding: 30px;
overflow: hidden;
}
nav {
flex: 0 0 250px;
border-left: 1px solid $Grey-400;
margin: 20px 0;
padding: 0 10px;
#menu-toggle,
[for="menu-toggle"] {
display: none;
}
.nav-list {
list-style-type: none;
margin: 0;
padding: 0;
&__link {
margin-bottom: 10px;
}
}
}
}
footer {
padding: 20px;
margin-top: auto;
.logo {
background: url(/images/logo.png) no-repeat center;
background-size: contain;
height: 32px;
}
}
button,
.button {
background: $accent;
border: none;
box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.25);
padding: 10px 20px;
border-radius: 50px;
color: white;
font-weight: bold;
}
.round-button {
border-radius: 50%;
background: $accent;
padding: 0;
width: 40px;
height: 40px;
border: none;
box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.25);
margin: 10px;
display: flex;
justify-content: center;
align-items: center;
}
form {
.form-row {
margin: 5px 0;
}
}
@import "blog.scss";
@import "home.scss";
@media screen and (max-width: 992px) {
#content {
flex-direction: column-reverse;
main {
padding-top: 0;
}
nav {
flex: 1 1 auto;
border: none;
margin: 0;
padding: 30px;
.nav-open {
filter: invert(100%);
width: 32px;
height: 32px;
}
#menu-toggle:checked+.nav-container {
max-height: 1000px;
margin-top: 20px;
opacity: 1;
}
[for="menu-toggle"] {
display: inline;
}
.nav-container {
max-height: 0;
overflow: hidden;
transition: all 0.5s ease-out;
opacity: 0;
}
.strava {
display: none;
}
}
}
.header {
padding: 20px 10px;
&__blurb {
padding: 20px 10px;
}
&__headshot {
width: 40vw;
height: 40vw;
max-width: 200px;
max-height: 200px;
}
}
}

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="#CFD8DC" d="M19.967,7.972v12c0,1.1-0.9,2-2,2H5.958c-1.1,0-1.99-0.9-1.99-2l0.01-16c0-1.1,0.89-2,1.99-2h8L19.967,7.972
z"/>
<path fill="none" d="M0,0h24v24H0V0z"/>
<polygon fill="#455A64" points="16,16 13,16 13,19 11,19 11,16 8,16 8,14 11,14 11,11 13,11 13,14 16,14 "/>
<polygon opacity="0.5" fill="#90A4AE" enable-background="new " points="19.967,8.067 19.967,9.067 13.967,9.067 13.967,2.067
"/>
</svg>

After

Width:  |  Height:  |  Size: 895 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<path fill="#4CAF50" enable-background="new " d="M4,6H2v14c0,1.1,0.9,2,2,2h14v-2H4V6z"/>
<path fill="#66BB6A" d="M22,4v12c0,1.1-0.9,2-2,2H8c-1.1,0-2-0.9-2-2V4c0-1.1,0.9-2,2-2h12C21.1,2,22,2.9,22,4z"/>
<path opacity="0.7" fill="#4CAF50" enable-background="new " d="M22,4v12c0,1.1-0.9,2-2,2h-6.04V2H20C21.1,2,22,2.9,22,4z"/>
<polygon fill="#FFFFFF" points="19,11 15,11 15,15 13,15 13,11 9,11 9,9 13,9 13,5 15,5 15,9 19,9 "/>
</svg>

After

Width:  |  Height:  |  Size: 950 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="#CFD8DC" d="M20,2H4C2.9,2,2.01,2.9,2.01,4L2,22l4-4h14c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z"/>
<path fill="#607D8B" d="M6,9h12v2H6V9z M14,14H6v-2h8V14z M18,8H6V6h12V8z"/>
<path fill="none" d="M0,0h24v24H0V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="#9E9E9E" d="M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V6H6V19z"/>
<polygon fill="#616161" points="19,4 15.5,4 14.5,3 9.5,3 8.5,4 5,4 5,6 19,6 "/>
<path fill="none" d="M0,0h24v24H0V0z"/>
<rect x="8" y="8" fill="#757575" width="2" height="11"/>
<rect x="12" y="8" fill="#757575" width="2" height="11"/>
<rect x="16" y="8" fill="#757575" width="2" height="11"/>
</svg>

After

Width:  |  Height:  |  Size: 851 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<path fill="#546E7A" d="M23,4v12c0,1.1-0.9,2-2,2h-7v2h2v2H8v-2h2v-2H3c-1.1,0-2-0.9-2-2V4c0-1.1,0.9-2,2-2h18C22.1,2,23,2.9,23,4z"
/>
<rect x="3" y="4" fill="#B3E5FC" width="18" height="12"/>
<rect x="10" y="17.98" fill="#455A64" width="4" height="2.02"/>
</svg>

After

Width:  |  Height:  |  Size: 776 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<path fill="#81C784" d="M9,19L21,7l-1.41-1.41L9,16.17V19z"/>
<path fill="#4CAF50" d="M9,16.17L4.83,12l-1.42,1.41L9,19V16.17z"/>
</svg>

After

Width:  |  Height:  |  Size: 648 B

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<polygon fill="#CFD8DC" points="17.266,10.453 19.094,8.656 15.344,4.891 13.703,6.547 "/>
<polygon fill="#78909C" points="17.811,9.94 6.75,21 3,21 3,17.25 14.061,6.19 "/>
<path fill="#B0BEC5" d="M20.71,7.04c0.39-0.39,0.39-1.02,0-1.41l-2.34-2.34c-0.39-0.39-1.021-0.39-1.41,0l-1.83,1.83l3.75,3.75
L20.71,7.04z"/>
<path fill="none" d="M0,0h24v24H0V0z"/>
<polygon fill="#B0BEC5" points="6.75,21 3,21 3,17.25 "/>
</svg>

After

Width:  |  Height:  |  Size: 890 B

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<path fill="#E57373" d="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10c5.52,0,10-4.48,10-10C22,6.48,17.52,2,12,2z"/>
<path fill="#F44336" d="M12,4c-4.417,0-8,3.583-8,8c0,4.414,3.583,8,8,8c4.414,0,8-3.586,8-8C20,7.583,16.414,4,12,4z"/>
<path fill="#CFD8DC" d="M13,17h-2v-2h2V17z M13,13h-2V7h2V13z"/>
</svg>

After

Width:  |  Height:  |  Size: 819 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<path fill="#B3E5FC" d="M22,4v12c0,1.1-0.9,2-2,2H8c-1.1,0-2-0.9-2-2V4c0-1.1,0.9-2,2-2h12C21.1,2,22,2.9,22,4z"/>
<path fill="#81D4FA" d="M22,4v12c0,1.1-0.9,2-2,2h-6.03V2H20C21.1,2,22,2.9,22,4z"/>
<polygon fill="#66BB6A" points="11,12 13.029,14.71 16,11 20,16 8,16 "/>
<path fill="#81D4FA" d="M2,6v14c0,1.1,0.9,2,2,2h14v-2H4V6H2z"/>
</svg>

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<rect x="9.375" y="13.292" fill="#795548" width="5.333" height="6.708"/>
<polygon fill="#D7CCC8" points="5,12 5,20 10,20 10,14 14,14 14,20 19,20 19,12 19.021,12 12.01,5.691 "/>
<polygon fill="#F44336" points="12,3 2,12 5,12 12.01,5.691 19.021,12 22,12 "/>
</svg>

After

Width:  |  Height:  |  Size: 777 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<path fill="#6D4C41" d="M4,6H2v14c0,1.1,0.9,2,2,2h14v-2H4V6z"/>
<path fill="#8D6E63" d="M22,4v12c0,1.1-0.9,2-2,2H8c-1.1,0-2-0.9-2-2V4c0-1.1,0.9-2,2-2h12C21.1,2,22,2.9,22,4z"/>
<path fill="#6D4C41" d="M22,4v12c0,1.1-0.9,2-2,2h-6.04V2H20C21.1,2,22,2.9,22,4z"/>
<rect x="9" y="9" fill="#FFFFFF" width="10" height="2"/>
<rect x="9" y="13" fill="#FFFFFF" width="6" height="2"/>
<rect x="9" y="5" fill="#FFFFFF" width="10" height="2"/>
</svg>

After

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<circle fill="#1976D2" cx="12" cy="8" r="4"/>
<path fill="#1565C0" d="M12,14c-2.67,0-8,1.34-8,4v2h16v-2C20,15.34,14.67,14,12,14z"/>
<path fill="none" d="M0,0h24v24H0V0z"/>
<polygon fill="none" points="62,-15 62,9 52.88,9 52.88,-12.92 38,-12.92 38,-15 "/>
</svg>

After

Width:  |  Height:  |  Size: 735 B

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<rect x="3" y="16" fill="#FFFFFF" width="18" height="2"/>
<rect x="3" y="11" fill="#FFFFFF" width="18" height="2"/>
<rect x="3" y="6" fill="#FFFFFF" width="18" height="2"/>
</svg>

After

Width:  |  Height:  |  Size: 693 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="#FFFFFF" d="M10,6L8.59,7.41L13.17,12l-4.58,4.59L10,18l6-6L10,6z"/>
<path fill="none" d="M0,0h24v24H0V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 597 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<path fill="#BBDEFB" d="M21,19V5c0-1.1-0.9-2-2-2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14C20.1,21,21,20.1,21,19z M8.5,13.5
l2.5,3.01L14.5,12l4.5,6H5L8.5,13.5z"/>
<g>
<polygon fill="#43A047" points="19,17.979 5,17.979 8.5,13.479 11,16.489 14.5,11.979 "/>
<polygon fill="#66BB6A" points="5,17.979 8.5,13.479 12.234,17.979 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 856 B

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<rect x="11" y="2" fill="#64B5F6" width="2" height="10"/>
<path fill="#42A5F5" d="M16.561,4.44l-1.451,1.45C16.84,6.94,18,8.83,18,11c0,3.311-2.689,6-6,6c-3.31,0-6-2.689-6-6
c0-2.17,1.16-4.06,2.88-5.12L7.44,4.44C5.36,5.88,4,8.28,4,11c0,4.42,3.58,8,8,8s8-3.58,8-8C20,8.28,18.641,5.88,16.561,4.44z"/>
</svg>

After

Width:  |  Height:  |  Size: 818 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="#FFFFFF" d="M15.41,7.41L14,6l-6,6l6,6l1.41-1.41L10.83,12L15.41,7.41z"/>
<path fill="none" d="M0,0h24v24H0V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 602 B

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<path fill="#4E342E" d="M19.001,3.016H14.82c-0.42-1.16-1.521-2-2.819-2s-2.4,0.84-2.82,2h-4.18c-1.1,0-2,0.9-2,2v14
c0,1.101,0.9,2,2,2h14c1.101,0,2-0.899,2-2v-14C21.001,3.916,20.102,3.016,19.001,3.016z"/>
<path fill="#D7CCC8" d="M19.71,6.553c0-1.354-1.103-2.454-2.455-2.454H6.79c-1.356,0-2.455,1.099-2.455,2.454V17.23
c0,1.354,1.099,2.453,2.455,2.453h10.465c1.354,0,2.455-1.101,2.455-2.453V6.553z"/>
<circle fill="#F1F8E9" cx="12.001" cy="4.016" r="1"/>
</g>
<path fill="none" d="M0,0h24v24H0V0z"/>
<rect x="7" y="16" fill="#455A64" width="7" height="2"/>
<rect x="7" y="12" fill="#455A64" width="10" height="2"/>
<rect x="7" y="8" fill="#455A64" width="10" height="2"/>
<path fill="#FFA000" d="M7.011,6.213l9.984,0.031l0.002-1.236v-2h-2.18c-0.42-1.16-1.521-2-2.82-2s-2.4,0.84-2.82,2h-2.18v2
L7.011,6.213z M11.997,3.008c0.55,0,1,0.45,1,1s-0.45,1-1,1s-1-0.45-1-1S11.447,3.008,11.997,3.008z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<circle fill="#1976D2" cx="15" cy="8.13" r="4"/>
<polygon fill="#8BC34A" points="6,10 6,7 4,7 4,10 1,10 1,12 4,12 4,15 6,15 6,12 9,12 9,10 "/>
<path fill="#1565C0" d="M23,18v2H7v-2c0-2.66,5.33-4,8-4S23,15.34,23,18z"/>
<path fill="none" d="M15,12.13v-8c2.21,0,4,1.79,4,4S17.21,12.13,15,12.13z"/>
<path fill="none" d="M23,18v2h-8v-6C17.67,14,23,15.34,23,18z"/>
<polygon fill="none" points="39,-2 39,22 29.88,22 29.88,0.08 15,0.08 15,-2 "/>
</svg>

After

Width:  |  Height:  |  Size: 961 B

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g id="Capa_1_1_">
<path fill="none" d="M0,0h24v24H0V0z"/>
<path fill="#37474F" d="M21,7v12c0,1.1-0.9,2-2,2H5c-1.11,0-2-0.9-2-2V5c0-1.1,0.89-2,2-2h12L21,7z"/>
<path opacity="0.5" fill="#455A64" d="M11.96,3v18H5c-1.11,0-2-0.9-2-2V5c0-1.1,0.89-2,2-2H11.96z"/>
<path fill="#B0BEC5" d="M15,16c0,1.66-1.34,3-3,3h-0.05C10.31,18.97,9,17.641,9,16s1.31-2.97,2.95-3H12C13.66,13,15,14.34,15,16z"
/>
<rect x="5" y="5" fill="#B0BEC5" width="10" height="4"/>
<path fill="#90A4AE" d="M15,16c0,1.66-1.34,3-3,3h-0.05v-6H12C13.66,13,15,14.34,15,16z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<polygon fill="#1E88E5" points="23,12 2.01,21 2,14 17,12 2,10 2.01,3 "/>
<path fill="none" d="M0,0h24v24H0V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 591 B

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><svg width='200px' height='200px' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="uil-ring-alt"><rect x="0" y="0" width="100" height="100" fill="none" class="bk"></rect><circle cx="50" cy="50" r="40" stroke="none" fill="none" stroke-width="10" stroke-linecap="round"></circle><circle cx="50" cy="50" r="40" stroke="#e53935" fill="none" stroke-width="6" stroke-linecap="round"><animate attributeName="stroke-dashoffset" dur="2s" repeatCount="indefinite" from="0" to="502"></animate><animate attributeName="stroke-dasharray" dur="2s" repeatCount="indefinite" values="150.6 100.4;1 250;150.6 100.4"></animate></circle></svg>

After

Width:  |  Height:  |  Size: 706 B

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<rect x="4" y="17" fill="#BA68C8" width="10" height="2"/>
<rect x="4" y="9" fill="#4DB6AC" width="16" height="2"/>
<rect x="4" y="13" fill="#DCE775" width="16" height="2"/>
<rect x="4" y="5" fill="#4FC3F7" width="16" height="2"/>
<path fill="none" d="M0,0v24h24V0H0z M22.49,19.42H13.97V19H14v-2h-0.03v-2H20v-2h-6.03v-2H20V9h-6.03V7H20V5h-6.03V3.28h8.521
V19.42z"/>
<rect x="13.97" y="17" opacity="0.2" fill="#546E7A" width="0.03" height="2"/>
<rect x="13.97" y="5" opacity="0.2" fill="#546E7A" width="6.03" height="2"/>
<rect x="13.97" y="9" opacity="0.2" fill="#546E7A" width="6.03" height="2"/>
<rect x="13.97" y="13" opacity="0.2" fill="#546E7A" width="6.03" height="2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<rect x="9.375" y="13.292" fill="#795548" width="5.333" height="6.708"/>
<polygon fill="#D7CCC8" points="5,12 5,20 10,20 10,14 14,14 14,20 19,20 19,12 19.021,12 12.01,5.691 "/>
<polygon fill="#F44336" points="12,3 2,12 5,12 12.01,5.691 19.021,12 22,12 "/>
</svg>

After

Width:  |  Height:  |  Size: 777 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="none" d="M0,0h24v24H0V0z"/>
<polygon fill="#03A9F4" points="9,16 15,16 15,10 19,10 12,3 5,10 9,10 "/>
<rect x="5" y="18" fill="#0288D1" width="14" height="2"/>
</svg>

After

Width:  |  Height:  |  Size: 652 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g id="Capa_1_1_">
<polygon fill="#E53935" points="22,12 22,18 16,18 18.29,15.71 13.41,10.83 9.41,14.83 2,7.41 3.41,6 9.41,12 13.41,8 19.71,14.29
"/>
<path fill="none" d="M0,0h24v24H0V0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 682 B

View file

@ -0,0 +1,67 @@
function Navigate(url) {
window.location = url;
}
function CreateCookie(name, value, days) {
var expires;
if (days) {
var date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
expires = "; expires=" + date.toGMTString();
}
else {
expires = "";
}
document.cookie = name + "=" + value + expires + "; path=/";
}
function round(value, decimals) {
return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
}
function isNumber(value){
return isFinite(String(value));
}
String.prototype.upperCaseFirst = function() {
var lower = this.toLowerCase();
return lower.charAt(0).toUpperCase() + lower.slice(1);
};
function twoDigitZeroPad(input){
input=input.toString();
return [!input[1] && '0' || "", input].join('');
}
Date.prototype.oldToString = Date.prototype.toString;
Date.prototype.toString = function(format) {
if (!format)
return this.oldToString();
var me=this;
return format.replace(/./g, function(match) {
switch(match){
case 'd': return twoDigitZeroPad(me.getDate());
case 'm': return twoDigitZeroPad(me.getMonth()+1);
case 'y': return me.getFullYear();
case "H": return twoDigitZeroPad(me.getHours());
case "i": return twoDigitZeroPad(me.getMinutes());
case "s": return twoDigitZeroPad(me.getSeconds());
default: return match;
}
});
};
Number.prototype.oldToString=Number.prototype.toString;
Number.prototype.toString=function(input, shouldRound){
if (!shouldRound)
return this.oldToString(input);
return round(this, input);
};
function documentReady(callback) {
if (document.readyState === "complete" || document.readyState === "interactive")
setTimeout(callback, 1);
else
document.addEventListener("DOMContentLoaded", callback);
}

View file

@ -0,0 +1 @@
../node_modules/@fortawesome/fontawesome-free/webfonts