WIP on storing themes in database
@ -76,6 +76,20 @@ type TagMapIdConverter () =
|
||||
override _.ReadJson (reader : JsonReader, _ : Type, _ : TagMapId, _ : bool, _ : JsonSerializer) =
|
||||
(string >> TagMapId) reader.Value
|
||||
|
||||
type ThemeAssetIdConverter () =
|
||||
inherit JsonConverter<ThemeAssetId> ()
|
||||
override _.WriteJson (writer : JsonWriter, value : ThemeAssetId, _ : JsonSerializer) =
|
||||
writer.WriteValue (ThemeAssetId.toString value)
|
||||
override _.ReadJson (reader : JsonReader, _ : Type, _ : ThemeAssetId, _ : bool, _ : JsonSerializer) =
|
||||
(string >> ThemeAssetId.ofString) reader.Value
|
||||
|
||||
type ThemeIdConverter () =
|
||||
inherit JsonConverter<ThemeId> ()
|
||||
override _.WriteJson (writer : JsonWriter, value : ThemeId, _ : JsonSerializer) =
|
||||
writer.WriteValue (ThemeId.toString value)
|
||||
override _.ReadJson (reader : JsonReader, _ : Type, _ : ThemeId, _ : bool, _ : JsonSerializer) =
|
||||
(string >> ThemeId) reader.Value
|
||||
|
||||
type WebLogIdConverter () =
|
||||
inherit JsonConverter<WebLogId> ()
|
||||
override _.WriteJson (writer : JsonWriter, value : WebLogId, _ : JsonSerializer) =
|
||||
@ -106,6 +120,8 @@ let all () : JsonConverter seq =
|
||||
PageIdConverter ()
|
||||
PostIdConverter ()
|
||||
TagMapIdConverter ()
|
||||
ThemeAssetIdConverter ()
|
||||
ThemeIdConverter ()
|
||||
WebLogIdConverter ()
|
||||
WebLogUserIdConverter ()
|
||||
// Handles DUs with no associated data, as well as option fields
|
||||
|
@ -20,6 +20,12 @@ module Table =
|
||||
/// The tag map table
|
||||
let TagMap = "TagMap"
|
||||
|
||||
/// The theme table
|
||||
let Theme = "Theme"
|
||||
|
||||
/// The theme asset table
|
||||
let ThemeAsset = "ThemeAsset"
|
||||
|
||||
/// The web log table
|
||||
let WebLog = "WebLog"
|
||||
|
||||
@ -27,7 +33,7 @@ module Table =
|
||||
let WebLogUser = "WebLogUser"
|
||||
|
||||
/// A list of all tables
|
||||
let all = [ Category; Comment; Page; Post; TagMap; WebLog; WebLogUser ]
|
||||
let all = [ Category; Comment; Page; Post; TagMap; Theme; ThemeAsset; WebLog; WebLogUser ]
|
||||
|
||||
|
||||
/// Functions to assist with retrieving data
|
||||
@ -697,6 +703,68 @@ module TagMap =
|
||||
}
|
||||
|
||||
|
||||
/// Functions to manipulate themes
|
||||
module Theme =
|
||||
|
||||
/// Get all themes
|
||||
let list =
|
||||
rethink<Theme list> {
|
||||
withTable Table.Theme
|
||||
filter (fun row -> row["id"].Ne "admin" :> obj)
|
||||
without [ "templates" ]
|
||||
orderBy "id"
|
||||
result; withRetryDefault
|
||||
}
|
||||
|
||||
/// Retrieve a theme by its ID
|
||||
let findById (themeId : ThemeId) =
|
||||
rethink<Theme> {
|
||||
withTable Table.Theme
|
||||
get themeId
|
||||
resultOption; withRetryOptionDefault
|
||||
}
|
||||
|
||||
/// Save a theme
|
||||
let save (theme : Theme) =
|
||||
rethink {
|
||||
withTable Table.Theme
|
||||
get theme.id
|
||||
replace theme
|
||||
write; withRetryDefault; ignoreResult
|
||||
}
|
||||
|
||||
|
||||
/// Functions to manipulate theme assets
|
||||
module ThemeAsset =
|
||||
|
||||
/// Delete all assets for a theme
|
||||
let deleteByTheme themeId =
|
||||
let keyPrefix = $"^{ThemeId.toString themeId}/"
|
||||
rethink {
|
||||
withTable Table.ThemeAsset
|
||||
filter (fun row -> row["id"].Match keyPrefix :> obj)
|
||||
delete
|
||||
write; withRetryDefault; ignoreResult
|
||||
}
|
||||
|
||||
/// Find a theme asset by its ID
|
||||
let findById (assetId : ThemeAssetId) =
|
||||
rethink<ThemeAsset> {
|
||||
withTable Table.ThemeAsset
|
||||
get assetId
|
||||
resultOption; withRetryOptionDefault
|
||||
}
|
||||
|
||||
/// Save a theme assed
|
||||
let save (asset : ThemeAsset) =
|
||||
rethink {
|
||||
withTable Table.ThemeAsset
|
||||
get asset.id
|
||||
replace asset
|
||||
write; withRetryDefault; ignoreResult
|
||||
}
|
||||
|
||||
|
||||
/// Functions to manipulate web logs
|
||||
module WebLog =
|
||||
|
||||
|
@ -246,6 +246,47 @@ module TagMap =
|
||||
}
|
||||
|
||||
|
||||
/// A theme
|
||||
type Theme =
|
||||
{ /// The ID / path of the theme
|
||||
id : ThemeId
|
||||
|
||||
/// A long name of the theme
|
||||
name : string
|
||||
|
||||
/// The version of the theme
|
||||
version : string
|
||||
|
||||
/// The templates for this theme
|
||||
templates: ThemeTemplate list
|
||||
}
|
||||
|
||||
/// Functions to support themes
|
||||
module Theme =
|
||||
|
||||
/// An empty theme
|
||||
let empty =
|
||||
{ id = ThemeId ""
|
||||
name = ""
|
||||
version = ""
|
||||
templates = []
|
||||
}
|
||||
|
||||
|
||||
/// A theme asset (a file served as part of a theme, at /themes/[theme]/[asset-path])
|
||||
type ThemeAsset =
|
||||
{
|
||||
/// The ID of the asset (consists of theme and path)
|
||||
id : ThemeAssetId
|
||||
|
||||
/// The updated date (set from the file date from the ZIP archive)
|
||||
updatedOn : DateTime
|
||||
|
||||
/// The data for the asset
|
||||
data : byte[]
|
||||
}
|
||||
|
||||
|
||||
/// A web log
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type WebLog =
|
||||
|
@ -373,6 +373,39 @@ module TagMapId =
|
||||
let create () = TagMapId (newId ())
|
||||
|
||||
|
||||
/// An identifier for a theme (represents its path)
|
||||
type ThemeId = ThemeId of string
|
||||
|
||||
/// Functions to support theme IDs
|
||||
module ThemeId =
|
||||
let toString = function ThemeId ti -> ti
|
||||
|
||||
|
||||
/// An identifier for a theme asset
|
||||
type ThemeAssetId = ThemeAssetId of ThemeId * string
|
||||
|
||||
/// Functions to support theme asset IDs
|
||||
module ThemeAssetId =
|
||||
|
||||
/// Convert a theme asset ID into a path string
|
||||
let toString = function ThemeAssetId (ThemeId theme, asset) -> $"{theme}/{asset}"
|
||||
|
||||
/// Convert a string into a theme asset ID
|
||||
let ofString (it : string) =
|
||||
let themeIdx = it.IndexOf "/"
|
||||
ThemeAssetId (ThemeId it[..(themeIdx - 1)], it[(themeIdx + 1)..])
|
||||
|
||||
|
||||
/// A template for a theme
|
||||
type ThemeTemplate =
|
||||
{ /// The name of the template
|
||||
name : string
|
||||
|
||||
/// The text of the template
|
||||
text : string
|
||||
}
|
||||
|
||||
|
||||
/// An identifier for a web log
|
||||
type WebLogId = WebLogId of string
|
||||
|
||||
|
@ -97,7 +97,6 @@ module CategoryCache =
|
||||
module TemplateCache =
|
||||
|
||||
open System
|
||||
open System.IO
|
||||
open System.Text.RegularExpressions
|
||||
open DotLiquid
|
||||
|
||||
@ -107,19 +106,28 @@ module TemplateCache =
|
||||
/// Custom include parameter pattern
|
||||
let private hasInclude = Regex ("""{% include_template \"(.*)\" %}""", RegexOptions.None, TimeSpan.FromSeconds 2)
|
||||
|
||||
/// Get a template for the given theme and template nate
|
||||
let get (theme : string) (templateName : string) = backgroundTask {
|
||||
let templatePath = $"themes/{theme}/{templateName}"
|
||||
/// Get a template for the given theme and template name
|
||||
let get (themeId : string) (templateName : string) conn = backgroundTask {
|
||||
let templatePath = $"{themeId}/{templateName}"
|
||||
match _cache.ContainsKey templatePath with
|
||||
| true -> ()
|
||||
| false ->
|
||||
let! file = File.ReadAllTextAsync $"{templatePath}.liquid"
|
||||
let mutable text = file
|
||||
while hasInclude.IsMatch text do
|
||||
let child = hasInclude.Match text
|
||||
let! file = File.ReadAllTextAsync $"themes/{theme}/{child.Groups[1].Value}.liquid"
|
||||
text <- text.Replace (child.Value, file)
|
||||
_cache[templatePath] <- Template.Parse (text, SyntaxCompatibility.DotLiquid22)
|
||||
match! Data.Theme.findById (ThemeId themeId) conn with
|
||||
| Some theme ->
|
||||
let mutable text = (theme.templates |> List.find (fun t -> t.name = templateName)).text
|
||||
while hasInclude.IsMatch text do
|
||||
let child = hasInclude.Match text
|
||||
let childText = (theme.templates |> List.find (fun t -> t.name = child.Groups[1].Value)).text
|
||||
text <- text.Replace (child.Value, childText)
|
||||
_cache[templatePath] <- Template.Parse (text, SyntaxCompatibility.DotLiquid22)
|
||||
| None -> ()
|
||||
return _cache[templatePath]
|
||||
}
|
||||
|
||||
/// Invalidate all template cache entries for the given theme ID
|
||||
let invalidateTheme (themeId : string) =
|
||||
_cache.Keys
|
||||
|> Seq.filter (fun key -> key.StartsWith themeId)
|
||||
|> List.ofSeq
|
||||
|> List.iter (fun key -> match _cache.TryRemove key with _, _ -> ())
|
||||
|
||||
|
@ -50,7 +50,7 @@ let dashboard : HttpHandler = fun next ctx -> task {
|
||||
|
||||
// GET /admin/categories
|
||||
let listCategories : HttpHandler = fun next ctx -> task {
|
||||
let! catListTemplate = TemplateCache.get "admin" "category-list-body"
|
||||
let! catListTemplate = TemplateCache.get "admin" "category-list-body" ctx.Conn
|
||||
let hash = Hash.FromAnonymousObject {|
|
||||
web_log = ctx.WebLog
|
||||
categories = CategoryCache.get ctx
|
||||
@ -271,49 +271,6 @@ let savePage : HttpHandler = fun next ctx -> task {
|
||||
| None -> return! Error.notFound next ctx
|
||||
}
|
||||
|
||||
// -- WEB LOG SETTINGS --
|
||||
|
||||
// GET /admin/settings
|
||||
let settings : HttpHandler = fun next ctx -> task {
|
||||
let webLog = ctx.WebLog
|
||||
let! allPages = Data.Page.findAll webLog.id ctx.Conn
|
||||
return!
|
||||
Hash.FromAnonymousObject
|
||||
{| csrf = csrfToken ctx
|
||||
model = SettingsModel.fromWebLog webLog
|
||||
pages =
|
||||
seq {
|
||||
KeyValuePair.Create ("posts", "- First Page of Posts -")
|
||||
yield! allPages
|
||||
|> List.sortBy (fun p -> p.title.ToLower ())
|
||||
|> List.map (fun p -> KeyValuePair.Create (PageId.toString p.id, p.title))
|
||||
}
|
||||
|> Array.ofSeq
|
||||
themes = themes ()
|
||||
web_log = webLog
|
||||
page_title = "Web Log Settings"
|
||||
|}
|
||||
|> viewForTheme "admin" "settings" next ctx
|
||||
}
|
||||
|
||||
// POST /admin/settings
|
||||
let saveSettings : HttpHandler = fun next ctx -> task {
|
||||
let webLog = ctx.WebLog
|
||||
let conn = ctx.Conn
|
||||
let! model = ctx.BindFormAsync<SettingsModel> ()
|
||||
match! Data.WebLog.findById webLog.id conn with
|
||||
| Some webLog ->
|
||||
let webLog = model.update webLog
|
||||
do! Data.WebLog.updateSettings webLog conn
|
||||
|
||||
// Update cache
|
||||
WebLogCache.set webLog
|
||||
|
||||
do! addMessage ctx { UserMessage.success with message = "Web log settings saved successfully" }
|
||||
return! redirectToGet (WebLog.relativeUrl webLog (Permalink "admin/settings")) next ctx
|
||||
| None -> return! Error.notFound next ctx
|
||||
}
|
||||
|
||||
// -- TAG MAPPINGS --
|
||||
|
||||
open Microsoft.AspNetCore.Http
|
||||
@ -332,7 +289,7 @@ let private tagMappingHash (ctx : HttpContext) = task {
|
||||
// GET /admin/settings/tag-mappings
|
||||
let tagMappings : HttpHandler = fun next ctx -> task {
|
||||
let! hash = tagMappingHash ctx
|
||||
let! listTemplate = TemplateCache.get "admin" "tag-mapping-list-body"
|
||||
let! listTemplate = TemplateCache.get "admin" "tag-mapping-list-body" ctx.Conn
|
||||
|
||||
hash.Add ("tag_mapping_list", listTemplate.Render hash)
|
||||
hash.Add ("page_title", "Tag Mappings")
|
||||
@ -392,3 +349,156 @@ let deleteMapping tagMapId : HttpHandler = fun next ctx -> task {
|
||||
| false -> do! addMessage ctx { UserMessage.error with message = "Tag mapping not found; nothing deleted" }
|
||||
return! tagMappingsBare next ctx
|
||||
}
|
||||
|
||||
// -- THEMES --
|
||||
|
||||
open System.IO.Compression
|
||||
open System.Text.RegularExpressions
|
||||
|
||||
// GET /admin/theme/update
|
||||
let themeUpdatePage : HttpHandler = fun next ctx -> task {
|
||||
return!
|
||||
Hash.FromAnonymousObject {|
|
||||
csrf = csrfToken ctx
|
||||
page_title = "Upload Theme"
|
||||
|}
|
||||
|> viewForTheme "admin" "upload-theme" next ctx
|
||||
}
|
||||
|
||||
/// Update the name and version for a theme based on the version.txt file, if present
|
||||
let private updateNameAndVersion (theme : Theme) (zip : ZipArchive) = backgroundTask {
|
||||
let now () = DateTime.UtcNow.ToString "yyyyMMdd.HHmm"
|
||||
match zip.Entries |> Seq.filter (fun it -> it.FullName = "version.txt") |> Seq.tryHead with
|
||||
| Some versionItem ->
|
||||
use versionFile = new StreamReader(versionItem.Open ())
|
||||
let! versionText = versionFile.ReadToEndAsync ()
|
||||
let parts = versionText.Trim().Replace("\r", "").Split "\n"
|
||||
let displayName = if parts[0] > "" then parts[0] else ThemeId.toString theme.id
|
||||
let version = if parts.Length > 1 && parts[1] > "" then parts[1] else now ()
|
||||
return { theme with name = displayName; version = version }
|
||||
| None ->
|
||||
return { theme with name = ThemeId.toString theme.id; version = now () }
|
||||
}
|
||||
|
||||
/// Delete all theme assets, and remove templates from theme
|
||||
let private checkForCleanLoad (theme : Theme) cleanLoad conn = backgroundTask {
|
||||
if cleanLoad then
|
||||
do! Data.ThemeAsset.deleteByTheme theme.id conn
|
||||
return { theme with templates = [] }
|
||||
else
|
||||
return theme
|
||||
}
|
||||
|
||||
/// Update the theme with all templates from the ZIP archive
|
||||
let private updateTemplates (theme : Theme) (zip : ZipArchive) = backgroundTask {
|
||||
let tasks =
|
||||
zip.Entries
|
||||
|> Seq.filter (fun it -> it.Name.EndsWith ".liquid")
|
||||
|> Seq.map (fun templateItem -> backgroundTask {
|
||||
use templateFile = new StreamReader (templateItem.Open ())
|
||||
let! template = templateFile.ReadToEndAsync ()
|
||||
return { name = templateItem.Name.Replace (".liquid", ""); text = template }
|
||||
})
|
||||
let! templates = Task.WhenAll tasks
|
||||
return
|
||||
templates
|
||||
|> Array.fold (fun t template ->
|
||||
{ t with templates = template :: (t.templates |> List.filter (fun it -> it.name <> template.name)) })
|
||||
theme
|
||||
}
|
||||
|
||||
/// Update theme assets from the ZIP archive
|
||||
let private updateAssets themeId (zip : ZipArchive) conn = backgroundTask {
|
||||
for asset in zip.Entries |> Seq.filter (fun it -> it.FullName.StartsWith "wwwroot") do
|
||||
let assetName = asset.FullName.Replace ("wwwroot/", "")
|
||||
if assetName <> "" && not (assetName.EndsWith "/") then
|
||||
use stream = new MemoryStream ()
|
||||
do! asset.Open().CopyToAsync stream
|
||||
do! Data.ThemeAsset.save
|
||||
{ id = ThemeAssetId (themeId, assetName)
|
||||
updatedOn = asset.LastWriteTime.DateTime
|
||||
data = stream.ToArray ()
|
||||
} conn
|
||||
}
|
||||
|
||||
/// Load a theme from the given stream, which should contain a ZIP archive
|
||||
let loadThemeFromZip themeName file clean conn = backgroundTask {
|
||||
use zip = new ZipArchive (file, ZipArchiveMode.Read)
|
||||
let themeId = ThemeId themeName
|
||||
let! theme = backgroundTask {
|
||||
match! Data.Theme.findById themeId conn with
|
||||
| Some t -> return t
|
||||
| None -> return { Theme.empty with id = themeId }
|
||||
}
|
||||
let! theme = updateNameAndVersion theme zip
|
||||
let! theme = checkForCleanLoad theme clean conn
|
||||
let! theme = updateTemplates theme zip
|
||||
do! updateAssets themeId zip conn
|
||||
do! Data.Theme.save theme conn
|
||||
}
|
||||
|
||||
// POST /admin/theme/update
|
||||
let updateTheme : HttpHandler = fun next ctx -> task {
|
||||
if ctx.Request.HasFormContentType && ctx.Request.Form.Files.Count > 0 then
|
||||
let themeFile = Seq.head ctx.Request.Form.Files
|
||||
let themeName = themeFile.FileName.Split(".").[0].ToLowerInvariant().Replace (" ", "-")
|
||||
// TODO: add restriction for admin theme based on role
|
||||
if Regex.IsMatch (themeName, """^[a-z0-9\-]+$""") then
|
||||
use stream = new MemoryStream ()
|
||||
do! themeFile.CopyToAsync stream
|
||||
do! loadThemeFromZip themeName stream true ctx.Conn
|
||||
do! addMessage ctx { UserMessage.success with message = "Theme updated successfully" }
|
||||
return! redirectToGet (WebLog.relativeUrl ctx.WebLog (Permalink "admin/dashboard")) next ctx
|
||||
else
|
||||
do! addMessage ctx { UserMessage.error with message = $"Theme name {themeName} is invalid" }
|
||||
return! redirectToGet (WebLog.relativeUrl ctx.WebLog (Permalink "admin/theme/update")) next ctx
|
||||
else
|
||||
return! RequestErrors.BAD_REQUEST "Bad request" next ctx
|
||||
}
|
||||
|
||||
// -- WEB LOG SETTINGS --
|
||||
|
||||
// GET /admin/settings
|
||||
let settings : HttpHandler = fun next ctx -> task {
|
||||
let webLog = ctx.WebLog
|
||||
let! allPages = Data.Page.findAll webLog.id ctx.Conn
|
||||
let! themes = Data.Theme.list ctx.Conn
|
||||
return!
|
||||
Hash.FromAnonymousObject
|
||||
{| csrf = csrfToken ctx
|
||||
model = SettingsModel.fromWebLog webLog
|
||||
pages =
|
||||
seq {
|
||||
KeyValuePair.Create ("posts", "- First Page of Posts -")
|
||||
yield! allPages
|
||||
|> List.sortBy (fun p -> p.title.ToLower ())
|
||||
|> List.map (fun p -> KeyValuePair.Create (PageId.toString p.id, p.title))
|
||||
}
|
||||
|> Array.ofSeq
|
||||
themes = themes
|
||||
|> Seq.ofList
|
||||
|> Seq.map (fun it -> KeyValuePair.Create (ThemeId.toString it.id, $"{it.name} (v{it.version})"))
|
||||
|> Array.ofSeq
|
||||
web_log = webLog
|
||||
page_title = "Web Log Settings"
|
||||
|}
|
||||
|> viewForTheme "admin" "settings" next ctx
|
||||
}
|
||||
|
||||
// POST /admin/settings
|
||||
let saveSettings : HttpHandler = fun next ctx -> task {
|
||||
let webLog = ctx.WebLog
|
||||
let conn = ctx.Conn
|
||||
let! model = ctx.BindFormAsync<SettingsModel> ()
|
||||
match! Data.WebLog.findById webLog.id conn with
|
||||
| Some webLog ->
|
||||
let webLog = model.update webLog
|
||||
do! Data.WebLog.updateSettings webLog conn
|
||||
|
||||
// Update cache
|
||||
WebLogCache.set webLog
|
||||
|
||||
do! addMessage ctx { UserMessage.success with message = "Web log settings saved successfully" }
|
||||
return! redirectToGet (WebLog.relativeUrl webLog (Permalink "admin/settings")) next ctx
|
||||
| None -> return! Error.notFound next ctx
|
||||
}
|
||||
|
@ -108,12 +108,12 @@ let viewForTheme theme template next ctx = fun (hash : Hash) -> task {
|
||||
// the net effect is a "layout" capability similar to Razor or Pug
|
||||
|
||||
// Render view content...
|
||||
let! contentTemplate = TemplateCache.get theme template
|
||||
let! contentTemplate = TemplateCache.get theme template ctx.Conn
|
||||
hash.Add ("content", contentTemplate.Render hash)
|
||||
|
||||
// ...then render that content with its layout
|
||||
let isHtmx = ctx.Request.IsHtmx && not ctx.Request.IsHtmxRefresh
|
||||
let! layoutTemplate = TemplateCache.get theme (if isHtmx then "layout-partial" else "layout")
|
||||
let! layoutTemplate = TemplateCache.get theme (if isHtmx then "layout-partial" else "layout") ctx.Conn
|
||||
|
||||
return! htmlString (layoutTemplate.Render hash) next ctx
|
||||
}
|
||||
@ -123,10 +123,10 @@ let bareForTheme theme template next ctx = fun (hash : Hash) -> task {
|
||||
do! populateHash hash ctx
|
||||
|
||||
// Bare templates are rendered with layout-bare
|
||||
let! contentTemplate = TemplateCache.get theme template
|
||||
let! contentTemplate = TemplateCache.get theme template ctx.Conn
|
||||
hash.Add ("content", contentTemplate.Render hash)
|
||||
|
||||
let! layoutTemplate = TemplateCache.get theme "layout-bare"
|
||||
let! layoutTemplate = TemplateCache.get theme "layout-bare" ctx.Conn
|
||||
|
||||
// add messages as HTTP headers
|
||||
let messages = hash["messages"] :?> UserMessage[]
|
||||
|
@ -2,13 +2,13 @@
|
||||
module MyWebLog.Handlers.Routes
|
||||
|
||||
open Giraffe
|
||||
open Microsoft.AspNetCore.Http
|
||||
open MyWebLog
|
||||
|
||||
/// Module to resolve routes that do not match any other known route (web blog content)
|
||||
module CatchAll =
|
||||
|
||||
open DotLiquid
|
||||
open Microsoft.AspNetCore.Http
|
||||
open MyWebLog.ViewModels
|
||||
|
||||
/// Sequence where the first returned value is the proper handler for the link
|
||||
@ -89,6 +89,48 @@ module CatchAll =
|
||||
| None -> return! Error.notFound next ctx
|
||||
}
|
||||
|
||||
|
||||
/// Serve theme assets
|
||||
module Asset =
|
||||
|
||||
open System
|
||||
open Microsoft.AspNetCore.Http.Headers
|
||||
open Microsoft.AspNetCore.StaticFiles
|
||||
open Microsoft.Net.Http.Headers
|
||||
|
||||
/// Determine if the asset has been modified since the date/time specified by the If-Modified-Since header
|
||||
let private checkModified asset (ctx : HttpContext) : HttpHandler option =
|
||||
match ctx.Request.Headers.IfModifiedSince with
|
||||
| it when it.Count < 1 -> None
|
||||
| it ->
|
||||
if asset.updatedOn > DateTime.Parse it[0] then
|
||||
None
|
||||
else
|
||||
Some (setStatusCode 304 >=> setBodyFromString "Not Modified")
|
||||
|
||||
/// An instance of ASP.NET Core's file extension to MIME type converter
|
||||
let private mimeMap = FileExtensionContentTypeProvider ()
|
||||
|
||||
// GET /theme/{theme}/{**path}
|
||||
let serveAsset (urlParts : string seq) : HttpHandler = fun next ctx -> task {
|
||||
let path = urlParts |> Seq.skip 1 |> Seq.head
|
||||
match! Data.ThemeAsset.findById (ThemeAssetId.ofString path) ctx.Conn with
|
||||
| Some asset ->
|
||||
match checkModified asset ctx with
|
||||
| Some threeOhFour -> return! threeOhFour next ctx
|
||||
| None ->
|
||||
let mimeType =
|
||||
match mimeMap.TryGetContentType path with
|
||||
| true, typ -> typ
|
||||
| false, _ -> "application/octet-stream"
|
||||
let headers = ResponseHeaders ctx.Response.Headers
|
||||
headers.LastModified <- Some (DateTimeOffset asset.updatedOn) |> Option.toNullable
|
||||
headers.ContentType <- MediaTypeHeaderValue mimeType
|
||||
return! setBody asset.data next ctx
|
||||
| None -> return! Error.notFound next ctx
|
||||
}
|
||||
|
||||
|
||||
/// The primary myWebLog router
|
||||
let router : HttpHandler = choose [
|
||||
GET >=> choose [
|
||||
@ -126,7 +168,8 @@ let router : HttpHandler = choose [
|
||||
routef "/%s/edit" Admin.editMapping
|
||||
])
|
||||
])
|
||||
route "/user/edit" >=> User.edit
|
||||
route "/theme/update" >=> Admin.themeUpdatePage
|
||||
route "/user/edit" >=> User.edit
|
||||
]
|
||||
POST >=> validateCsrf >=> choose [
|
||||
subRoute "/category" (choose [
|
||||
@ -155,15 +198,17 @@ let router : HttpHandler = choose [
|
||||
routef "/%s/delete" Admin.deleteMapping
|
||||
])
|
||||
])
|
||||
route "/user/save" >=> User.save
|
||||
route "/theme/update" >=> Admin.updateTheme
|
||||
route "/user/save" >=> User.save
|
||||
]
|
||||
])
|
||||
GET >=> routexp "/category/(.*)" Post.pageOfCategorizedPosts
|
||||
GET >=> routef "/page/%i" Post.pageOfPosts
|
||||
GET >=> routef "/page/%i/" Post.redirectToPageOfPosts
|
||||
GET >=> routexp "/tag/(.*)" Post.pageOfTaggedPosts
|
||||
GET_HEAD >=> routexp "/category/(.*)" Post.pageOfCategorizedPosts
|
||||
GET_HEAD >=> routef "/page/%i" Post.pageOfPosts
|
||||
GET_HEAD >=> routef "/page/%i/" Post.redirectToPageOfPosts
|
||||
GET_HEAD >=> routexp "/tag/(.*)" Post.pageOfTaggedPosts
|
||||
GET_HEAD >=> routexp "/themes/(.*)" Asset.serveAsset
|
||||
subRoute "/user" (choose [
|
||||
GET >=> choose [
|
||||
GET_HEAD >=> choose [
|
||||
route "/log-on" >=> User.logOn None
|
||||
route "/log-off" >=> User.logOff
|
||||
]
|
||||
@ -171,7 +216,7 @@ let router : HttpHandler = choose [
|
||||
route "/log-on" >=> User.doLogOn
|
||||
]
|
||||
])
|
||||
GET >=> CatchAll.route
|
||||
GET_HEAD >=> CatchAll.route
|
||||
Error.notFound
|
||||
]
|
||||
|
||||
|
@ -37,7 +37,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include=".\themes\**" CopyToOutputDirectory="Always" />
|
||||
<None Include=".\wwwroot\**" CopyToOutputDirectory="Always" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
"hostname": "data02.bitbadger.solutions",
|
||||
"database": "myWebLog_dev"
|
||||
},
|
||||
"Generator": "myWebLog 2.0-alpha33",
|
||||
"Generator": "myWebLog 2.0-alpha34",
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"MyWebLog.Handlers": "Debug"
|
||||
|
@ -1,4 +0,0 @@
|
||||
{%- assign ot_books = "ZPSOFpd27EeFQvrTZE296Q|VERPK2rYN0G44bo7LjBSRw|fA6zV4ON002oMPoVUZs17Q|vLsJo3GL1ECOQbSIEMUaig|WZ933xSw6kmo7644X9mmXw|68f2_zVlekaaIZD_BX0sZg|WffJH3t68kuVUyNZXYSFYw|xNlTF35s_UulA7S-NTgcAA|0y4PDhOc7kiw9hp0RwaJCg|59ysY5363EaVCG_g7fUH5A|aBlyuIzm206LTKx-uYIGnA|YoQKqdkrbUivAeUf3DISow|WOAq8A9bB0a8TKmYAB7IdA|TsxpRv1lrUiNNeGGfIx6XA|rJkq5d_MyUGPgACkMm0iTw|F_X9vUdpAUy957_qSejcnw|5jvC66CtG06zRlkKuP1naw|su8Qq5KUwEyoDmssUQhjHQ|FtVxmNkn_EavFJY3jqodfA|oToPYf8OQkCBsB6J65j3SA|rKzSJptZjU6YAOy6nIhfbw|K2lHyseO6UWdHftwLswhgg|rpThtWd1HUuSeyHLIs7gpw|CD8jYdoi0kGyXTbsD6F0aA|ttObDyRHnUudsSNUsdQ8BA|fi4ORg0B90C-DeQGTrOaMA|6AW2cZfbAkWX5HT4XEGjig|Pf6Gllf9gU-IF9xDcmWd0g|7cDaYscN6kmKsgwuiwPt9Q|WG_j96GOH0KzGORwKL8YVA|p7wi1sNiGUWhUa1tqRhS8Q|CgIcCoHtpUiCshdHe8wMoA|V1PTu4Xoq0eUnniYIb8Mkw|1h1LTN1XyU-qWIPHVY398A|t34QuaRN6kOWizvKNPtdSg|Tw5oe-kzfkiqCc6_4AVskQ|At7RBOfiN0aWMKln0Dkreg|JiUy9RgUZUOrjeMdBpln9g|hc7roSimX0mukJPTe6clWA|kTbX-fYdFk6TJ8Tikec-AQ" | split: "|" -%}
|
||||
{%- assign nt_books = "Ksr4Dto97Um5NxhLpoRk5A|w-4_O87lYE-9_nrIXOuIHw|oJ_dgZir80mv2hat1rK4Qg|9QCQnNlevES8cYwHZWzrJw|DbkcRuTlnUyPXPPQso3L6g|CJZtcewtvUKqJH-rIUoR6g|xMRsXDsvDUiylTlFq6Tv-w|RmItBW75JUG7Iish-KRw1g|lKSYua82uE-YeMAzxqfrMg|Bopzxr31bkWJrFqO6o1oxg|KhiJGiHzvkiHXvkk9fEUOg|vFCY7egZskyaH-0jwDdQTQ|uxYuSzlDzU-AYGK-uo3RTg|VANy1bANREmh2DLujlaKGQ|CyO7saVd8kWHwzs3MaLe5w|71tayZrmnk6E8E-z8IWeIw|5H2xaOfutEyeZ755pY0mmA|uPHRl846Xk-SG-HEmITW-w|DF4Ub1Vqg0GSBIGWYJgCGw|-LfywKHkMEqRb1WVNSkcxg|AgO2o_kbG0GVTNVNT0233A|R6oXNiKgIkqjGhp-42inxg|WI1S5jo5-kOb5bgoNSb8Hg|MVsyPrQ8iUa_M6DJ4IR0CQ|6iDQYEk4yUa6B_-z49KYHA|MznlCejhZUqq_rxf4xrmwg|kG4B-4Pt6ECSxm14f3FWiA|T9GJpphYiUGYVgw7lmkmDA" | split: "|" -%}
|
||||
{%- assign series_ids = "fpgE1kmfr0-xNBMbxUWxMA|FP7IiNghSEeZLBW21kkAgw|ca1OWFTvMU2H-lHpjsm5MQ|aK4djQBz_UCObjyPq5At_A" | split: "|" -%}
|
||||
{%- assign series_details = "FP7IiNghSEeZLBW21kkAgw,Advent Readings,2019/advent-readings.html|aK4djQBz_UCObjyPq5At_A,Twisted Scripture,2017/twisted-scripture.html|ca1OWFTvMU2H-lHpjsm5MQ,The “3:16”s of the New Testament,2007/the-316s-of-the-new-testament.html" | split: "|" -%}
|
@ -1,154 +0,0 @@
|
||||
{% include_template "_books" %}
|
||||
<div class="content">
|
||||
{%- if is_category or is_tag %}
|
||||
<h1 class="index-title">{{ page_title }}</h1>
|
||||
{%- if is_category %}
|
||||
{%- assign cat = categories | where: "slug", slug | first -%}
|
||||
{%- if cat.description %}<h2 class="index-title">{{ cat.description.value }}</h2>{% endif -%}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{% for post in model.posts %}
|
||||
<article class="item">
|
||||
<h1 class="item-heading">
|
||||
<a href="{{ post.permalink | relative_link }}"
|
||||
title="Permanent Link to “{{ post.title | strip_html | escape_once }}”">
|
||||
{{ post.title }}
|
||||
</a>
|
||||
</h1>
|
||||
<p class="item-meta">
|
||||
<i class="fa fa-calendar" title="Date"></i> {{ post.published_on | date: "dddd, MMMM d, yyyy" }}
|
||||
{% if logged_on %} • <a href="{{ post | edit_post_link }}">Edit Post</a>{% endif %}
|
||||
</p>
|
||||
{%- assign media = post.metadata | value: "episode_media_file" -%}
|
||||
{%- unless media == "-- episode_media_file not found --" %}
|
||||
<aside class="podcast">
|
||||
<p class="text-center"><strong>Listen While<br>You Read</strong></p>
|
||||
<audio controls onplaying="awftw.countPlay('{{ media }}')">
|
||||
<source src="https://files.bitbadger.solutions/devotions/{{ media }}">
|
||||
</audio>
|
||||
<p class="text-center">
|
||||
<a class="dl" href="https://pdcst.click/c/awftw/files.bitbadger.solutions/devotions/{{ media }}" download>
|
||||
<i class="fa fa-download" title="Download Audio"></i>
|
||||
</a>
|
||||
</p>
|
||||
</aside>
|
||||
{%- endunless %}
|
||||
{{ post.text }}
|
||||
</article>
|
||||
{% endfor %}
|
||||
<nav aria-label="pagination">
|
||||
<div>{% if model.newer_link %}<a href="{{ model.newer_link.value }}">« Newer Posts</a>{% endif %}</div>
|
||||
<div>{% if model.older_link %}<a href="{{ model.older_link.value }}">Older Posts »</a>{% endif %}</div>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="sidebar">
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Author</h4>
|
||||
<p>Daniel is a man who wants to be used of God however He sees fit.</p>
|
||||
<hr class="sidebar-sep">
|
||||
<ul>
|
||||
<li><a href="https://daniel.summershome.org/my-testimony">Daniel’s Testimony</a></li>
|
||||
<li><a href="{{ "2007/about-daniels-weekly-devotions.html" | relative_link }}">About This Site</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Series</h4>
|
||||
{%- for series in series_details %}
|
||||
{% assign parts = series | split: "," %}
|
||||
{% assign cat = categories | where: "id", parts[0] | first %}
|
||||
<h4 class="text-center">{{ cat.name }}</h4>
|
||||
<p class="text-center">
|
||||
<a href="{{ parts[2] | relative_link }}"
|
||||
title="About the series “{{ cat.name | escape_once }}” • A Word from the Word">
|
||||
About the Series
|
||||
</a> •
|
||||
<a href="{{ cat | category_link }}">Read All</a> <small class="count">({{ cat.post_count }})</small>
|
||||
</p>
|
||||
{% unless forloop.last %}<hr class="sidebar-sep">{% endunless %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Books</h4>
|
||||
<p>Each devotion is categorized under the books of the Bible which are referenced within it.</p>
|
||||
<hr class="sidebar-sep">
|
||||
<h4 class="text-center">Old Testament</h4>
|
||||
<p class="text-center">
|
||||
{%- assign cat_id = ot_books | first -%}
|
||||
{%- assign cat = categories | where: "id", cat_id | first -%}
|
||||
<a href="{{ cat | category_link }}"
|
||||
{%- if cat.description %} title="{{ cat.description.value | escape_once }}"{% endif %}>
|
||||
See All
|
||||
</a> <small class="count">({{ cat.post_count }})</small>
|
||||
</p>
|
||||
<ul>
|
||||
{%- assign first_time = true -%}
|
||||
{% for cat_id in ot_books -%}
|
||||
{%- if first_time -%}
|
||||
{%- assign first_time = false -%}
|
||||
{%- else %}
|
||||
{%- assign cat = categories | where: "id", cat_id | first -%}
|
||||
<li>
|
||||
{% if cat.post_count == 0 -%}
|
||||
<span{% if cat.description %} title="{{ cat.description.value | escape_once }}"{% endif %}>
|
||||
{{ cat.name }}
|
||||
</span> <small class="count">(0)</small>
|
||||
{%- else -%}
|
||||
<a href="{{ cat | category_link }}"
|
||||
{%- if cat.description %} title="{{ cat.description.value | escape_once }}"{% endif %}>
|
||||
{{ cat.name }}
|
||||
</a> <small class="count">({{ cat.post_count }})</small>
|
||||
{%- endif %}
|
||||
</li>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
<hr class="sidebar-sep">
|
||||
<h4 class="text-center">New Testament</h4>
|
||||
<p class="text-center">
|
||||
{%- assign cat_id = nt_books | first -%}
|
||||
{%- assign cat = categories | where: "id", cat_id | first -%}
|
||||
<a href="{{ cat | category_link }}"
|
||||
{%- if cat.description %} title="{{ cat.description.value | escape_once }}"{% endif %}>
|
||||
See All
|
||||
</a> <small class="count">({{ cat.post_count }})</small>
|
||||
</p>
|
||||
<ul>
|
||||
{%- assign first_time = true -%}
|
||||
{%- for cat_id in nt_books -%}
|
||||
{%- if first_time -%}
|
||||
{%- assign first_time = false -%}
|
||||
{%- else %}
|
||||
{%- assign cat = categories | where: "id", cat_id | first -%}
|
||||
<li>
|
||||
{% if cat.post_count == 0 -%}
|
||||
<span{% if cat.description %} title="{{ cat.description.value | escape_once }}"{% endif %}>
|
||||
{{ cat.name }}
|
||||
</span> <small class="count">(0)</small>
|
||||
{%- else -%}
|
||||
<a href="{{ cat | category_link }}"
|
||||
{%- if cat.description %} title="{{ cat.description.value | escape_once }}"{% endif %}>
|
||||
{{ cat.name }}
|
||||
</a> <small class="count">({{ cat.post_count }})</small>
|
||||
{%- endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Topics</h4>
|
||||
<ul>
|
||||
{%- for cat in categories -%}
|
||||
{%- unless ot_books contains cat.id or nt_books contains cat.id or series_ids contains cat.id -%}
|
||||
<li>
|
||||
{%- for it in cat.parent_names %} {% endfor -%}
|
||||
<a href="{{ cat | category_link }}"
|
||||
{%- if cat.description %} title="{{ cat.description.value | escape_once }}"{% endif %}>
|
||||
{{ cat.name }}
|
||||
</a> <small class="count">({{ cat.post_count }})</small>
|
||||
</li>
|
||||
{% endunless %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
@ -1,47 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>
|
||||
{%- if is_home -%}
|
||||
{{ web_log.name }}{% if web_log.subtitle %} | {{ web_log.subtitle.value }}{% endif %}
|
||||
{%- else -%}
|
||||
{{ page_title | strip_html }}{% if page_title and page_title != "" %} » {% endif %}{{ web_log.name }}
|
||||
{%- endif -%}
|
||||
</title>
|
||||
<link rel="preload" href="https://fonts.googleapis.com/css?family=Quicksand|Federo|Istok+Web" as="style">
|
||||
<link rel="preload" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" as="style">
|
||||
<link rel="preload" href="{{ "style.css" | theme_asset }}" as="style">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
|
||||
{% page_head -%}
|
||||
</head>
|
||||
<body {% comment %} hx-boost="true" hx-target="main" hx-indicator="#loadOverlay" hx-swap="innerHTML show:body:top" {% endcomment %}>
|
||||
<header class="site-header">
|
||||
<p class="site-title"><a class="navbar-brand" href="{{ "" | relative_link }}">{{ web_log.name }}</a></p>
|
||||
{%- if web_log.subtitle %}<p class="site-subtitle">{{ web_log.subtitle.value }}</p>{% endif -%}
|
||||
<p class="site-page">
|
||||
<a href="{{ "gods-simple-plan-of-salvation/" | relative_link }}">God’s Simple Plan of Salvation</a>
|
||||
</p>
|
||||
<p class="site-page"><a href="{{ "podcast/" | relative_link }}">Podcast</a></p>
|
||||
</header>
|
||||
{% comment %}<div class="load-overlay" id="loadOverlay"><h1>Loading...</h1></div>{% endcomment %}
|
||||
<main class="container">{{ content }}</main>
|
||||
<footer class="part-1"></footer>
|
||||
<footer class="page-footer">
|
||||
Designed by
|
||||
<a href="https://bitbadger.solutions" title="Building the site you need to ensure your success!" target="_blank"
|
||||
rel="noopener">
|
||||
Bit Badger Solutions
|
||||
</a> • Powered by
|
||||
<a href="https://github.com/bit-badger/myWebLog/tree/v2" target="_blank" rel="noopener">myWebLog</a> •
|
||||
{% if logged_on %}
|
||||
<a href="{{ "admin/dashboard" | relative_link }}">Dashboard</a>
|
||||
{% else %}
|
||||
<a href="{{ "user/log-on" | relative_link }}">Log On</a>
|
||||
{%- endif %}
|
||||
</footer>
|
||||
{% page_foot %}
|
||||
</body>
|
||||
</html>
|
@ -1,7 +0,0 @@
|
||||
<div class="content">
|
||||
<article class="item">
|
||||
<h1 class="item-heading">{{ page.title }}</h1>
|
||||
{%- if logged_on %}<p class="item-meta"><a href="{{ page | edit_page_link }}">Edit Page</a></p>{% endif %}
|
||||
{{ page.text }}
|
||||
</article>
|
||||
</div>
|
@ -1,173 +0,0 @@
|
||||
{% include_template "_books" %}
|
||||
{%- assign post = model.posts | first -%}
|
||||
<div class="content">
|
||||
<article class="item">
|
||||
<h1 class="item-heading">{{ post.title }}</h1>
|
||||
{%- if logged_on %}<p class="item-meta"><a href="{{ post | edit_post_link }}">Edit Post</a></p>{% endif %}
|
||||
{% assign media = post.metadata | value: "episode_media_file" %}
|
||||
{%- unless media == "-- episode_media_file not found --" %}
|
||||
<aside class="podcast">
|
||||
<p class="text-center"><strong>Listen While<br>You Read</strong></p>
|
||||
<audio controls onplaying="awftw.countPlay('{{ media }}')">
|
||||
<source src="https://files.bitbadger.solutions/devotions/{{ media }}">
|
||||
</audio>
|
||||
<p class="text-center">
|
||||
<a class="dl" href="https://pdcst.click/c/awftw/files.bitbadger.solutions/devotions/{{ media }}" download>
|
||||
<i class="fa fa-download" title="Download Audio"></i>
|
||||
</a>
|
||||
</p>
|
||||
</aside>
|
||||
{%- endunless %}
|
||||
{{ post.text }}
|
||||
</article>
|
||||
<nav aria-label="pagination">
|
||||
<div class="nav-previous">
|
||||
{% if model.newer_link %}
|
||||
<span class="post-nav">Previous Post</span><br>
|
||||
<a class="post-nav-title" href="{{ model.newer_link.value | relative_link }}">{{ model.newer_name.value }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="nav-next">
|
||||
{% if model.older_link %}
|
||||
<span class="post-nav">Next Post</span> <br>
|
||||
<a class="post-nav-title" href="{{ model.older_link.value | relative_link }}">{{ model.older_name.value }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="sidebar post-sidebar">
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Date</h4>
|
||||
<div class="date-posted">
|
||||
<i class="fa fa-calendar" title="Date" aria-hidden="true"></i>
|
||||
{{ post.published_on | date: "dddd, MMMM d, yyyy" }}
|
||||
</div>
|
||||
</div>
|
||||
{%- assign has_ot = false -%}
|
||||
{%- for ot_id in ot_books -%}
|
||||
{%- if post.category_ids contains ot_id %}{% assign has_ot = true %}{% endif -%}
|
||||
{%- endfor -%}
|
||||
{%- assign has_nt = false -%}
|
||||
{%- for nt_id in nt_books -%}
|
||||
{%- if post.category_ids contains nt_id %}{% assign has_nt = true %}{% endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if has_ot or has_nt %}
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Scripture</h4>
|
||||
{%- if has_ot %}
|
||||
<ul>
|
||||
<li>
|
||||
{%- assign cat_id = ot_books | first -%}
|
||||
{%- assign cat = categories | where: "id", cat_id | first -%}
|
||||
<i class="fa fa-book" title="Book" aria-hidden="true"></i>
|
||||
<a href="{{ cat | category_link }}"
|
||||
{%- if cat.description %} title="{{ cat.description.value | escape_once }}"{% endif %}>
|
||||
{{ cat.name }}
|
||||
</a>
|
||||
<ul>
|
||||
{%- assign first_time = true -%}
|
||||
{%- for cat_id in ot_books %}
|
||||
{%- if first_time %}
|
||||
{%- assign first_time = false -%}
|
||||
{%- elsif post.category_ids contains cat_id %}
|
||||
{% assign cat = categories | where: "id", cat_id | first %}
|
||||
<li>
|
||||
<i class="fa fa-book" title="Book" aria-hidden="true"></i>
|
||||
<a href="{{ cat | category_link }}"
|
||||
{%- if cat.description %} title="{{ cat.description.value | escape_once }}"{% endif %}>
|
||||
{{ cat.name }}
|
||||
</a>
|
||||
</li>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{%- endif %}
|
||||
{%- if has_nt %}
|
||||
<ul>
|
||||
<li>
|
||||
{%- assign cat_id = nt_books | first -%}
|
||||
{%- assign cat = categories | where: "id", cat_id | first -%}
|
||||
<i class="fa fa-book" title="Book" aria-hidden="true"></i>
|
||||
<a href="{{ cat | category_link }}"
|
||||
{%- if cat.description %} title="{{ cat.description.value | escape_once }}"{% endif %}>
|
||||
{{ cat.name }}
|
||||
</a>
|
||||
<ul>
|
||||
{%- assign first_time = true -%}
|
||||
{%- for cat_id in nt_books %}
|
||||
{%- if first_time %}
|
||||
{%- assign first_time = false -%}
|
||||
{%- elsif post.category_ids contains cat_id %}
|
||||
{% assign cat = categories | where: "id", cat_id | first %}
|
||||
<li>
|
||||
<i class="fa fa-book" title="Book" aria-hidden="true"></i>
|
||||
<a href="{{ cat | category_link }}"
|
||||
{%- if cat.description %} title="{{ cat.description.value | escape_once }}"{% endif %}>
|
||||
{{ cat.name }}
|
||||
</a>
|
||||
</li>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{%- endif %}
|
||||
</div>
|
||||
{%- endif %}
|
||||
{%- assign in_series = false -%}
|
||||
{%- for series_id in series_ids %}
|
||||
{%- if post.category_ids contains series_id -%}
|
||||
{%- assign in_series = true -%}
|
||||
{%- assign series_cat = categories | where: "id", series_id | first -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if in_series %}
|
||||
<div class="item text-center">
|
||||
<h4 class="item-heading">Series</h4>
|
||||
<a href="{{ series_cat | category_link }}"
|
||||
{% if series_cat.description %} title="{{ series_cat.description.value | escape_once }}"{% endif %}>
|
||||
{{ series_cat.name }}
|
||||
</a>
|
||||
</div>
|
||||
{%- endif %}
|
||||
{%- assign has_topics = false -%}
|
||||
{% for cat_id in post.category_ids -%}
|
||||
{%- unless ot_books contains cat_id or nt_books contains cat_id or series_ids contains cat_id -%}
|
||||
{% assign has_topics = true -%}
|
||||
{%- endunless -%}
|
||||
{% endfor %}
|
||||
{%- if has_topics %}
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Topics</h4>
|
||||
<ul>
|
||||
{%- for cat_id in post.category_ids %}
|
||||
{% unless ot_books contains cat_id or nt_books contains cat_id or series_ids contains cat_id %}
|
||||
{%- assign cat = categories | where: "id", cat_id | first -%}
|
||||
<li>
|
||||
<i class="fa fa-folder-open-o" title="Category" aria-hidden="true"></i>
|
||||
<a href="{{ cat | category_link }}" rel="tag" title="Categorized under {{ cat.name | escape }}">
|
||||
{{ cat.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endunless %}
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{%- endif %}
|
||||
{%- assign tag_count = post.tags | size -%}
|
||||
{%- if tag_count > 0 %}
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Tags</h4>
|
||||
<ul>
|
||||
{%- for tag in post.tags %}
|
||||
<li>
|
||||
<a href="{{ tag | tag_link }}" title="Tagged “{{ tag }}”">
|
||||
<i class="fa fa-tag" aria-hidden="true"></i> {{ tag }}
|
||||
</a>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{%- endif %}
|
||||
</div>
|
@ -1,131 +0,0 @@
|
||||
<div class="home">
|
||||
<article class="content auto">
|
||||
{{ page.text }}
|
||||
{% if logged_on -%}
|
||||
<p><small><a hx-boost="false" href="{{ page | edit_page_link }}">Edit This Page</a></small></p>
|
||||
{% endif %}
|
||||
</article>
|
||||
<aside class="app-sidebar">
|
||||
<div>
|
||||
<div class="app-sidebar-head">Web Sites and Applications</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>PrayerTracker</strong><br>
|
||||
<a href="{{ "solutions/prayer-tracker" | relative_link }}"
|
||||
title="About PrayerTracker • Bit Badger Solutions">
|
||||
About
|
||||
</a> •
|
||||
<a href="https://prayer.bitbadger.solutions" title="PrayerTracker" target="_blank" rel="noopener">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">
|
||||
A prayer request tracking website (Free for any church or Sunday School class!)
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>myPrayerJournal</strong><br>
|
||||
<a href="{{ "solutions/my-prayer-journal" | relative_link }}"
|
||||
title="About myPrayerJournal • Bit Badger Solutions">
|
||||
About
|
||||
</a> •
|
||||
<a href="https://prayerjournal.me" title="myPrayerJournal" target="_blank" rel="noopener">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Minimalist personal prayer journal</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Linux Resources</strong><br>
|
||||
<a href="https://blog.bitbadger.solutions/linux/" title="Linux Resources" target="_blank" rel="noopener">
|
||||
Visit
|
||||
</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Handy information for Linux folks</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="app-sidebar-head">WordPress</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Futility Closet</strong><br>
|
||||
<a href="{{ "solutions/futility-closet" | relative_link }}"
|
||||
title="About Futility Closet • Bit Badger Solutions">
|
||||
About
|
||||
</a> •
|
||||
<a href="https://www.futilitycloset.com" title="Futility Closet" target="_blank" rel="noopener">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">An idler’s miscellany of compendious amusements</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Mindy Mackenzie</strong><br>
|
||||
<a href="{{ "solutions/mindy-mackenzie" | relative_link }}"
|
||||
title="About Mindy Mackenzie • Bit Badger Solutions">
|
||||
About
|
||||
</a> •
|
||||
<a href="https://mindymackenzie.com" title="Mindy Mackenzie" target="_blank" rel="noopener">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description"><em>WSJ</em>-best-selling author of <em>The Courage Solution</em></p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Riehl World News</strong><br>
|
||||
<a href="{{ "solutions/riehl-world-news" | relative_link }}"
|
||||
title="About Riehl World News • Bit Badger Solutions">
|
||||
About
|
||||
</a> •
|
||||
<a href="http://riehlworldview.com" title="Riehl World News" target="_blank" rel="noopener">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Riehl news for real people</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="app-sidebar-head">Static Sites</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Bay Vista Baptist Church</strong><br>
|
||||
<a href="{{ "solutions/bay-vista" | relative_link }}"
|
||||
title="About Bay Vista Baptist Church • Bit Badger Solutions">
|
||||
About
|
||||
</a> •
|
||||
<a href="https://bayvista.org" title="Bay Vista Baptist Church" target="_blank" rel="noopener">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Biloxi, Mississippi</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<div class="app-sidebar-head">myWebLog</div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>The Bit Badger Blog</strong><br>
|
||||
<a href="{{ "solutions/tech-blog" | relative_link }}"
|
||||
title="About The Bit Badger Blog • Bit Badger Solutions">
|
||||
About
|
||||
</a> •
|
||||
<a href="https://bitbadger.solutions/blog" title="The Bit Badger Blog" target="_blank" rel="noopener">
|
||||
Visit
|
||||
</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Technical information (“geek stuff”) from Bit Badger Solutions</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="app-sidebar-head">Personal</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Daniel J. Summers</strong><br>
|
||||
<a href="https://daniel.summershome.org" title="Daniel J. Summers" target="_blank" rel="noopener">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Daniel’s personal blog</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>A Word from the Word</strong><br>
|
||||
<a href="https://devotions.summershome.org" title="A Word from the Word" target="_blank" rel="noopener">
|
||||
Visit
|
||||
</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Devotions by Daniel</p>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ page_title }} » Bit Badger Solutions</title>
|
||||
</head>
|
||||
<body>
|
||||
{{ content }}
|
||||
</body>
|
||||
</html>
|
@ -1,49 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>{{ page_title }} » Bit Badger Solutions</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Oswald|Raleway">
|
||||
{% page_head -%}
|
||||
</head>
|
||||
<body hx-boost="true" hx-target="main" hx-indicator="#loadOverlay" hx-swap="innerHTML show:body:top">
|
||||
<header class="site-header">
|
||||
<div class="header-logo">
|
||||
<a href="{{ "" | relative_link }}">
|
||||
<img src="{{ "bitbadger.png" | theme_asset }}"
|
||||
alt="A cartoon badger looking at a computer screen, with his paw on a mouse"
|
||||
title="Bit Badger Solutions">
|
||||
</a>
|
||||
</div>
|
||||
<div class="header-title"><a href="{{ "" | relative_link }}">Bit Badger Solutions</a></div>
|
||||
<div class="header-spacer"> </div>
|
||||
<div class="header-social">
|
||||
<a href="https://twitter.com/Bit_Badger" title="Bit_Badger on Twitter" target="_blank">
|
||||
<img src="{{ "twitter.png" | theme_asset }}" alt="Twitter">
|
||||
</a>
|
||||
<a href="https://www.facebook.com/bitbadger.solutions" title="Bit Badger Solutions on Facebook" target="_blank">
|
||||
<img src="{{ "facebook.png" | theme_asset }}" alt="Facebook">
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="load-overlay" id="loadOverlay"><h1>Loading…</h1></div>
|
||||
<main>{{ content }}</main>
|
||||
<footer hx-boost="false">
|
||||
<div>
|
||||
<small>
|
||||
{% if logged_on -%}
|
||||
<a href="{{ "admin/dashboard" | relative_link }}">Dashboard</a> ~
|
||||
<a href="{{ "user/log-off" | relative_link }}">Log Off</a>
|
||||
{% else %}
|
||||
<a href="{{ "user/log-on" | relative_link }}">Log On</a>
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
<div class="footer-by">
|
||||
A <strong><a href="{{ "" | relative_link }}">Bit Badger Solutions</a></strong> original design
|
||||
</div>
|
||||
</footer>
|
||||
{% page_foot %}
|
||||
</body>
|
||||
</html>
|
@ -1,33 +0,0 @@
|
||||
<article class="content auto">
|
||||
{%- assign parts = page.title | split: ' » ' -%}
|
||||
{%- assign parts_count = parts | size -%}
|
||||
<h1 class="project-title">
|
||||
{% if parts_count == 1 -%}
|
||||
{{ page.title }}<br>
|
||||
<small><small>
|
||||
<a href="https://github.com/bit-badger/{{ page.title }}" target="_blank" rel="noopener"
|
||||
title="{{ parts[0] }} on GitHub">
|
||||
View on GitHub
|
||||
</a>
|
||||
{% if logged_on %} • <a hx-boost="false" href="{{ page | edit_page_link }}">Edit Page</a>{% endif %}
|
||||
</small></small>
|
||||
{%- else -%}
|
||||
<small><small>
|
||||
<a href="." title="{{ parts[0] }} Home">{{ parts[0] }}</a>
|
||||
{% if logged_on %} • <a hx-boost="false" href="{{ page | edit_page_link }}">Edit</a>{% endif %}
|
||||
</small></small>
|
||||
<br>
|
||||
{{ parts[1] }}
|
||||
{%- endif %}
|
||||
</h1>
|
||||
{{ page.text }}
|
||||
{% if parts_count > 1 -%}
|
||||
<p class="project-footer">
|
||||
<a href="." title="{{ parts[0] }} Home">« {{ parts[0] }} Home</a>
|
||||
<a href="https://github.com/bit-badger/{{ parts[0] }}" target="_blank" rel="noopener"
|
||||
title="{{ parts[0] }} on GitHub">
|
||||
View GitHub Repo
|
||||
</a>
|
||||
</p>
|
||||
{%- endif %}
|
||||
</article>
|
@ -1,8 +0,0 @@
|
||||
<article class="content auto">
|
||||
<h1>{{ page.title }}</h1>
|
||||
{{ page.text }}
|
||||
<p><br><a href="{{ "" | relative_link }}" title="Home">« Home</a></p>
|
||||
{% if logged_on -%}
|
||||
<p><small><a hx-boost="false" href="{{ page | edit_page_link }}">Edit This Page</a></small></p>
|
||||
{% endif %}
|
||||
</article>
|
@ -1,112 +0,0 @@
|
||||
<h1 class="solution-header">
|
||||
{{ page.title }}<br>
|
||||
<small><small>
|
||||
{%- assign url = page.metadata | value: "url" -%}
|
||||
{%- assign no_link = page.metadata | value: "no_link" -%}
|
||||
{%- assign archive = page.metadata | where: "name", "archive_url" | size -%}
|
||||
{% if no_link == "true" -%}
|
||||
{{ url }}
|
||||
{%- else -%}
|
||||
<a href="{{ url }}" target="_blank" rel="noopener">{{ url }}</a>
|
||||
{%- endif %}
|
||||
{% if archive > 0 -%}
|
||||
|
||||
<a href="{{ page.metadata | value: "archive_url" }}" target="_blank" rel="noopener"><small>(Archive)</small></a>
|
||||
{%- endif %}
|
||||
</small></small>
|
||||
</h1>
|
||||
<div class="app-info">
|
||||
<article class="content">
|
||||
<aside>
|
||||
{%- capture screen_url %}screenshots/{{ page.permalink | split: "/" | last }}.png{% endcapture -%}
|
||||
<img src="{{ screen_url | theme_asset }}" alt="Screen shot of {{ page.title | escape }}">
|
||||
</aside>
|
||||
{{ page.text }}
|
||||
{%- assign curr_tech = page.metadata | where: "name", "tech" -%}
|
||||
{%- assign past_tech = page.metadata | where: "name", "past_tech" -%}
|
||||
{%- assign curr_count = curr_tech | size -%}
|
||||
{%- assign past_count = past_tech | size -%}
|
||||
{% if curr_count > 0 or past_count > 0 -%}
|
||||
{% comment %} TODO / WIP
|
||||
{% capture all_links -%}
|
||||
ASP.NET MVC|https://dotnet.microsoft.com/apps/aspnet/mvc,
|
||||
Azure|https://azure.microsoft.com/,
|
||||
BlogEngine.NET|http://www.dotnetblogengine.net/,
|
||||
Database Abstraction|https://github.com/danieljsummers/DatabaseAbstraction,
|
||||
Digital Ocean|https://www.digitalocean.com/,
|
||||
Giraffe|https://github.com/giraffe-fsharp/Giraffe,
|
||||
GitHub|https://github.com/,
|
||||
GitHub Pages|https://pages.github.com/,
|
||||
Hexo|https://hexo.io/,
|
||||
Hugo|https://gohugo.io/,
|
||||
Jekyll|https://jekyllrb.com/,
|
||||
MongoDB|https://www.mongodb.com/,
|
||||
MySQL|https://www.mysql.com/,
|
||||
myWebLog|https://github.com/bit-badger/myWebLog,
|
||||
nginx|http://nginx.org/,
|
||||
Orchard|https://orchardproject.net/,
|
||||
PHP|https://www.php.net/,
|
||||
PostgreSQL|https://www.postgresql.org/,
|
||||
Rackspace Cloud|https://www.rackspace.com/cloud,
|
||||
RavenDB|https://ravendb.net/,
|
||||
RethinkDB|https://rethinkdb.com/,
|
||||
SQL Server|https://www.microsoft.com/en-us/sql-server/,
|
||||
Vue.js|https://vuejs.org/,
|
||||
WordPress|https://wordpress.org
|
||||
{%- endcapture %}
|
||||
{% endcomment %}
|
||||
<section>
|
||||
<h3 onclick="toggle('techStack')">
|
||||
The Technology Stack<span id="techStackArrow" class="arrow">▼</span>
|
||||
</h3>
|
||||
<div id="techStack" class="collapse-panel hidden">
|
||||
{% if curr_count > 0 -%}
|
||||
{% if past_count > 0 -%}
|
||||
<p><small><strong>Current:</strong></small></p>
|
||||
{%- endif %}
|
||||
<ul>
|
||||
{% for curr in curr_tech -%}
|
||||
{%- assign tech = curr.value | split: "|" -%}
|
||||
<li>
|
||||
{% comment %} <a v-if="hasLink(tech[0])" :href="techLinks[tech[0]]" target="_blank">{{ tech[0] }}</a> {% endcomment %}
|
||||
{{ tech[0] }} for {{ tech[1] }}
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{%- endif %}
|
||||
{% if past_count > 0 -%}
|
||||
{% if curr_count > 0 %}
|
||||
<p><small><strong>Previously:</strong></small></p>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{% for past in past_tech -%}
|
||||
{%- assign tech = past.value | split: "|" -%}
|
||||
<li>
|
||||
{% comment %} <a v-if="hasLink(tech[0])" :href="techLinks[tech[0]]" target="_blank">{{ tech[0] }}</a> {% endcomment %}
|
||||
{{ tech[0] }} for {{ tech[1] }}
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{%- endif %}
|
||||
<p><br><a href="{{ "solutions" | relative_link }}">« Back to All Solutions</a></p>
|
||||
{% if logged_on -%}
|
||||
<p><small><a hx-boost="false" href="{{ page | edit_page_link }}">Edit This Page</a></small></p>
|
||||
{% endif %}
|
||||
</article>
|
||||
</div>
|
||||
<script>
|
||||
function toggle(id) {
|
||||
const section = document.getElementById(id)
|
||||
const arrow = document.getElementById(`${id}Arrow`)
|
||||
if (section.className.indexOf("shown") === -1) {
|
||||
section.className = `${section.className} shown`
|
||||
arrow.innerHTML = "▲"
|
||||
} else {
|
||||
section.className = section.className.replace(" shown", "")
|
||||
arrow.innerHTML = "▼"
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,84 +0,0 @@
|
||||
<div class="content">
|
||||
{% if is_category or is_tag %}
|
||||
<h2>{{ page_title }}</h2>
|
||||
{%- if subtitle %}
|
||||
<p>{{ subtitle }}</p>
|
||||
{%- endif %}
|
||||
{% endif %}
|
||||
{%- for post in model.posts %}
|
||||
<article class="item">
|
||||
<h1 class="item-heading">
|
||||
<a href="{{ post | relative_link }}"
|
||||
title="Permanent Link to "{{ post.title | strip_html | escape }}"">
|
||||
{{ post.title }}
|
||||
</a>
|
||||
</h1>
|
||||
<h4 class="post-meta">
|
||||
<span title="Published On">
|
||||
<i class="fa fa-calendar"></i> {{ post.published_on | date: "dddd, MMMM d, yyyy" }}
|
||||
</span>
|
||||
<span title="Published At">
|
||||
<i class="fa fa-clock-o"></i> {{ post.published_on | date: "h:mm tt" | downcase }}
|
||||
</span>
|
||||
<span title="Author">
|
||||
<i class="fa fa-user"></i> {{ model.authors | value: post.author_id }}
|
||||
</span>
|
||||
{% if logged_on %}
|
||||
<span>
|
||||
<a hx-boost="false" href="{{ post | edit_post_link }}"><i class="fa fa-pencil-square-o"></i> Edit Post</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</h4>
|
||||
{{ post.text }}
|
||||
</article>
|
||||
{%- endfor %}
|
||||
<nav aria-label="pagination">
|
||||
<ul class="pager">
|
||||
{% if model.newer_link -%}
|
||||
<li class="previous item"><a href="{{ model.newer_link.value }}">« Newer Posts</a></li>
|
||||
{%- else -%}
|
||||
<li></li>
|
||||
{% endif %}
|
||||
{% if model.older_link -%}
|
||||
<li class="next item"><a href="{{ model.older_link.value }}">Older Posts »</a></li>
|
||||
{%- endif -%}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="sidebar">
|
||||
<br>
|
||||
<div class="item votd-item">
|
||||
<h4 class="item-heading votd-heading">Verse of the Day</h4>
|
||||
<div>
|
||||
<span class="verse"></span> –
|
||||
<a class="votd-reference" target="_blank" rel="noopener"></a>
|
||||
<small> <a class="version-link" target="_blank" rel="noopener">(ESV)</a></small><br>
|
||||
<div class="votd-credits">
|
||||
<small>Powered by <a href="https://biblegateway.com" target="_blank" rel="noopener">Bible Gateway</a></small>
|
||||
</div>
|
||||
<script src="https://www.biblegateway.com/votd/get/?format=json&version=ESV&callback=djs.displayVotd">
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<h4 class="item-heading">#VoteGold</h4>
|
||||
<div class="text-center">
|
||||
<p><em>
|
||||
(Don't blame me;<br>
|
||||
I voted for <a href="https://jo20.com/" title="Jorgensen / Cohen 2020" target="_blank" rel="noopener">Jo</a>)
|
||||
</em></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Categories</h4>
|
||||
<ul class="cat-list">
|
||||
{% for cat in categories -%}
|
||||
{%- assign indent = cat.parent_names | size -%}
|
||||
<li class="cat-list-item"{% if indent > 0 %} style="padding-left:{{ indent }}rem;"{% endif %}>
|
||||
<a href="{{ cat | category_link }}" class="cat-list-link">{{ cat.name }}</a>
|
||||
<span class="cat-list-count">{{ cat.post_count }}</span>
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>
|
||||
{%- if is_home -%}
|
||||
{{ web_log.name }}{% if web_log.subtitle %} | {{ web_log.subtitle.value }}{% endif %}
|
||||
{%- else -%}
|
||||
{{ page_title | strip_html }}{% if page_title and page_title != "" %} » {% endif %}{{ web_log.name }}
|
||||
{%- endif -%}
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
{{ content }}
|
||||
</body>
|
||||
</html>
|
@ -1,151 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>
|
||||
{%- if is_home -%}
|
||||
{{ web_log.name }}{% if web_log.subtitle %} | {{ web_log.subtitle.value }}{% endif %}
|
||||
{%- else -%}
|
||||
{{ page_title | strip_html }}{% if page_title and page_title != "" %} » {% endif %}{{ web_log.name }}
|
||||
{%- endif -%}
|
||||
</title>
|
||||
<link rel="preload" href="https://fonts.googleapis.com/css?family=Quicksand|Oswald" as="style">
|
||||
<link rel="preload" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" as="style">
|
||||
<link rel="preload" href="{{ "style.css" | theme_asset }}" as="style">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
|
||||
<script src="{{ "djs.js" | theme_asset }}"></script>
|
||||
{% page_head -%}
|
||||
</head>
|
||||
<body hx-boost="true" hx-target="main" hx-indicator="#loadOverlay" hx-swap="innerHTML show:body:top">
|
||||
<nav class="site-header" role="navigation">
|
||||
<p><a class="nav-home" href="{{ "" | relative_link }}">{{ web_log.name }}</a></p>
|
||||
{%- if web_log.subtitle %}<p>{{ web_log.subtitle.value }}</p>{% endif -%}
|
||||
<p class="nav-spacer"></p>
|
||||
{%- for page in page_list %}
|
||||
<p class="desktop"><a href="{{ page | relative_link }}">{{ page.title }}</a></p>
|
||||
{%- endfor %}
|
||||
<p class="desktop">
|
||||
<a href="https://devotions.summershome.org" target="_blank" rel="noopener">A Word from the Word</a>
|
||||
</p>
|
||||
<p class="mobile"><a href="#links">Site Links</a></p>
|
||||
</nav>
|
||||
<div class="load-overlay" id="loadOverlay"><h1>Loading...</h1></div>
|
||||
<main>{{ content }}</main>
|
||||
<footer class="part-1" id="links">
|
||||
{%- for page in page_list %}
|
||||
<p class="mobile"><a href="{{ page | relative_link }}">{{ page.title }}</a></p>
|
||||
{%- endfor %}
|
||||
<p class="mobile">
|
||||
<a href="https://devotions.summershome.org" target="_blank" rel="noopener">A Word from the Word</a>
|
||||
</p>
|
||||
</footer>
|
||||
<footer class="part-2">
|
||||
<div>
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Tip Jar</h4>
|
||||
<div class="text-center">
|
||||
<p style="padding: 5px 0;">What do we holler?<br>“Thanks for the dollar!”</p>
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
<input type="hidden" name="encrypted"
|
||||
value="-----BEGIN PKCS7-----MIIHXwYJKoZIhvcNAQcEoIIHUDCCB0wCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYCdeo4jz/VlsdAgHXG/9uV7zVyd+OWxLhPx/nGdCcANMelbjAUPFj7a9/cisuUG0hyH//qJWdOptVIjKrWrcyC6fNZxqPezHfmw7oNogLmMpVmpT771cGD4YkrB/okzs8KyDBGxJ/HW9kXXoQtZXFmz/Pu9Z9XQSAtFw5e4qmoF/DELMAkGBSsOAwIaBQAwgdwGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIJlQtvH21m3CAgbhQUNrfjKQIQuNDYkPZH7BUhnjpmratPAdk18397qsJ7y/loyL64E8u8G8AHo6T+uA+ANibr5h2EypKbcYaFQKjBg1o/jdDcRaznIySw6d8uzOZMS4U/lSeaWKH3I5H2LYzfBO9upEhnBDJt5Hxns1rSKnkC+WOrfdJJdgSTXIsiVKfarXRQnm+AeFeXlgiwAv+2S3K+SgGZuEeDxsgMpo2lyYFJmE1xcjMeHtCU9P6lum8VCKEpXaMoIIDhzCCA4MwggLsoAMCAQICAQAwDQYJKoZIhvcNAQEFBQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0MDIxMzEwMTMxNVoXDTM1MDIxMzEwMTMxNVowgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBR07d/ETMS1ycjtkpkvjXZe9k+6CieLuLsPumsJ7QC1odNz3sJiCbs2wC0nLE0uLGaEtXynIgRqIddYCHx88pb5HTXv4SZeuv0Rqq4+axW9PLAAATU8w04qqjaSXgbGLP3NmohqM6bV9kZZwZLR/klDaQGo1u9uDb9lr4Yn+rBQIDAQABo4HuMIHrMB0GA1UdDgQWBBSWn3y7xm8XvVk/UtcKG+wQ1mSUazCBuwYDVR0jBIGzMIGwgBSWn3y7xm8XvVk/UtcKG+wQ1mSUa6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCBXzpWmoBa5e9fo6ujionW1hUhPkOBakTr3YCDjbYfvJEiv/2P+IobhOGJr85+XHhN0v4gUkEDI8r2/rNk1m0GA8HKddvTjyGw/XqXa+LSTlDYkqI8OwR8GEYj4efEtcRpRYBxV8KxAW93YDWzFGvruKnnLbDAF6VR5w/cCMn5hzGCAZowggGWAgEBMIGUMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTIwNjE1MDA1MjMxWjAjBgkqhkiG9w0BCQQxFgQUe9ggMne5iX9doWN/3fDOuOmLgPswDQYJKoZIhvcNAQEBBQAEgYAphf2/hGBj64tOF4Q25S7rf2e7VO6aUHmT7oimydTWVKg3tWK0avKTuG6OS5GMw8gVv2GSrqHLz4KPBdjEDKu3y2WwrfNYaMKeQU1eRPquE4C+f6xcEf5RxqelUHoPUjiU46grnu51MPl+jQf3PERmbkK3N0hOax8QBx+N7gR1mQ==-----END PKCS7-----">
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" style="border:0;"
|
||||
name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||
<img src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" alt="" style="border:0;" width="1"
|
||||
height="1">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Let’s Go Geocaching</h4>
|
||||
<div class="text-center">
|
||||
<a href="https://www.geocaching.com/guide/" title="What Is Geocaching?" target="_blank" rel="noopener">
|
||||
<img src="https://img.geocaching.com/stats/img.aspx?txt=I+am+the+search+engine!&uid=61a3c13b-9dbb-41d2-a695-9e3432d4d5ea"
|
||||
alt="My Geocaching Stats" title="What Is Geocaching?" style="border:0;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Tennessee Football</h4>
|
||||
<div class="football-panel">
|
||||
<span>
|
||||
<a href="https://utsports.com" style="background:none;padding:0;" target="_blank" rel="noopener">
|
||||
<img src="{{ "tennessee.png" | theme_asset }}" alt="T" title="Tennessee Sports" style="border:0;"
|
||||
width="75px">
|
||||
</a>
|
||||
</span>
|
||||
<div>
|
||||
2021 Season — <strong>NR</strong><br>
|
||||
<small>
|
||||
(7-6 • 4-4 SEC/3<sup>rd</sup> East)<br><br>
|
||||
Last — L* (45-48) vs. Purdue<br>
|
||||
<em>Music City Bowl</em>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Colorado State Football</h4>
|
||||
<div class="football-panel">
|
||||
<span>
|
||||
<a href="https://csurams.com" style="background:none;padding:0;" target="_blank" rel="noopener">
|
||||
<img src="{{ "csurams.png" | theme_asset }}" alt="CSU Rams Logo"
|
||||
title="Colorado State Sports" style="border:0;" height="75px" width="75px">
|
||||
</a>
|
||||
</span>
|
||||
<div>
|
||||
2021 Season — <strong>NR</strong><br>
|
||||
<small>
|
||||
(3-9 • 2-6 MWC/5<sup>th</sup> Mountain)<br><br>
|
||||
Last — L (10-52) at Nevada
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="item">
|
||||
<h4 class="item-heading">Some Other Places to Find Me</h4>
|
||||
<div class="text-center">
|
||||
<a class="twitter-timeline" href="https://twitter.com/DanJSum" data-height="300">Tweets by DanJSum</a>
|
||||
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
<br><br>
|
||||
<a href="https://www.facebook.com/daniel.j.summers" title="Daniel J. Summers’s Facebook profile">
|
||||
<img src="https://badge.facebook.com/badge/1270539383.37.619606444.png" style="border:0;"
|
||||
alt="Daniel J. Summers’s Facebook profile">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<footer class="part-3">
|
||||
<div class="pull-left">
|
||||
<a rel="license noopener" href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank">
|
||||
<img src="https://licensebuttons.net/l/by-nc/4.0/88x31.png" alt="Creative Commons License"
|
||||
style="border-width:0;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="copy" hx-boost="false">
|
||||
Licensed by the
|
||||
<a rel="license noopener" href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank">
|
||||
Creative Commons Attribution-NonCommercial 4.0 International License
|
||||
</a><br>
|
||||
Designed by
|
||||
<a href="https://bitbadger.solutions" title="Building the site you need to ensure your success!">
|
||||
Bit Badger Solutions
|
||||
</a>
|
||||
• Powered by <a href="https://github.com/bit-badger/myWebLog/tree/v2">myWebLog</a> •
|
||||
{% if logged_on %}
|
||||
<a href="{{ "admin/dashboard" | relative_link }}">Dashboard</a>
|
||||
{% else %}
|
||||
<a href="{{ "user/log-on" | relative_link }}">Log On</a>
|
||||
{%- endif %}
|
||||
</div>
|
||||
</footer>
|
||||
{% page_foot %}
|
||||
</body>
|
||||
</html>
|
@ -1,6 +0,0 @@
|
||||
<div class="content single">
|
||||
<article class="item">
|
||||
<h1 class="item-heading">{{ page.title }}</h1>
|
||||
{{ page.text }}
|
||||
</article>
|
||||
</div>
|
@ -1,77 +0,0 @@
|
||||
{%- assign post = model.posts | first -%}
|
||||
<div class="content single">
|
||||
<article class="item">
|
||||
<h1 class="item-heading">{{ post.title }}</h1>
|
||||
<h4 class="post-meta">
|
||||
{% if post.published_on -%}
|
||||
<span title="Published On">
|
||||
<i class="fa fa-calendar"></i> {{ post.published_on | date: "dddd, MMMM d, yyyy" }}
|
||||
</span>
|
||||
<span title="Published At">
|
||||
<i class="fa fa-clock-o"></i> {{ post.published_on | date: "h:mm tt" | downcase }}
|
||||
</span>
|
||||
{%- else -%}
|
||||
<span>**DRAFT**</span>
|
||||
{% endif %}
|
||||
<span title="Author"><i class="fa fa-user"></i> {{ model.authors | value: post.author_id }}</span>
|
||||
{% if logged_on %}
|
||||
<span>
|
||||
<a hx-boost="false" href="{{ post | edit_post_link }}">
|
||||
<i class="fa fa-pencil-square-o"></i> Edit Post
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</h4>
|
||||
<div>{{ post.text }}</div>
|
||||
{%- assign cat_count = post.category_ids | size -%}
|
||||
{% if cat_count > 0 -%}
|
||||
<h4>
|
||||
Categorized
|
||||
{% for cat_id in post.category_ids -%}
|
||||
{% assign cat = categories | where: "id", cat_id | first %}
|
||||
<span class="no-wrap">
|
||||
<i class="fa fa-folder-open-o" title="Category"></i>
|
||||
<a href="{{ cat | category_link }}" title="Categorized under “{{ cat.name | escape }}”">
|
||||
{{ cat.name }}
|
||||
</a>
|
||||
</span>
|
||||
{%- endfor %}
|
||||
</h4>
|
||||
{%- endif %}
|
||||
{%- assign tag_count = post.tags | size -%}
|
||||
{% if tag_count > 0 -%}
|
||||
<h4>
|
||||
Tagged
|
||||
{% for tag in post.tags %}
|
||||
<span class="no-wrap">
|
||||
<a href="{{ tag | tag_link }}" title="Posts tagged “{{ tag | escape }}”" rel="tag">
|
||||
<i class="fa fa-tag"></i> {{ tag }}
|
||||
</a>
|
||||
</span>
|
||||
{%- endfor %}
|
||||
</h4>
|
||||
{%- endif %}
|
||||
</article>
|
||||
<div>
|
||||
<nav aria-label="pagination">
|
||||
<ul class="pager">
|
||||
{% if model.newer_link -%}
|
||||
<li class="previous item">
|
||||
<h4 class="item-heading">
|
||||
<a href="{{ model.newer_link.value | relative_link }}">«</a> Previous Post
|
||||
</h4>
|
||||
<a href="{{ model.newer_link.value | relative_link }}">{{ model.newer_name.value }}</a>
|
||||
</li>
|
||||
{%- else -%}
|
||||
<li></li>
|
||||
{% endif %}
|
||||
{% if model.older_link -%}
|
||||
<li class="next item">
|
||||
<h4 class="item-heading">Next Post <a href="{{ model.older_link.value | relative_ling }}">»</a></h4>
|
||||
<a href="{{ model.older_link.value | relative_link }}">{{ model.older_name.value }}</a>
|
||||
</li>
|
||||
{%- endif -%}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
@ -1,56 +0,0 @@
|
||||
{% if is_category or is_tag %}
|
||||
<h1>{{ page_title }}</h1>
|
||||
{%- if subtitle %}
|
||||
<p>{{ subtitle }}</p>
|
||||
{%- endif %}
|
||||
{% endif %}
|
||||
{%- for post in model.posts %}
|
||||
<article class="auto">
|
||||
<div>
|
||||
<h1 class="home-title">
|
||||
<small class="home-lead">{{ post.published_on | date: "dddd, MMMM d, yyyy" }}</small><br>
|
||||
|
||||
<a href="{{ post | relative_link }}"
|
||||
id="{{ post.title | strip_html | downcase | replace: " ", "-" | replace: ":", "" }}"
|
||||
title="Permanent Link to "{{ post.title | strip_html | escape }}"">
|
||||
{{ post.title }}
|
||||
</a>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="entry-content">{{ post.text }}</div>
|
||||
{%- assign cat_count = post.category_ids | size -%}
|
||||
{%- if cat_count > 0 %}
|
||||
<small>
|
||||
Categorized under
|
||||
{% for cat_id in post.category_ids -%}
|
||||
{%- assign cat = categories | where: "id", cat_id | first -%}
|
||||
<a href="{{ cat | category_link }}" title="Categorized under {{ cat.name | strip_html | escape }}"
|
||||
rel="tag">{{ cat.name }}</a>{% unless forloop.last %}, {% endunless %}
|
||||
{%- endfor %}
|
||||
</small><br>
|
||||
{%- endif %}
|
||||
{%- assign tag_count = post.tags | size -%}
|
||||
{%- if tag_count > 0 %}
|
||||
<small>
|
||||
Tagged
|
||||
{% for tag in post.tags -%}
|
||||
<a href="{{ tag | tag_link }}" title="Tagged “{{ tag | escape }}”"
|
||||
rel="tag">{{ tag }}</a>{% unless forloop.last %}, {% endunless %}
|
||||
{%- endfor %}
|
||||
</small><br>
|
||||
{%- endif %}
|
||||
{%- if logged_on %}<small><a hx-boost="false" href="{{ post | edit_post_link }}">Edit Post</a></small>{% endif %}
|
||||
</article>
|
||||
{%- endfor %}
|
||||
<div class="bottom-nav" role="navigation">
|
||||
<div class="nav-previous">
|
||||
{% if model.newer_link -%}
|
||||
<a href="{{ model.newer_link.value }}">« Newer Posts</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="nav-next">
|
||||
{% if model.older_link -%}
|
||||
<a href="{{ model.older_link.value }}">Older Posts »</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
@ -1,14 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>
|
||||
{%- if is_home -%}
|
||||
{{ web_log.name }}{% if web_log.subtitle %} | {{ web_log.subtitle.value }}{% endif %}
|
||||
{%- else -%}
|
||||
{{ page_title | strip_html }}{% if page_title and page_title != "" %} » {% endif %}{{ web_log.name }}
|
||||
{%- endif -%}
|
||||
</title>
|
||||
</head>
|
||||
<body>{{ content }}</body>
|
||||
</html>
|
@ -1,92 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>
|
||||
{%- if is_home -%}
|
||||
{{ web_log.name }}{% if web_log.subtitle %} | {{ web_log.subtitle.value }}{% endif %}
|
||||
{%- else -%}
|
||||
{{ page_title | strip_html }}{% if page_title and page_title != "" %} » {% endif %}{{ web_log.name }}
|
||||
{%- endif -%}
|
||||
</title>
|
||||
{% page_head -%}
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header" id="top">
|
||||
<div class="header-logo">
|
||||
<a href="{{ "" | relative_link }}" hx-boost="true" hx-target="#content" hx-swap="innerHTML show:#top:top"
|
||||
hx-indicator="#loadOverlay">
|
||||
<img src="{{ "img/bitbadger.png" | theme_asset }}"
|
||||
alt="A cartoon badger looking at a computer screen, with his paw on a mouse"
|
||||
title="Bit Badger Solutions">
|
||||
</a>
|
||||
</div>
|
||||
<div class="header-title">
|
||||
<a href="{{ "" | relative_link }}" hx-boost="true" hx-target="#content" hx-swap="innerHTML show:#top:top"
|
||||
hx-indicator="#loadOverlay">
|
||||
The Bit Badger Blog
|
||||
</a>
|
||||
</div>
|
||||
<div class="header-spacer"> </div>
|
||||
<div class="header-social">
|
||||
<a href="{{ "feed.xml" | relative_link }}" title="Subscribe to The Bit Badger Blog via RSS">
|
||||
<img src="{{ "img/rss.png" | theme_asset }}" alt="RSS">
|
||||
</a>
|
||||
<a href="https://twitter.com/Bit_Badger" title="Bit_Badger on Twitter">
|
||||
<img src="{{ "img/twitter.png" | theme_asset }}" alt="Twitter">
|
||||
</a>
|
||||
<a href="https://www.facebook.com/bitbadger.solutions" title="Bit Badger Solutions on Facebook">
|
||||
<img src="{{ "img/facebook.png" | theme_asset }}" alt="Facebook">
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="content-wrapper" hx-boost="true" hx-target="#content" hx-swap="innerHTML show:#top:top"
|
||||
hx-indicator="#loadOverlay">
|
||||
<div class="load-overlay" id="loadOverlay">
|
||||
<h1>Loading…</h1>
|
||||
</div>
|
||||
<main class="content" id="content" role="main">
|
||||
{{ content }}
|
||||
</main>
|
||||
<aside class="blog-sidebar">
|
||||
<div>
|
||||
<div class="sidebar-head">Linux Resources</div>
|
||||
<ul><li><a href="{{ "linux/" | relative_link }}">Browse Resources</a></li></ul>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sidebar-head">Categories</div>
|
||||
<ul class="cat-list">
|
||||
{% for cat in categories -%}
|
||||
{%- assign indent = cat.parent_names | size -%}
|
||||
<li class="cat-list-item"{% if indent > 0 %} style="padding-left:{{ indent }}rem;"{% endif %}>
|
||||
<a href="{{ cat | category_link }}" class="cat-list-link">{{ cat.name }}</a>
|
||||
<span class="cat-list-count">{{ cat.post_count }}</span>
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
<footer class="site-footer">
|
||||
<span>
|
||||
A production of
|
||||
<a href="https://bitbadger.solutions" title="Bit Badger Solutions" target="_blank" rel="noopener"
|
||||
class="no-wrap">
|
||||
Bit Badger Solutions
|
||||
</a>
|
||||
</span>
|
||||
<span>
|
||||
Powered by <a href="https://github.com/bit-badger/myWebLog/tree/v2" target="_blank" rel="noopener">myWebLog</a>
|
||||
•
|
||||
{% if logged_on %}
|
||||
<a href="{{ "admin" | relative_link }}">Dashboard</a>
|
||||
{% else %}
|
||||
<a href="{{ "user/log-on" | relative_link }}">Log On</a>
|
||||
{%- endif %}
|
||||
</span>
|
||||
</footer>
|
||||
{% page_foot %}
|
||||
</body>
|
||||
</html>
|
@ -1,7 +0,0 @@
|
||||
<article class="auto">
|
||||
<h1 class="entry-title">{{ page.title }}</h1>
|
||||
<div class="entry-content">{{ page.text }}</div>
|
||||
{%- if logged_on %}
|
||||
<p><small><a hx-boost="false" href="{{ page | edit_page_link }}">Edit Page</a></small></p>
|
||||
{% endif -%}
|
||||
</article>
|
@ -1,64 +0,0 @@
|
||||
{%- assign post = model.posts | first -%}
|
||||
<article class="auto">
|
||||
<h1 class="entry-title">
|
||||
{{ post.title }}
|
||||
</h1>
|
||||
<header class="entry-header">
|
||||
<p>
|
||||
Posted by {{ model.authors | value: post.author_id }} on {{ post.published_on | date: "dddd, MMMM d, yyyy" }}
|
||||
at {{ post.published_on | date: "h:mm tt" | downcase }}
|
||||
</p>
|
||||
{%- assign cat_count = post.category_ids | size -%}
|
||||
{%- if cat_count > 0 %}
|
||||
<p>
|
||||
<small>
|
||||
Categorized under
|
||||
{% for cat_id in post.category_ids -%}
|
||||
{%- assign cat = categories | where: "id", cat_id | first -%}
|
||||
<a href="{{ cat | category_link }}" title="Categorized under {{ cat.name | strip_html | escape }}"
|
||||
rel="tag">{{ cat.name }}</a>{% unless forloop.last %}, {% endunless %}
|
||||
{%- endfor %}
|
||||
</small>
|
||||
</p>
|
||||
{%- endif %}
|
||||
</header>
|
||||
<div class="entry-content">{{ post.text }}</div>
|
||||
<footer class="entry-footer">
|
||||
{%- assign tag_count = post.tags | size -%}
|
||||
{%- if tag_count > 0 %}
|
||||
<p>
|
||||
Tagged:
|
||||
{% for tag in post.tags -%}
|
||||
<a href="{{ tag | tag_link }}" title="Tagged “{{ tag | escape }}”"
|
||||
rel="tag">{{ tag }}</a>{% unless forloop.last %}, {% endunless %}
|
||||
{%- endfor %}
|
||||
</p>
|
||||
{%- endif %}
|
||||
<p>
|
||||
Bookmark:
|
||||
<a href="{{ post | absolute_link }}" rel="bookmark"
|
||||
title="Permanent link to “{{ post.title | strip_html | escape }}”">
|
||||
the permalink
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
{%- if logged_on %}
|
||||
Edit: <a hx-boost="false" href="{{ post | edit_post_link }}">this post</a>
|
||||
{% else %}
|
||||
|
||||
{% endif -%}
|
||||
</p>
|
||||
{% if model.newer_link -%}
|
||||
<p>
|
||||
Next Newer Post:
|
||||
<a href="{{ model.newer_link.value | relative_link }}">{{ model.newer_name.value }}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if model.older_link -%}
|
||||
<p>
|
||||
Next Older Post:
|
||||
<a href="{{ model.older_link.value | relative_link }}">{{ model.older_name.value }}</a>
|
||||
</p>
|
||||
{%- endif -%}
|
||||
</footer>
|
||||
</article>
|
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 5.7 KiB |
@ -1,12 +0,0 @@
|
||||
const awftw = {
|
||||
counted: false,
|
||||
countPlay: function (fileLink) {
|
||||
if (!this.counted) {
|
||||
const request = new XMLHttpRequest()
|
||||
request.open('HEAD', 'https://pdcst.click/c/awftw/files.bitbadger.solutions/devotions/' + fileLink, true)
|
||||
request.onload = function () { awftw.counted = true }
|
||||
request.onerror = function () { }
|
||||
request.send()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,349 +0,0 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Quicksand|Federo|Istok+Web');
|
||||
:root {
|
||||
--text-color: hsl(0, 0%, 10%);
|
||||
--accent-color: green;
|
||||
--link-color: green;
|
||||
--superscript-color: #707070;
|
||||
--bkg-color: lightgray;
|
||||
--heading-bkg-color: darkgray;
|
||||
--item-bkg-color: white;
|
||||
--title-text-color: white;
|
||||
--audio-bkg-color: hsla(0, 0%, 0%, .05);
|
||||
--audio-text-color: hsla(0, 0%, 0%, .5);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--text-color: hsl(0, 0%, 80%);
|
||||
--accent-color: hsl(120, 100%, 15%);
|
||||
--link-color: hsl(120, 100%, 34%);
|
||||
--superscript-color: hsl(0, 0%, 70%);
|
||||
--bkg-color: hsl(0, 0%, 20%);
|
||||
--heading-bkg-color: hsla(0, 0%, 100%, .2);
|
||||
--item-bkg-color: hsla(0, 0%, 7%);
|
||||
--audio-bkg-color: hsl(0, 0%, 10%);
|
||||
--audio-text-color: hsl(0, 0%, 50%);
|
||||
}
|
||||
blockquote {
|
||||
background-color: var(--bkg-color);
|
||||
background-image: linear-gradient(hsl(0, 0%, 85%), hsl(0, 0%, 85%)), url('../img/paper.png') repeat;
|
||||
background-blend-mode: soft-light;
|
||||
color: hsl(0, 0%, 95%);
|
||||
}
|
||||
blockquote.standard {
|
||||
background: unset;
|
||||
color: unset;
|
||||
border-top: none;
|
||||
}
|
||||
.ref {
|
||||
text-shadow: white 0 0 6px, white 0 0 6px, white 0 0 6px, white 0 0 6px;
|
||||
}
|
||||
.ref sup {
|
||||
text-shadow: none;
|
||||
}
|
||||
.index-title {
|
||||
text-shadow: white 0 0 6px, white 0 0 6px;
|
||||
}
|
||||
}
|
||||
html {
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
body {
|
||||
font-family: "Istok Web",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
|
||||
font-size: 1.1rem;
|
||||
margin: 0;
|
||||
color: var(--text-color);
|
||||
background-color: var(--bkg-color);
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
a:link, a:visited {
|
||||
color: var(--link-color);
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
h1, h2, h3, h4, p, ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
sup {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
.site-header {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: flex-end;
|
||||
margin-bottom: 1rem;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(var(--accent-color)), to(var(--bkg-color)));
|
||||
background-image: -webkit-linear-gradient(top, var(--accent-color), var(--bkg-color));
|
||||
background-image: -moz-linear-gradient(top, var(--accent-color), var(--bkg-color));
|
||||
background-image: linear-gradient(to bottom, var(--accent-color), var(--bkg-color));
|
||||
}
|
||||
.site-header a {
|
||||
color: var(--title-text-color);
|
||||
}
|
||||
.site-header p {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
.site-title,
|
||||
.index-title,
|
||||
.item-heading,
|
||||
.item-meta,
|
||||
.post-meta,
|
||||
.post-nav-title,
|
||||
.page-footer a {
|
||||
font-family: Federo,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
|
||||
}
|
||||
.site-title {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
padding-left: 1rem;
|
||||
padding-top: .3rem;
|
||||
}
|
||||
.site-title a {
|
||||
color: var(--title-text-color);
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
max-width: 1400px;
|
||||
margin: auto;
|
||||
}
|
||||
@media all and (max-width:81rem) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.index-title {
|
||||
color: black;
|
||||
text-align: center;
|
||||
}
|
||||
.content {
|
||||
max-width: 60rem;
|
||||
margin: 0;
|
||||
}
|
||||
.sidebar {
|
||||
min-width: 10rem;
|
||||
max-width: 20rem;
|
||||
font-size: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@media all and (max-width:60rem) {
|
||||
.content {
|
||||
padding: 0 .4rem;
|
||||
}
|
||||
}
|
||||
@media all and (max-width:81rem) {
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
max-width: 60rem;
|
||||
padding: 0;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.sidebar .item {
|
||||
max-width: 12rem;
|
||||
}
|
||||
}
|
||||
.sidebar h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.sidebar ul {
|
||||
padding-left: 1rem;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
.sidebar ul li {
|
||||
list-style-type: none;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
.sidebar ul li ul > li {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
.sidebar .item > ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
.post-sidebar {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
@media all and (max-width:81rem) {
|
||||
.post-sidebar {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
hr.sidebar-sep {
|
||||
margin: 0 -.4rem .5rem -.4rem;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
color: var(--accent-color);
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
blockquote {
|
||||
margin: 1rem 2rem;
|
||||
/* from bible */
|
||||
padding: 11px;
|
||||
border: 0;
|
||||
background: var(--bkg-color) url('img/paper.png') repeat;
|
||||
font-family: Quicksand, serif;
|
||||
border-top: solid 1px black;
|
||||
display: flow-root;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
blockquote p:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
blockquote p:nth-last-of-type(2) {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
blockquote.standard p:nth-last-of-type(2) {
|
||||
margin-bottom: inherit;
|
||||
}
|
||||
blockquote.standard, blockquote blockquote {
|
||||
margin: 1rem 2rem 1rem 1rem;
|
||||
border-left: solid 3px var(--accent-color);
|
||||
border-top: unset;
|
||||
font-family: inherit;
|
||||
}
|
||||
blockquote cite {
|
||||
display: block;
|
||||
padding-right: 11px;
|
||||
text-align: right;
|
||||
background: var(--item-bkg-color) url('img/ribbon.png') no-repeat bottom left;
|
||||
color: var(--text-color);
|
||||
height: 28px;
|
||||
font-style: normal;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
margin: 0 -11px -11px -12px;
|
||||
}
|
||||
blockquote cite::before {
|
||||
content: "— ";
|
||||
}
|
||||
.ref {
|
||||
color: red;
|
||||
}
|
||||
blockquote sup {
|
||||
color: var(--superscript-color);
|
||||
padding-right: .25rem;
|
||||
}
|
||||
.lord, .sc {
|
||||
font-variant: small-caps;
|
||||
}
|
||||
.u {
|
||||
text-decoration: underline;
|
||||
}
|
||||
blockquote.standard footer {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
blockquote.standard footer cite {
|
||||
font-style: italic;
|
||||
background: unset;
|
||||
display: inline;
|
||||
text-align: left;
|
||||
height: unset;
|
||||
background: unset;
|
||||
position: unset;
|
||||
top: unset;
|
||||
margin: inherit;
|
||||
}
|
||||
cite {
|
||||
font-size: 1rem;
|
||||
}
|
||||
nav {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.nav-next {
|
||||
text-align: right;
|
||||
}
|
||||
.post-nav {
|
||||
font-size: .8rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
footer.part-1 {
|
||||
height: 2rem;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(var(--bkg-color)), to(var(--accent-color)));
|
||||
background-image: -webkit-linear-gradient(top, var(--bkg-color), var(--accent-color));
|
||||
background-image: -moz-linear-gradient(top, var(--bkg-color), var(--accent-color));
|
||||
background-image: linear-gradient(to bottom, var(--bkg-color), var(--accent-color));
|
||||
}
|
||||
footer.page-footer {
|
||||
padding: 0 .5rem .5rem 0;
|
||||
text-align: right;
|
||||
background-color: var(--accent-color);
|
||||
color: var(--title-text-color);
|
||||
font-size: 1rem;
|
||||
}
|
||||
footer.page-footer a:link,
|
||||
footer.page-footer a:visited {
|
||||
color: var(--title-text-color);
|
||||
}
|
||||
small.count {
|
||||
padding-left: .35rem;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
.item {
|
||||
border: solid 1px black;
|
||||
background-color: var(--item-bkg-color);
|
||||
padding: .4rem;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
.item-heading {
|
||||
margin: -.4rem -.4rem .4rem -.4rem;
|
||||
border-bottom: solid 2px var(--link-color);
|
||||
padding-top: .4rem;
|
||||
padding-bottom: .2rem;
|
||||
text-align: center;
|
||||
background-color: var(--heading-bkg-color);
|
||||
}
|
||||
.item-heading,
|
||||
.item-heading a {
|
||||
color: var(--title-text-color);
|
||||
}
|
||||
.item-meta {
|
||||
margin-bottom: 1.2rem;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
.date-posted {
|
||||
padding: 0 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
aside.podcast {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
background: var(--audio-bkg-color);
|
||||
border: solid 1px var(--accent-color);
|
||||
border-radius: .5rem;
|
||||
color: var(--audio-text-color);
|
||||
margin: 0 .5rem 1rem .5rem;
|
||||
}
|
||||
aside.podcast audio {
|
||||
width: 100%;
|
||||
}
|
||||
aside.podcast p {
|
||||
margin: 0 .5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
a.dl:link,
|
||||
a.dl:visited {
|
||||
color: var(--audio-text-color);
|
||||
}
|
||||
@media all and (max-width: 40rem) {
|
||||
blockquote {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 44 KiB |
@ -1,372 +0,0 @@
|
||||
:root {
|
||||
--heading-fonts: Oswald, "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||
--accent-color: navy;
|
||||
--app-color: maroon;
|
||||
--edge-color: lightgray;
|
||||
--bkg-color: #fffafa;
|
||||
}
|
||||
html {
|
||||
background-color: var(--edge-color);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Raleway, "Segoe UI", Ubuntu, Tahoma, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||
background-color: var(--bkg-color);
|
||||
}
|
||||
a {
|
||||
color: var(--accent-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
border-bottom: dotted 1px var(--accent-color);
|
||||
}
|
||||
a img {
|
||||
border: 0;
|
||||
}
|
||||
acronym {
|
||||
border-bottom: dotted 1px black;
|
||||
}
|
||||
header, h1, h2, h3, .footer-by a {
|
||||
font-family: var(--heading-fonts);
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 1.4rem 0;
|
||||
font-size: 2rem;
|
||||
}
|
||||
h1.project-title {
|
||||
line-height: 1.2;
|
||||
}
|
||||
h2 {
|
||||
margin: 1.2rem 0;
|
||||
}
|
||||
h3 {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
h2, h3 {
|
||||
border-bottom: solid 2px var(--accent-color);
|
||||
width: 95%;
|
||||
}
|
||||
@media all and (min-width: 40rem) {
|
||||
h1 {
|
||||
margin: 0 0 1.4rem 0;
|
||||
}
|
||||
h2, h3 {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
p {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
code, pre {
|
||||
font-family: "JetBrains Mono","SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;
|
||||
font-size: 80%;
|
||||
}
|
||||
code {
|
||||
background-color: rgba(0, 0, 0, .1);
|
||||
padding: 0 .25rem;
|
||||
white-space: pre;
|
||||
}
|
||||
pre {
|
||||
background-color: rgba(0, 0, 0, .9);
|
||||
color: rgba(255, 255, 255, .9);
|
||||
padding: .5rem;
|
||||
border-radius: .5rem;
|
||||
overflow: auto;
|
||||
}
|
||||
pre > code {
|
||||
background-color: unset;
|
||||
}
|
||||
div[style="color:#DADADA;background-color:#1E1E1E;"] {
|
||||
background-color: unset !important;
|
||||
}
|
||||
#content {
|
||||
margin: 0 1rem;
|
||||
}
|
||||
.content {
|
||||
font-size: 1.1rem;
|
||||
padding: 0 .5rem;
|
||||
}
|
||||
.auto {
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media all and (min-width: 68rem) {
|
||||
.content {
|
||||
width: 66rem;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
}
|
||||
.hdr {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
.strike {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
.alignleft {
|
||||
float: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
ul {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
.app-info {
|
||||
display: flex;
|
||||
flex-flow: row-reverse wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
abbr[title] {
|
||||
text-decoration: none;
|
||||
border-bottom: dotted 1px rgba(0, 0, 0, .5)
|
||||
}
|
||||
/* Page header */
|
||||
.site-header {
|
||||
height: 100px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
background-image: linear-gradient(to bottom, var(--edge-color), var(--bkg-color));
|
||||
}
|
||||
.site-header a, .site-header a:visited {
|
||||
color: black;
|
||||
}
|
||||
.site-header a:hover {
|
||||
border-bottom: none;
|
||||
}
|
||||
.header-title {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
line-height: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
.header-spacer {
|
||||
flex-grow: 3;
|
||||
}
|
||||
.header-social {
|
||||
padding: 25px .8rem 0 0;
|
||||
}
|
||||
.header-social img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
@media all and (max-width: 40rem) {
|
||||
.site-header {
|
||||
height: auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.header-title {
|
||||
line-height: 3rem;
|
||||
}
|
||||
.header-spacer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
/* Home page */
|
||||
@media all and (min-width: 80rem) {
|
||||
.home {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-around;
|
||||
}
|
||||
}
|
||||
.home-lead {
|
||||
font-size: 1.3rem;
|
||||
text-align: center;
|
||||
}
|
||||
.app-sidebar {
|
||||
text-align: center;
|
||||
border-top: dotted 1px var(--edge-color);
|
||||
padding-top: 1rem;
|
||||
font-size: .9rem;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.app-sidebar > div {
|
||||
width: 20rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
@media all and (min-width: 68rem) {
|
||||
.app-sidebar {
|
||||
width: 66rem;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
@media all and (min-width: 80rem) {
|
||||
.app-sidebar {
|
||||
width: 12rem;
|
||||
border-top: none;
|
||||
border-left: dotted 1px var(--edge-color);
|
||||
padding-top: 0;
|
||||
padding-left: 2rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
.app-sidebar > div {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
.app-sidebar a {
|
||||
font-size: 10pt;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.app-sidebar-head {
|
||||
font-family: var(--heading-fonts);
|
||||
font-weight: bold;
|
||||
color: var(--app-color);
|
||||
margin-bottom: .8rem;
|
||||
padding: 3px 12px;
|
||||
border-bottom: solid 2px var(--edge-color);
|
||||
font-size: 1rem;
|
||||
}
|
||||
.app-sidebar-name, .app-sidebar-description {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.app-sidebar-description {
|
||||
font-style: italic;
|
||||
color: #555555;
|
||||
padding-bottom: .6rem;
|
||||
}
|
||||
/* All solution page */
|
||||
.app-name {
|
||||
font-family: var(--heading-fonts);
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: var(--app-color);
|
||||
}
|
||||
/* Individual solution pages */
|
||||
h1.solution-header {
|
||||
line-height: 1;
|
||||
}
|
||||
.app-info aside {
|
||||
float: right;
|
||||
background-color: #FFFAFA;
|
||||
padding-left: .5rem;
|
||||
}
|
||||
@media all and (max-width: 40rem) {
|
||||
.app-info aside {
|
||||
float: none;
|
||||
text-align: center;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
.app-info aside > img {
|
||||
overflow: hidden;
|
||||
border: dotted 1px darkgray;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
.tech-stack p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.tech-stack ul {
|
||||
margin-top: 0;
|
||||
}
|
||||
blockquote {
|
||||
border-left: solid 1px darkgray;
|
||||
margin-left: 25px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.quote {
|
||||
font-style: italic;
|
||||
}
|
||||
.source {
|
||||
text-align: right;
|
||||
padding-right: 60px;
|
||||
}
|
||||
.app-info h3:hover {
|
||||
cursor: hand;
|
||||
cursor: pointer;
|
||||
}
|
||||
.app-info .arrow {
|
||||
font-size: .75rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.collapse-panel {
|
||||
max-height: 0;
|
||||
transition: max-height .5s ease-in-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
.collapse-panel.shown {
|
||||
max-height: 25rem;
|
||||
overflow: auto;
|
||||
}
|
||||
.collapse-panel p:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
/* Footer */
|
||||
.project-footer {
|
||||
padding-top: 1rem;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
padding: 20px 15px 10px 15px;
|
||||
font-size: 1rem;
|
||||
color: black;
|
||||
clear: both;
|
||||
background-image: linear-gradient(to bottom, var(--bkg-color), var(--edge-color));
|
||||
}
|
||||
footer a:link, footer a:visited {
|
||||
color: black;
|
||||
}
|
||||
/* htmx "Loading" overlay */
|
||||
.load-overlay {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: center;
|
||||
width: 95%;
|
||||
margin-left: 2.5%;
|
||||
height: 0;
|
||||
z-index: 2000;
|
||||
background-color: rgba(0, 0, 0, .5);
|
||||
border-radius: 1rem;
|
||||
animation: fadeOut .25s ease-in-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
.load-overlay h1 {
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, .75);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 1rem;
|
||||
width: 50%;
|
||||
padding: 1rem;
|
||||
}
|
||||
.load-overlay.htmx-request {
|
||||
height: 80vh;
|
||||
animation: fadeIn .25s ease-in-out;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
height: 80vh;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
height: 80vh;
|
||||
}
|
||||
}
|
||||
@keyframes fadeOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
height: 80vh;
|
||||
}
|
||||
99% {
|
||||
opacity: 0;
|
||||
height: 80vh;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 20 KiB |
@ -1,18 +0,0 @@
|
||||
djs = {
|
||||
|
||||
displayVotd: res => {
|
||||
const votd = res.votd
|
||||
const votdItem = document.querySelector('.votd-item')
|
||||
votdItem.querySelector('.verse').innerHTML = votd.text
|
||||
const reference = votdItem.querySelector('.votd-reference')
|
||||
reference.href = votd.permalink.replace('&', '&')
|
||||
const ref = votd.display_ref
|
||||
const isPassage = ref.indexOf(',') >= 0 || ref.indexOf('-') >= 0 || ref.indexOf(';') >= 0
|
||||
if (isPassage) {
|
||||
document.querySelector('.votd-heading').innerText = 'Passage of the Day'
|
||||
}
|
||||
reference.innerHTML = ref
|
||||
votdItem.querySelector('.version-link').href = votd.copyrightlink.replace('&', '&').replace('&', '&')
|
||||
votdItem.style.display = 'block'
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 15 KiB |
@ -1,345 +0,0 @@
|
||||
@import "https://fonts.googleapis.com/css?family=Quicksand|Oswald";
|
||||
:root {
|
||||
--text-color: rgb(0, 0, 0);
|
||||
--accent-color: rgb(21, 140, 186);
|
||||
--bkg-color: rgb(68, 68, 68);
|
||||
--hdr-text-color: hsl(0, 0%, 100%);
|
||||
--hdr-bkg-color: hsl(0, 0%, 95%);
|
||||
--item-bkg-color: hsl(0, 0%, 100%);
|
||||
--overlay-bkg-color: rgba(0, 0, 0, .5)
|
||||
}
|
||||
@media ( prefers-color-scheme: dark ) {
|
||||
:root {
|
||||
--text-color: rgb(210, 210, 210);
|
||||
--hdr-bkg-color: hsl(0, 0%, 7%);
|
||||
--item-bkg-color: hsl(0, 0%, 12%);
|
||||
--overlay-bgk-color: rgba(255, 255, 255, .2)
|
||||
}
|
||||
}
|
||||
html {
|
||||
background-color: var(--bkg-color);
|
||||
}
|
||||
body {
|
||||
font-family: Quicksand, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
font-size: 1.2rem;
|
||||
background-color: var(--bkg-color);
|
||||
margin: 0;
|
||||
color: var(--text-color);
|
||||
}
|
||||
a:link, a:visited {
|
||||
color: var(--accent-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
h1, h2, h3, h4 {
|
||||
font-family: Oswald, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
h1 a:link, h1 a:visited {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
blockquote {
|
||||
margin: 1rem 2rem 1rem 1rem;
|
||||
border-left: solid 3px var(--accent-color);
|
||||
padding-left: 1rem;
|
||||
}
|
||||
sup, sub {
|
||||
font-size: smaller;
|
||||
}
|
||||
sup {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
sub {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
.content img {
|
||||
max-width: 100%;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
.content img.flat {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* ----- SITE HEADER ----- */
|
||||
.site-header p, footer.part-1 p {
|
||||
margin-top: .8rem;
|
||||
margin-right: 1.2rem;
|
||||
color: var(--hdr-text-color);
|
||||
}
|
||||
.site-header p a:link, .site-header p a:visited {
|
||||
font-size: 1rem;
|
||||
color: var(--hdr-text-color);
|
||||
}
|
||||
.site-header {
|
||||
min-height: 4rem;
|
||||
padding: 0 .4rem;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: flex-end;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(var(--accent-color)), to(var(--bkg-color)));
|
||||
background-image: -webkit-linear-gradient(top, var(--accent-color), var(--bkg-color));
|
||||
background-image: -moz-linear-gradient(top, var(--accent-color), var(--bkg-color));
|
||||
background-image: linear-gradient(to bottom, var(--accent-color), var(--bkg-color));
|
||||
}
|
||||
.site-header p {
|
||||
margin: 0;
|
||||
}
|
||||
.site-header p a.nav-home {
|
||||
font-family: Oswald, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
font-weight: bold;
|
||||
margin-left: .2rem;
|
||||
color: var(--hdr-text-color);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.nav-spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/* ----- CONTENT STYLES ----- */
|
||||
.index-title {
|
||||
color: var(--hdr-text-color);
|
||||
border-bottom: solid 2px var(--accent-color);
|
||||
}
|
||||
.content {
|
||||
max-width: 60rem;
|
||||
margin: 0 auto auto;
|
||||
padding: 0 .4rem;
|
||||
}
|
||||
.item {
|
||||
border: solid 1px black;
|
||||
border-radius: .5rem;
|
||||
background-color: var(--item-bkg-color);
|
||||
padding: .4rem;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
.item-heading {
|
||||
margin: -.4rem -.4rem .4rem;
|
||||
border-top-left-radius: .5rem;
|
||||
border-top-right-radius: .5rem;
|
||||
border-bottom: solid 1px darkgray;
|
||||
padding-bottom: .2rem;
|
||||
text-align: center;
|
||||
color: var(--accent-color);
|
||||
background-color: var(--hdr-bkg-color);
|
||||
}
|
||||
.post-meta {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
.pager {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
padding: 0;
|
||||
}
|
||||
.pager li {
|
||||
list-style-type: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* ----- SIDEBAR STYLES ----- */
|
||||
.sidebar {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.votd-item {
|
||||
display: none;
|
||||
}
|
||||
.votd-credits {
|
||||
margin-top: 1rem;
|
||||
text-align: right;
|
||||
}
|
||||
.cat-list {
|
||||
padding-left: 0
|
||||
}
|
||||
.cat-list ul {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.cat-list li {
|
||||
list-style-type: none;
|
||||
padding-bottom: .25rem;
|
||||
}
|
||||
.cat-list ul li ul > li {
|
||||
padding-top: .2rem;
|
||||
}
|
||||
.cat-list-count {
|
||||
font-size: .8rem;
|
||||
padding-left: .4rem;
|
||||
}
|
||||
.cat-list-count:before {
|
||||
content: '(';
|
||||
}
|
||||
.cat-list-count:after {
|
||||
content: ')';
|
||||
}
|
||||
|
||||
/* ----- FOOTER STYLES ----- */
|
||||
footer.part-1 {
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(var(--bkg-color)), to(var(--accent-color)));
|
||||
background-image: -webkit-linear-gradient(top, var(--bkg-color), var(--accent-color));
|
||||
background-image: -moz-linear-gradient(top, var(--bkg-color), var(--accent-color));
|
||||
background-image: linear-gradient(to bottom, var(--bkg-color), var(--accent-color));
|
||||
min-height: 2rem;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
footer.part-2 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(var(--accent-color)), to(var(--bkg-color)));
|
||||
background-image: -webkit-linear-gradient(top, var(--accent-color), var(--bkg-color));
|
||||
background-image: -moz-linear-gradient(top, var(--accent-color), var(--bkg-color));
|
||||
background-image: linear-gradient(to bottom, var(--accent-color), var(--bkg-color));
|
||||
padding-bottom: .4rem;
|
||||
}
|
||||
footer.part-2 > div {
|
||||
flex-basis: 30%;
|
||||
min-width: 400px;
|
||||
}
|
||||
footer.part-2 sup {
|
||||
line-height: 1;
|
||||
}
|
||||
footer.part-3 {
|
||||
padding: 0 .4rem 1rem .4rem;
|
||||
background-color: var(--bkg-color);
|
||||
}
|
||||
.copy, .copy a:link, .copy a:visited {
|
||||
font-size: 1rem;
|
||||
color: #dddddd;
|
||||
text-decoration: none;
|
||||
}
|
||||
.copy a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.football-panel {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
.football-panel div {
|
||||
text-align: center;
|
||||
line-height: 1.6rem;
|
||||
}
|
||||
|
||||
/* ----- UTILITY CLASSES ----- */
|
||||
.desktop {
|
||||
display: none;
|
||||
}
|
||||
.float-left {
|
||||
float: left;
|
||||
padding-right: .5rem;
|
||||
}
|
||||
.float-right {
|
||||
float: right;
|
||||
padding-left: .5rem;
|
||||
}
|
||||
.small-caps {
|
||||
font-variant: small-caps;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.no-wrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ----- OVERLAY ----- */
|
||||
.load-overlay {
|
||||
position: fixed;
|
||||
top: 4rem;
|
||||
left: 1rem;
|
||||
width: 50%;
|
||||
height: 0;
|
||||
z-index: 2000;
|
||||
background-color: var(--overlay-bgk-color);
|
||||
border-radius: 1rem;
|
||||
animation: fadeOut .25s ease-in-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
.load-overlay h1 {
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, .75);
|
||||
margin: 1.5rem auto;
|
||||
border-radius: 1rem;
|
||||
width: 50%;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
.load-overlay.htmx-request {
|
||||
height: unset;
|
||||
animation: fadeIn .25s ease-in-out;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
height: unset;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
height: unset;
|
||||
}
|
||||
}
|
||||
@keyframes fadeOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
height: unset;
|
||||
}
|
||||
99% {
|
||||
opacity: 0;
|
||||
height: unset;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----- SCALE UP STYLES ----- */
|
||||
@media screen and ( min-width: 50rem ) {
|
||||
main {
|
||||
grid-template-columns: 1fr 16rem;
|
||||
}
|
||||
.desktop {
|
||||
display: unset;
|
||||
}
|
||||
.mobile {
|
||||
display: none;
|
||||
}
|
||||
.site-header p {
|
||||
margin-inline-end: 1.2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.load-overlay {
|
||||
width: 25%;
|
||||
}
|
||||
main > .single {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.sidebar {
|
||||
max-width: 15rem;
|
||||
}
|
||||
footer.part-2 {
|
||||
grid-gap: 0;
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 756 B |
Before Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 10 KiB |
@ -1,484 +0,0 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Oswald|Raleway');
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
src: url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/fonts/webfonts/JetBrainsMono-Regular.woff2') format('woff2'),
|
||||
url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/fonts/ttf/JetBrainsMono-Regular.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
:root {
|
||||
--heading-fonts: Oswald, "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||
--body-fonts: Raleway, "Segoe UI", Ubuntu, Tahoma, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||
--code-fonts: "JetBrains Mono", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
--accent-color: navy;
|
||||
--app-color: maroon;
|
||||
--edge-color: lightgray;
|
||||
--bkg-color: #fffafa;
|
||||
}
|
||||
html {
|
||||
background-color: lightgray;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
body, .entry-meta {
|
||||
font-family: var(--body-fonts);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #FFFAFA;
|
||||
}
|
||||
a {
|
||||
color: navy;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
border-bottom: dotted 1px var(--accent-color);
|
||||
}
|
||||
a img {
|
||||
border:0;
|
||||
}
|
||||
acronym {
|
||||
border-bottom:dotted 1px black;
|
||||
text-decoration: none;
|
||||
}
|
||||
.site-header, h1, h2, h3, .site-footer a, .home-lead a, .highlight {
|
||||
font-family: var(--heading-fonts);
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 1.4rem 0;
|
||||
font-size: 2rem;
|
||||
}
|
||||
h2 {
|
||||
margin: 1.2rem 0;
|
||||
}
|
||||
h3 {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
h2, h3 {
|
||||
border-bottom: solid 2px var(--accent-color);
|
||||
width: 95%;
|
||||
}
|
||||
@media all and (min-width: 40rem) {
|
||||
h2, h3 {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
p {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
code, pre {
|
||||
font-family: var(--code-fonts);
|
||||
font-size: 80%;
|
||||
}
|
||||
code {
|
||||
background-color: rgba(0, 0, 0, .1);
|
||||
padding: 0 .25rem;
|
||||
white-space: pre;
|
||||
}
|
||||
pre {
|
||||
background-color: rgba(0, 0, 0, .9);
|
||||
color: rgba(255, 255, 255, .9);
|
||||
padding: .5rem;
|
||||
border-radius: .5rem;
|
||||
overflow: auto;
|
||||
}
|
||||
pre > code {
|
||||
background-color: unset;
|
||||
}
|
||||
div[style="color:#DADADA;background-color:#1E1E1E;"] {
|
||||
background-color: unset !important;
|
||||
}
|
||||
.content {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.auto {
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media all and (min-width: 68rem) {
|
||||
.content {
|
||||
width: 66rem;
|
||||
}
|
||||
}
|
||||
.hdr {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
.site-header {
|
||||
height: 100px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(var(--edge-color)), to(var(--bkg-color)));
|
||||
background-image: -webkit-linear-gradient(top, var(--edge-color), var(--bkg-color));
|
||||
background-image: -moz-linear-gradient(top, var(--edge-color), var(--bkg-color));
|
||||
background-image: linear-gradient(to bottom, var(--edge-color), var(--bkg-color));
|
||||
}
|
||||
.site-header a:link, .site-header a:visited {
|
||||
color: black;
|
||||
}
|
||||
.site-header a:hover {
|
||||
border-bottom:none;
|
||||
}
|
||||
.header-title {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
line-height: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
.header-spacer {
|
||||
flex-grow: 3;
|
||||
}
|
||||
.header-social {
|
||||
padding: 25px .8rem 0 0;
|
||||
}
|
||||
.header-social img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
@media all and (max-width: 40rem) {
|
||||
.site-header {
|
||||
height: auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.header-title {
|
||||
line-height: 3rem;
|
||||
}
|
||||
.header-spacer {
|
||||
display: none;
|
||||
}
|
||||
.header-social {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
.content-item {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
article.page .metadata {
|
||||
display: none;
|
||||
}
|
||||
.strike {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
.site-footer {
|
||||
padding: 20px 15px 10px 15px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
font-size: 1rem;
|
||||
color: black;
|
||||
clear: both;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(var(--bkg-color)), to(var(--edge-color)));
|
||||
background-image: -webkit-linear-gradient(top, var(--bkg-color), var(--edge-color));
|
||||
background-image: -moz-linear-gradient(top, var(--bkg-color), var(--edge-color));
|
||||
background-image: linear-gradient(to bottom, var(--bkg-color), var(--edge-color));
|
||||
}
|
||||
.site-footer a:link, .site-footer a:visited {
|
||||
color: black;
|
||||
}
|
||||
.alignleft {
|
||||
float:left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
ul {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
padding: 0 .5rem;
|
||||
}
|
||||
@media all and (min-width: 80rem) {
|
||||
.content-wrapper {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.content {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
.load-overlay {
|
||||
position: fixed;
|
||||
display: block;
|
||||
width: 90%;
|
||||
height: 0;
|
||||
z-index: 2000;
|
||||
background-color: rgba(0, 0, 0, .5);
|
||||
border-radius: 1rem;
|
||||
animation: fadeOut .25s ease-in-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
.load-overlay h1 {
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, .75);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 1rem;
|
||||
width: 50%;
|
||||
padding: 1rem;
|
||||
}
|
||||
.load-overlay.htmx-request {
|
||||
display: block;
|
||||
height: 80vh;
|
||||
animation: fadeIn .25s ease-in-out;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
height: 80vh;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
height: 80vh;
|
||||
}
|
||||
}
|
||||
@keyframes fadeOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
height: 80vh;
|
||||
}
|
||||
99% {
|
||||
opacity: 0;
|
||||
height: 80vh;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
@media all and (min-width: 68rem) {
|
||||
.content {
|
||||
width: 66rem;
|
||||
}
|
||||
.load-overlay {
|
||||
width: 60rem;
|
||||
left: 2rem;
|
||||
}
|
||||
}
|
||||
.home-title {
|
||||
text-align: left;
|
||||
line-height: 2rem;
|
||||
}
|
||||
.home-lead {
|
||||
font-family: var(--body-fonts);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.home-break {
|
||||
width: 80%;
|
||||
border: dotted 1px var(--edge-color);
|
||||
border-bottom: 0;
|
||||
}
|
||||
.blog-sidebar {
|
||||
border-top: dotted 1px var(--edge-color);
|
||||
padding-top: 1rem;
|
||||
font-size: 1rem;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.blog-sidebar ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
.blog-sidebar > ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.blog-sidebar li {
|
||||
list-style: none;
|
||||
}
|
||||
.blog-sidebar li:before {
|
||||
content: '» ';
|
||||
}
|
||||
@media all and (min-width: 68rem) {
|
||||
.blog-sidebar {
|
||||
width: 66rem;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
@media all and (min-width: 80rem) {
|
||||
.blog-sidebar {
|
||||
min-width: 12rem;
|
||||
max-width: 14rem;
|
||||
border-top: none;
|
||||
border-left: dotted 1px var(--edge-color);
|
||||
padding-top: 0;
|
||||
padding-left: 1rem;
|
||||
margin: 0 0 0 1rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
.blog-sidebar a {
|
||||
font-size: 10pt;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.sidebar-head {
|
||||
text-align: center;
|
||||
font-family: var(--heading-fonts);
|
||||
font-weight: bold;
|
||||
color: var(--app-color);
|
||||
margin-bottom: .8rem;
|
||||
padding: 3px 12px;
|
||||
border-bottom: solid 2px var(--edge-color);
|
||||
font-size: 1rem;
|
||||
}
|
||||
.app-sidebar-name, .app-sidebar-description {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.app-sidebar-description {
|
||||
font-style: italic;
|
||||
color: #555555;
|
||||
padding-bottom: .6rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.auto {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.auto > div {
|
||||
max-width: 66rem;
|
||||
}
|
||||
.entry-title {
|
||||
line-height: 1;
|
||||
}
|
||||
.entry-header {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.entry-content {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.entry-footer {
|
||||
font-size: 85%;
|
||||
border-top: solid 1px rgba(0, 0, 0, .2);
|
||||
}
|
||||
.entry-header p, .entry-footer p {
|
||||
margin: .5rem 0;
|
||||
}
|
||||
.cat-list-count {
|
||||
font-size: .8rem;
|
||||
}
|
||||
.cat-list-count:before {
|
||||
content: '(';
|
||||
}
|
||||
.cat-list-count:after {
|
||||
content: ')';
|
||||
}
|
||||
.bottom-nav {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.no-wrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
figure.highlight {
|
||||
background-color: #F8F8F8;
|
||||
}
|
||||
figure.highlight table {
|
||||
background-color: #002b36;
|
||||
width: 100%;
|
||||
}
|
||||
figure.highlight td.gutter,
|
||||
figure.highlight td.code,
|
||||
figure.highlight pre {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background-color: #002b36;
|
||||
color: #839496;
|
||||
}
|
||||
figure.highlight td.gutter {
|
||||
text-align:right;
|
||||
padding-right: .4rem;
|
||||
}
|
||||
figure.highlight td.gutter div.line:after {
|
||||
content: ':';
|
||||
color: #586e75;
|
||||
}
|
||||
figure.highlight td.code pre div.line:after {
|
||||
content: '.';
|
||||
visibility: hidden;
|
||||
}
|
||||
figure.highlight pre div.line,
|
||||
figure.highlight pre div.line > * {
|
||||
font-family: var(--code-fonts);
|
||||
}
|
||||
figure.highlight {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
color: #839496;
|
||||
border:1px dashed #ddd;
|
||||
}
|
||||
figure.highlight .comment,
|
||||
figure.highlight .quote {
|
||||
color: #586e75;
|
||||
}
|
||||
/* Solarized Green */
|
||||
figure.highlight .keyword,
|
||||
figure.highlight .selector-tag,
|
||||
figure.highlight .addition {
|
||||
color: #859900;
|
||||
}
|
||||
/* Solarized Cyan */
|
||||
figure.highlight .number,
|
||||
figure.highlight .string,
|
||||
figure.highlight .meta .meta-string,
|
||||
figure.highlight .literal,
|
||||
figure.highlight .doctag,
|
||||
figure.highlight .regexp {
|
||||
color: #2aa198;
|
||||
}
|
||||
/* Solarized Blue */
|
||||
figure.highlight .title,
|
||||
figure.highlight .section,
|
||||
figure.highlight .name,
|
||||
figure.highlight .selector-id,
|
||||
figure.highlight .selector-class {
|
||||
color: #268bd2;
|
||||
}
|
||||
/* Solarized Yellow */
|
||||
figure.highlight .attribute,
|
||||
figure.highlight .attr,
|
||||
figure.highlight .variable,
|
||||
figure.highlight .template-variable,
|
||||
figure.highlight .class .title,
|
||||
figure.highlight .type {
|
||||
color: #b58900;
|
||||
}
|
||||
/* Solarized Orange */
|
||||
figure.highlight .symbol,
|
||||
figure.highlight .bullet,
|
||||
figure.highlight .subst,
|
||||
figure.highlight .meta,
|
||||
figure.highlight .meta .keyword,
|
||||
figure.highlight .selector-attr,
|
||||
figure.highlight .selector-pseudo,
|
||||
figure.highlight .link {
|
||||
color: #cb4b16;
|
||||
}
|
||||
/* Solarized Red */
|
||||
figure.highlight .built_in,
|
||||
figure.highlight .deletion {
|
||||
color: #dc322f;
|
||||
}
|
||||
figure.highlight .formula {
|
||||
background: #eee8d5;
|
||||
}
|
||||
figure.highlight .emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
figure.highlight .strong {
|
||||
font-weight: bold;
|
||||
}
|
26
src/admin-theme/upload-theme.liquid
Normal file
@ -0,0 +1,26 @@
|
||||
<h2>Upload a Theme</h2>
|
||||
<article>
|
||||
<form action="{{ "admin/theme/update" | relative_link }}"
|
||||
method="post" class="container" enctype="multipart/form-data" hx-boost="false">
|
||||
<input type="hidden" name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6 offset-sm-3 pb-3">
|
||||
<div class="form-floating">
|
||||
<input type="file" id="file" name="file" class="form-control" accept=".zip" placeholder="Theme File" required>
|
||||
<label for="file">Theme File</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 pb-3">
|
||||
<div class="form-check form-switch pb-2">
|
||||
<input type="checkbox" name="clean" id="clean" class="form-check-input" value="true">
|
||||
<label for="clean" class="form-check-label">Delete Existing Theme Files</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pb-3">
|
||||
<div class="col text-center">
|
||||
<button type="submit" class="btn btn-primary">Upload Theme</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
2
src/admin-theme/version.txt
Normal file
@ -0,0 +1,2 @@
|
||||
myWebLog Admin
|
||||
2.0.0-alpha34
|