V2 #1

Merged
danieljsummers merged 102 commits from v2 into main 2022-06-23 00:35:12 +00:00
99 changed files with 420 additions and 2947 deletions
Showing only changes of commit ae729e008e - Show all commits

View File

@ -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

View File

@ -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 =

View File

@ -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 =

View File

@ -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

View File

@ -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 _, _ -> ())

View File

@ -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
}

View File

@ -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[]

View File

@ -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
]

View File

@ -37,7 +37,6 @@
</ItemGroup>
<ItemGroup>
<None Include=".\themes\**" CopyToOutputDirectory="Always" />
<None Include=".\wwwroot\**" CopyToOutputDirectory="Always" />
</ItemGroup>

View File

@ -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"

View File

@ -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 &ldquo;3:16&rdquo;s of the New Testament,2007/the-316s-of-the-new-testament.html" | split: "|" -%}

View File

@ -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 &ldquo;{{ post.title | strip_html | escape_once }}&rdquo;">
{{ 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 %} &bull; <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 }}">&#xab; Newer Posts</a>{% endif %}</div>
<div>{% if model.older_link %}<a href="{{ model.older_link.value }}">Older Posts &#xbb;</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&rsquo;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 &ldquo;{{ cat.name | escape_once }}&rdquo; &bull; A Word from the Word">
About the Series
</a> &bull;
<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 %} &nbsp; {% 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>

View File

@ -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 != "" %} &raquo; {% 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&rsquo;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> &bull; Powered by
<a href="https://github.com/bit-badger/myWebLog/tree/v2" target="_blank" rel="noopener">myWebLog</a> &bull;
{% 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>

View File

@ -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>

View File

@ -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 &ldquo;{{ tag }}&rdquo;">
<i class="fa fa-tag" aria-hidden="true"></i> {{ tag }}
</a>
{%- endfor %}
</ul>
</div>
{%- endif %}
</div>

View File

@ -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 &bull; 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 &bull; 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 &bull; 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 idlers 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 &bull; 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 &bull; 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 &bull; 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 &bull; 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">Daniels 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>

View File

@ -1,10 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ page_title }} &raquo; Bit Badger Solutions</title>
</head>
<body>
{{ content }}
</body>
</html>

View File

@ -1,49 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>{{ page_title }} &raquo; 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"> &nbsp; </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> &nbsp;
<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&hellip;</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>

View File

@ -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 %} &bull; <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 %} &bull; <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">&laquo; {{ 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>

View File

@ -1,8 +0,0 @@
<article class="content auto">
<h1>{{ page.title }}</h1>
{{ page.text }}
<p><br><a href="{{ "" | relative_link }}" title="Home">&laquo; 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>

View File

@ -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 -%}
&nbsp;&nbsp;
<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">&#x25BC;</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 }}">&laquo; 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 = "&#x25B2;"
} else {
section.className = section.className.replace(" shown", "")
arrow.innerHTML = "&#x25BC;"
}
}
</script>

View File

@ -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 &quot;{{ post.title | strip_html | escape }}&quot;">
{{ 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 }}">&laquo; Newer Posts</a></li>
{%- else -%}
<li></li>
{% endif %}
{% if model.older_link -%}
<li class="next item"><a href="{{ model.older_link.value }}">Older Posts &raquo;</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> &ndash;
<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&amp;version=ESV&amp;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>

View File

@ -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 != "" %} &raquo; {% endif %}{{ web_log.name }}
{%- endif -%}
</title>
</head>
<body>
{{ content }}
</body>
</html>

View File

@ -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 != "" %} &raquo; {% 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>&ldquo;Thanks for the dollar!&rdquo;</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&rsquo;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 &mdash; <strong>NR</strong><br>
<small>
(7-6 &bull; 4-4 SEC/3<sup>rd</sup> East)<br><br>
Last &mdash; 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 &mdash; <strong>NR</strong><br>
<small>
(3-9 &bull; 2-6 MWC/5<sup>th</sup> Mountain)<br><br>
Last &mdash; 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&rsquo;s Facebook profile">
<img src="https://badge.facebook.com/badge/1270539383.37.619606444.png" style="border:0;"
alt="Daniel J. Summers&rsquo;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">
&nbsp; 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>
&nbsp; Designed by
<a href="https://bitbadger.solutions" title="Building the site you need to ensure your success!">
Bit Badger Solutions
</a>
&bull; Powered by <a href="https://github.com/bit-badger/myWebLog/tree/v2">myWebLog</a> &bull;
{% 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>

View File

@ -1,6 +0,0 @@
<div class="content single">
<article class="item">
<h1 class="item-heading">{{ page.title }}</h1>
{{ page.text }}
</article>
</div>

View File

@ -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 &nbsp;
{% 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 &ldquo;{{ cat.name | escape }}&rdquo;">
{{ cat.name }}
</a> &nbsp; &nbsp;
</span>
{%- endfor %}
</h4>
{%- endif %}
{%- assign tag_count = post.tags | size -%}
{% if tag_count > 0 -%}
<h4>
Tagged &nbsp;
{% for tag in post.tags %}
<span class="no-wrap">
<a href="{{ tag | tag_link }}" title="Posts tagged &ldquo;{{ tag | escape }}&rdquo;" rel="tag">
<i class="fa fa-tag"></i> {{ tag }}
</a> &nbsp; &nbsp;
</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 }}">&laquo;</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 }}">&raquo;</a></h4>
<a href="{{ model.older_link.value | relative_link }}">{{ model.older_name.value }}</a>
</li>
{%- endif -%}
</ul>
</nav>
</div>
</div>

View File

@ -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>
&nbsp;
<a href="{{ post | relative_link }}"
id="{{ post.title | strip_html | downcase | replace: " ", "-" | replace: ":", "" }}"
title="Permanent Link to &quot;{{ post.title | strip_html | escape }}&quot;">
{{ 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 &ldquo;{{ tag | escape }}&rdquo;"
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 }}">&laquo; Newer Posts</a>
{% endif %}
</div>
<div class="nav-next">
{% if model.older_link -%}
<a href="{{ model.older_link.value }}">Older Posts &raquo;</a>
{% endif %}
</div>
</div>

View File

@ -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 != "" %} &raquo; {% endif %}{{ web_log.name }}
{%- endif -%}
</title>
</head>
<body>{{ content }}</body>
</html>

View File

@ -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 != "" %} &raquo; {% 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">&nbsp;</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> &nbsp; &nbsp;
<a href="https://twitter.com/Bit_Badger" title="Bit_Badger on Twitter">
<img src="{{ "img/twitter.png" | theme_asset }}" alt="Twitter">
</a> &nbsp; &nbsp;
<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&hellip;</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>
&bull;
{% 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>

View File

@ -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>

View File

@ -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 &ldquo;{{ tag | escape }}&rdquo;"
rel="tag">{{ tag }}</a>{% unless forloop.last %}, {% endunless %}
{%- endfor %}
</p>
{%- endif %}
<p>
Bookmark:
<a href="{{ post | absolute_link }}" rel="bookmark"
title="Permanent link to &ldquo;{{ post.title | strip_html | escape }}&rdquo;">
the permalink
</a>
</p>
<p>
{%- if logged_on %}
Edit: <a hx-boost="false" href="{{ post | edit_post_link }}">this post</a>
{% else %}
&nbsp;
{% 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>

View File

@ -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()
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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('&amp;', '&')
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('&amp;', '&').replace('&amp;', '&')
votdItem.style.display = 'block'
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View 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>

View File

@ -0,0 +1,2 @@
myWebLog Admin
2.0.0-alpha34