base: Added /health route.
nfp_moe: Complete re-thinking of all loading. Smarter loading and lighter site. Better user experience among other things.
This commit is contained in:
parent
4b36820f09
commit
6565409e52
17 changed files with 1585 additions and 1008 deletions
|
@ -23,8 +23,12 @@ export default class ArticleRoutes {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GET: /api/articles/[path] */
|
/** GET: /api/articles/[path] */
|
||||||
async getArticle(ctx) {
|
async getArticle(ctx, onlyReturn = false) {
|
||||||
let res = await ctx.db.safeCallProc('article_get_single', [ctx.params.path])
|
let res = await ctx.db.safeCallProc('article_get_single', [ctx.params.path])
|
||||||
|
|
||||||
|
if (onlyReturn) {
|
||||||
|
return this.getArticle_resOutput(res)
|
||||||
|
}
|
||||||
|
|
||||||
ctx.body = this.getArticle_resOutput(res)
|
ctx.body = this.getArticle_resOutput(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ export default class ServeHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
let indexFile = fsSync.readFileSync(path.join(this.root, 'index.html'))
|
let indexFile = fsSync.readFileSync(path.join(this.root, 'index.html'))
|
||||||
this.template = dot.template(indexFile.toString(), { argName: ['headerDescription', 'headerImage', 'headerTitle', 'headerUrl', 'payloadData', 'payloadTree', 'version', 'nonce'] })
|
this.template = dot.template(indexFile.toString(), { argName: ['headerDescription', 'headerImage', 'headerTitle', 'headerUrl', 'payloadData', 'payloadTree', 'version', 'nonce', 'type', 'banner'] })
|
||||||
// console.log(indexFile.toString())
|
// console.log(indexFile.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import PageRoutes from './page/routes.mjs'
|
||||||
import ArticleRoutes from './article/routes.mjs'
|
import ArticleRoutes from './article/routes.mjs'
|
||||||
import AuthenticationRoutes from './authentication/routes.mjs'
|
import AuthenticationRoutes from './authentication/routes.mjs'
|
||||||
import { authenticate } from './authentication/security.mjs'
|
import { authenticate } from './authentication/security.mjs'
|
||||||
|
import StaticRoutes from './static_routes.mjs'
|
||||||
|
|
||||||
export default class Server {
|
export default class Server {
|
||||||
constructor(http, port, core, opts = {}) {
|
constructor(http, port, core, opts = {}) {
|
||||||
|
@ -30,6 +31,7 @@ export default class Server {
|
||||||
page: new PageRoutes(),
|
page: new PageRoutes(),
|
||||||
article: new ArticleRoutes(),
|
article: new ArticleRoutes(),
|
||||||
auth: new AuthenticationRoutes(),
|
auth: new AuthenticationRoutes(),
|
||||||
|
static: new StaticRoutes(),
|
||||||
}
|
}
|
||||||
|
|
||||||
this.init()
|
this.init()
|
||||||
|
@ -54,6 +56,9 @@ export default class Server {
|
||||||
ctx.db = pool
|
ctx.db = pool
|
||||||
})
|
})
|
||||||
this.flaska.before(QueryHandler())
|
this.flaska.before(QueryHandler())
|
||||||
|
|
||||||
|
let healthChecks = 0
|
||||||
|
let healthCollectLimit = 60 * 60 * 12
|
||||||
|
|
||||||
this.flaska.after(function(ctx) {
|
this.flaska.after(function(ctx) {
|
||||||
let ended = new Date().getTime()
|
let ended = new Date().getTime()
|
||||||
|
@ -69,6 +74,18 @@ export default class Server {
|
||||||
level = 'error'
|
level = 'error'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx.url === '/health') {
|
||||||
|
healthChecks++
|
||||||
|
if (healthChecks >= healthCollectLimit) {
|
||||||
|
ctx.log[level]({
|
||||||
|
duration: Math.round(ended),
|
||||||
|
status: ctx.status,
|
||||||
|
}, `<-- ${status}${ctx.method} ${ctx.url} {has happened ${healthChecks} times}`)
|
||||||
|
healthChecks = 0
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ctx.log[level]({
|
ctx.log[level]({
|
||||||
duration: requestTime,
|
duration: requestTime,
|
||||||
status: ctx.status,
|
status: ctx.status,
|
||||||
|
|
17
base/static_routes.mjs
Normal file
17
base/static_routes.mjs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import config from './config.mjs'
|
||||||
|
|
||||||
|
export default class StaticRoutes {
|
||||||
|
constructor(opts = {}) {
|
||||||
|
Object.assign(this, { })
|
||||||
|
}
|
||||||
|
|
||||||
|
register(server) {
|
||||||
|
server.flaska.get('/health', this.health.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
health(ctx) {
|
||||||
|
ctx.body = {
|
||||||
|
environment: config.get('NODE_ENV'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ export function combineFilesWithArticles(articles, files) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseFile(file) {
|
export function parseFile(file) {
|
||||||
file.url = 'https://cdn.nfp.is' + file.path
|
file.path = 'https://cdn.nfp.is' + file.path
|
||||||
file.magnet = null
|
file.magnet = null
|
||||||
file.meta = JSON.parse(file.meta || '{}') || {}
|
file.meta = JSON.parse(file.meta || '{}') || {}
|
||||||
if (file.meta.torrent) {
|
if (file.meta.torrent) {
|
||||||
|
@ -27,6 +27,7 @@ export function parseFile(file) {
|
||||||
+ '&dn=' + encodeURIComponent(file.meta.torrent.name)
|
+ '&dn=' + encodeURIComponent(file.meta.torrent.name)
|
||||||
+ '&xt=urn:btih:' + file.meta.torrent.hash
|
+ '&xt=urn:btih:' + file.meta.torrent.hash
|
||||||
+ file.meta.torrent.announce.map(item => ('&tr=' + encodeURIComponent(item))).join('')
|
+ file.meta.torrent.announce.map(item => ('&tr=' + encodeURIComponent(item))).join('')
|
||||||
|
file.meta.torrent = { name: file.meta.torrent.name, files: file.meta.torrent.files }
|
||||||
}
|
}
|
||||||
return file
|
return file
|
||||||
}
|
}
|
|
@ -1,8 +1,22 @@
|
||||||
|
import path from 'path'
|
||||||
import Parent from '../base/serve.mjs'
|
import Parent from '../base/serve.mjs'
|
||||||
|
import fs from 'fs/promises'
|
||||||
|
import dot from 'dot'
|
||||||
|
|
||||||
export default class ServeHandler extends Parent {
|
export default class ServeHandler extends Parent {
|
||||||
|
traverseTree(set, tree) {
|
||||||
|
for (let branch of tree) {
|
||||||
|
if (branch.children && branch.children.length) {
|
||||||
|
set.add(branch.id)
|
||||||
|
this.traverseTree(set, branch.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async serveIndex(ctx) {
|
async serveIndex(ctx) {
|
||||||
|
let indexFile = await fs.readFile(path.join(this.root, 'index.html'))
|
||||||
|
this.template = dot.template(indexFile.toString(), { argName: ['headerDescription', 'headerImage', 'headerTitle', 'headerUrl', 'payloadData', 'payloadTree', 'version', 'nonce', 'type', 'banner', 'media'] })
|
||||||
|
|
||||||
let payload = {
|
let payload = {
|
||||||
headerDescription: 'Small fansubbing and scanlation group translating and encoding our favourite shows from Japan.',
|
headerDescription: 'Small fansubbing and scanlation group translating and encoding our favourite shows from Japan.',
|
||||||
headerImage: this.frontend + '/assets/img/heart.png',
|
headerImage: this.frontend + '/assets/img/heart.png',
|
||||||
|
@ -12,18 +26,39 @@ export default class ServeHandler extends Parent {
|
||||||
payloadTree: null,
|
payloadTree: null,
|
||||||
version: this.version,
|
version: this.version,
|
||||||
nonce: ctx.state.nonce,
|
nonce: ctx.state.nonce,
|
||||||
|
type: 'page',
|
||||||
|
banner: false,
|
||||||
|
media: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
payload.payloadTree = JSON.stringify(await this.pageRoutes.getPageTree(ctx, true))
|
let tree = await this.pageRoutes.getPageTree(ctx, true)
|
||||||
|
let setOfBranches = new Set()
|
||||||
|
this.traverseTree(setOfBranches, tree.tree)
|
||||||
|
|
||||||
|
payload.payloadTree = JSON.stringify(tree)
|
||||||
if (ctx.url === '/' || (ctx.url.startsWith('/page/') && ctx.url.lastIndexOf('/') === 5)) {
|
if (ctx.url === '/' || (ctx.url.startsWith('/page/') && ctx.url.lastIndexOf('/') === 5)) {
|
||||||
ctx.params.path = null
|
ctx.params.path = null
|
||||||
if (ctx.url.lastIndexOf('/') === 5) {
|
if (ctx.url.lastIndexOf('/') === 5) {
|
||||||
ctx.params.path = ctx.url.slice(ctx.url.lastIndexOf('/') + 1)
|
ctx.params.path = ctx.url.slice(ctx.url.lastIndexOf('/') + 1)
|
||||||
}
|
}
|
||||||
payload.payloadData = JSON.stringify(await this.pageRoutes.getPage(ctx, true))
|
let data = await this.pageRoutes.getPage(ctx, true)
|
||||||
|
if (!data.page) {
|
||||||
|
payload.type = 'frontpage'
|
||||||
|
}
|
||||||
|
else if (setOfBranches.has(data.page.id)) {
|
||||||
|
payload.type = 'page_with_children'
|
||||||
|
}
|
||||||
|
payload.media = data.page?.media_avif_preview || false
|
||||||
|
payload.banner = data.featured?.banner_avif_preview || data.page?.banner_avif_preview || false
|
||||||
|
payload.payloadData = JSON.stringify(data)
|
||||||
|
} else if (ctx.url.startsWith('/article/') && ctx.url.lastIndexOf('/') === 8) {
|
||||||
|
ctx.params.path = ctx.url.slice(ctx.url.lastIndexOf('/') + 1)
|
||||||
|
let data = await this.articleRoutes.getArticle(ctx, true)
|
||||||
|
payload.media = data.article?.media_avif_preview || false
|
||||||
|
payload.payloadData = JSON.stringify(data)
|
||||||
|
payload.type = 'article'
|
||||||
}
|
}
|
||||||
console.log('url', ctx.url)
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.log.error(e)
|
ctx.log.error(e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ const Fileinfo = {
|
||||||
m('a', {
|
m('a', {
|
||||||
target: '_blank',
|
target: '_blank',
|
||||||
rel: 'noopener',
|
rel: 'noopener',
|
||||||
href: vnode.attrs.file.url,
|
href: vnode.attrs.file.path,
|
||||||
}, this.getDownloadName(vnode)),
|
}, this.getDownloadName(vnode)),
|
||||||
vnode.attrs.file.magnet
|
vnode.attrs.file.magnet
|
||||||
? m('a', {
|
? m('a', {
|
||||||
|
|
|
@ -47,4 +47,4 @@ AVIF.onload = AVIF.onerror = function () {
|
||||||
m.route(document.getElementById('main'), '/', allRoutes)
|
m.route(document.getElementById('main'), '/', allRoutes)
|
||||||
m.mount(document.getElementById('footer'), Footer)
|
m.mount(document.getElementById('footer'), Footer)
|
||||||
}
|
}
|
||||||
AVIF.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=';
|
AVIF.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABcAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAABAA0ABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgAPkgIaDQgMgkf8AAAQAAAr3A=';
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const m = require('mithril')
|
||||||
|
|
||||||
export function generatePictureSource(item, cover) {
|
export function generatePictureSource(item, cover) {
|
||||||
if (!item || !item.media_alt_prefix) return null
|
if (!item || !item.media_alt_prefix) return null
|
||||||
|
|
||||||
|
@ -10,9 +12,56 @@ export function generatePictureSource(item, cover) {
|
||||||
+ item.media_alt_prefix + '_medium.avif' + ' 1300w, '
|
+ item.media_alt_prefix + '_medium.avif' + ' 1300w, '
|
||||||
+ item.media_alt_prefix + '_large.avif 1920w',
|
+ item.media_alt_prefix + '_large.avif 1920w',
|
||||||
cover: cover,
|
cover: cover,
|
||||||
|
preview: item.media_avif_preview,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let loadingImage = null
|
||||||
|
let loader = null
|
||||||
|
|
||||||
|
function cancelLoader() {
|
||||||
|
if (loader) {
|
||||||
|
loader.src = ''
|
||||||
|
}
|
||||||
|
loader = null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function smartBanner(item) {
|
||||||
|
if (!item) {
|
||||||
|
if (loader) {
|
||||||
|
cancelLoader()
|
||||||
|
}
|
||||||
|
loadingImage = null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item.preview) {
|
||||||
|
loadingImage = null
|
||||||
|
cancelLoader()
|
||||||
|
return item.banner
|
||||||
|
}
|
||||||
|
if (loadingImage !== item.banner && loader) {
|
||||||
|
cancelLoader()
|
||||||
|
}
|
||||||
|
if (loadingImage === item.banner && !loader) {
|
||||||
|
return item.banner
|
||||||
|
}
|
||||||
|
if (loadingImage === item.banner) {
|
||||||
|
return item.preview
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingImage = item.banner
|
||||||
|
loader = new Image();
|
||||||
|
|
||||||
|
loader.src = item.banner;
|
||||||
|
loader.onload = loader.onerror = function() {
|
||||||
|
loader = null
|
||||||
|
m.redraw()
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.preview
|
||||||
|
}
|
||||||
|
|
||||||
export function getBannerImage(item, prefix) {
|
export function getBannerImage(item, prefix) {
|
||||||
if (!item || !item.banner_alt_prefix) return null
|
if (!item || !item.banner_alt_prefix) return null
|
||||||
|
|
||||||
|
@ -20,7 +69,8 @@ export function getBannerImage(item, prefix) {
|
||||||
path: prefix + item.path,
|
path: prefix + item.path,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
original: item.banner_path,
|
original: item.banner_path,
|
||||||
banner: item.banner_alt_prefix
|
banner: item.banner_alt_prefix,
|
||||||
|
preview: item.banner_avif_preview,
|
||||||
}
|
}
|
||||||
|
|
||||||
var deviceWidth = window.innerWidth
|
var deviceWidth = window.innerWidth
|
||||||
|
@ -47,23 +97,24 @@ export function getArticlePicture(pictureData, useRouteLink, path, altText, fall
|
||||||
if (!pictureData) return fallback || null
|
if (!pictureData) return fallback || null
|
||||||
|
|
||||||
return m(useRouteLink ? m.route.Link : 'a', {
|
return m(useRouteLink ? m.route.Link : 'a', {
|
||||||
class: 'cover',
|
class: 'cover ' + (pictureData.preview ? 'haspreview' : ''),
|
||||||
rel: useRouteLink ? null : 'noopener',
|
rel: useRouteLink ? null : 'noopener',
|
||||||
target: useRouteLink ? null : '_blank',
|
target: useRouteLink ? null : '_blank',
|
||||||
href: path,
|
href: path,
|
||||||
},
|
}, [
|
||||||
m('picture', [
|
pictureData.preview ? m('img', { src: pictureData.preview }) : null,
|
||||||
m('source', {
|
m('picture', [
|
||||||
srcset: pictureData.avif,
|
m('source', {
|
||||||
sizes: pictureData.cover,
|
srcset: pictureData.avif,
|
||||||
type: 'image/avif',
|
sizes: pictureData.cover,
|
||||||
}),
|
type: 'image/avif',
|
||||||
m('img', {
|
}),
|
||||||
srcset: pictureData.jpeg,
|
m('img', {
|
||||||
sizes: pictureData.cover,
|
srcset: pictureData.jpeg,
|
||||||
alt: altText,
|
sizes: pictureData.cover,
|
||||||
src: pictureData.fallback,
|
alt: altText,
|
||||||
}),
|
src: pictureData.fallback,
|
||||||
|
}),
|
||||||
|
])
|
||||||
])
|
])
|
||||||
)
|
|
||||||
}
|
}
|
|
@ -21,7 +21,7 @@ const SiteArticle = {
|
||||||
|
|
||||||
if (window.__nfpdata) {
|
if (window.__nfpdata) {
|
||||||
this.path = m.route.param('id')
|
this.path = m.route.param('id')
|
||||||
this.data.article = window.__nfpdata
|
this.data = window.__nfpdata
|
||||||
window.__nfpdata = null
|
window.__nfpdata = null
|
||||||
this.afterData()
|
this.afterData()
|
||||||
} else {
|
} else {
|
||||||
|
@ -83,7 +83,30 @@ const SiteArticle = {
|
||||||
|
|
||||||
return [
|
return [
|
||||||
this.loading
|
this.loading
|
||||||
? m('div.loading-spinner')
|
? m('.inside.vertical', [
|
||||||
|
m('div.actions', m('div.lb-link')),
|
||||||
|
m('article.fullsize', [
|
||||||
|
m('h2.title', m('div.lb-main.lb--long')),
|
||||||
|
m('div.row', [
|
||||||
|
m('div.cover', m('picture.lb.nobg')),
|
||||||
|
m('div.description', [
|
||||||
|
m('div.lb-main.lb--long'),
|
||||||
|
m('div.lb-main'),
|
||||||
|
m('div.lb-main.lb--medium'),
|
||||||
|
m('div.lb-main.lb--medium'),
|
||||||
|
m('div.lb-main'),
|
||||||
|
m('p', m.trust(' ')),
|
||||||
|
m('fileinfo', [
|
||||||
|
m('div.lb-main.lb--slim.lb--longest'),
|
||||||
|
m('ul', [
|
||||||
|
m('li', m('div.lb-main.lb--slim.lb--longest')),
|
||||||
|
m('li', m('div.lb-main.lb--slim.lb--longest')),
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
])
|
||||||
: null,
|
: null,
|
||||||
!this.loading && this.error === 'Article not found'
|
!this.loading && this.error === 'Article not found'
|
||||||
? NotFoundView.view()
|
? NotFoundView.view()
|
||||||
|
@ -96,7 +119,7 @@ const SiteArticle = {
|
||||||
},
|
},
|
||||||
}, 'Article error: ' + this.error + '. Click here to try again'))
|
}, 'Article error: ' + this.error + '. Click here to try again'))
|
||||||
: null,
|
: null,
|
||||||
(article
|
(!this.loading && article
|
||||||
? m('.inside.vertical', [
|
? m('.inside.vertical', [
|
||||||
m('div.actions', [
|
m('div.actions', [
|
||||||
'« ',
|
'« ',
|
||||||
|
@ -134,6 +157,7 @@ const SiteArticle = {
|
||||||
}
|
}
|
||||||
}}, m('div.loading-spinner'))
|
}}, m('div.loading-spinner'))
|
||||||
: m('button.comments', {
|
: m('button.comments', {
|
||||||
|
style: 'display: none',
|
||||||
onclick: function() { window.LoadComments = true },
|
onclick: function() { window.LoadComments = true },
|
||||||
}, 'Open comment discussion'),
|
}, 'Open comment discussion'),
|
||||||
])
|
])
|
||||||
|
|
|
@ -15,7 +15,7 @@ const SitePage = {
|
||||||
oninit: function(vnode) {
|
oninit: function(vnode) {
|
||||||
this.error = ''
|
this.error = ''
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.showLoading = null
|
this.treePage = null
|
||||||
this.data = {
|
this.data = {
|
||||||
page: null,
|
page: null,
|
||||||
articles: [],
|
articles: [],
|
||||||
|
@ -26,7 +26,6 @@ const SitePage = {
|
||||||
this.children = []
|
this.children = []
|
||||||
this.currentPage = Number(m.route.param('page')) || 1
|
this.currentPage = Number(m.route.param('page')) || 1
|
||||||
|
|
||||||
console.log('test', window.__nfpdata)
|
|
||||||
if (window.__nfpdata) {
|
if (window.__nfpdata) {
|
||||||
this.path = m.route.param('id')
|
this.path = m.route.param('id')
|
||||||
this.lastpage = this.currentPage
|
this.lastpage = this.currentPage
|
||||||
|
@ -58,24 +57,13 @@ const SitePage = {
|
||||||
this.lastpage = this.currentPage
|
this.lastpage = this.currentPage
|
||||||
this.path = m.route.param('id')
|
this.path = m.route.param('id')
|
||||||
|
|
||||||
if (this.showLoading) {
|
this.loading = true
|
||||||
clearTimeout(this.showLoading)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.data.page) {
|
|
||||||
this.showLoading = setTimeout(() => {
|
|
||||||
this.showLoading = null
|
|
||||||
this.loading = true
|
|
||||||
m.redraw()
|
|
||||||
}, 300)
|
|
||||||
} else {
|
|
||||||
this.loading = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.path) {
|
if (this.path) {
|
||||||
this.children = PageTree.TreeMap.get(this.path)
|
this.treePage = PageTree.TreeMap.get(this.path)
|
||||||
this.children = this.children && this.children.children || []
|
this.children = this.treePage && this.treePage.children || []
|
||||||
} else {
|
} else {
|
||||||
|
this.treePage = { has_banner: true, has_media: true }
|
||||||
this.children = PageTree.Tree
|
this.children = PageTree.Tree
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,8 +78,6 @@ const SitePage = {
|
||||||
this.error = err.message
|
this.error = err.message
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
clearTimeout(this.showLoading)
|
|
||||||
this.showLoading = null
|
|
||||||
this.loading = false
|
this.loading = false
|
||||||
m.redraw()
|
m.redraw()
|
||||||
})
|
})
|
||||||
|
@ -111,8 +97,8 @@ const SitePage = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.picture = media.generatePictureSource(this.data.page,
|
this.picture = media.generatePictureSource(this.data.page,
|
||||||
'(max-width: 840px) calc(100vw - 82px), '
|
'(max-width: 1280px) calc(100vw - 2rem), '
|
||||||
+ '758px')
|
+ '1248px')
|
||||||
|
|
||||||
if (this.lastpage !== 1) {
|
if (this.lastpage !== 1) {
|
||||||
document.title = 'Page ' + this.lastpage + ' - ' + title
|
document.title = 'Page ' + this.lastpage + ' - ' + title
|
||||||
|
@ -128,107 +114,211 @@ const SitePage = {
|
||||||
|
|
||||||
return ([
|
return ([
|
||||||
this.loading
|
this.loading
|
||||||
? m('div.loading-spinner')
|
? [
|
||||||
: null,
|
this.treePage.has_banner
|
||||||
!this.loading && this.error === 'Page not found'
|
? m('div.page-banner.lb')
|
||||||
? NotFoundView.view()
|
: null,
|
||||||
: null,
|
this.path
|
||||||
!this.loading && this.error && this.error !== 'Page not found'
|
? m('div.inside.vertical', [
|
||||||
? m('div.wrapper', m('div.error', {
|
m('div.actions', m('div.lb-link')),
|
||||||
onclick: () => {
|
m('h2.title', m('div.lb-main.lb--long')),
|
||||||
this.error = ''
|
|
||||||
this.fetchPage(vnode)
|
|
||||||
},
|
|
||||||
}, 'Page error: ' + this.error + '. Click here to try again'))
|
|
||||||
: null,
|
|
||||||
(featuredBanner
|
|
||||||
? m(m.route.Link, {
|
|
||||||
class: 'page-banner',
|
|
||||||
href: featuredBanner.path,
|
|
||||||
style: { 'background-image': 'url("' + featuredBanner.banner + '")' },
|
|
||||||
},
|
|
||||||
m('div.inside', m('div.page-banner-title', featuredBanner.name))
|
|
||||||
)
|
|
||||||
: null),
|
|
||||||
(!featuredBanner && pageBanner
|
|
||||||
? m('a.page-banner', {
|
|
||||||
href: pageBanner.original,
|
|
||||||
target: '_blank',
|
|
||||||
style: { 'background-image': 'url("' + pageBanner.banner + '")' },
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: null),
|
|
||||||
(page
|
|
||||||
? m('.inside.vertical', [
|
|
||||||
m('div.actions', [
|
|
||||||
'« ',
|
|
||||||
m(m.route.Link, {
|
|
||||||
href: page.parent_path
|
|
||||||
? '/page/' + page.parent_path
|
|
||||||
: '/'
|
|
||||||
}, page.parent_name || 'Home'),
|
|
||||||
Authentication.currentUser
|
|
||||||
? [
|
|
||||||
m('div.filler'),
|
|
||||||
'Actions:',
|
|
||||||
m(m.route.Link, { href: '/admin/pages/' + page.id }, 'Edit page'),
|
|
||||||
]
|
|
||||||
: null,
|
|
||||||
]),
|
|
||||||
m('h2.title', page.name)
|
|
||||||
])
|
|
||||||
: null),
|
|
||||||
(page || this.data.articles.length
|
|
||||||
? m('.inside', [
|
|
||||||
this.children.length
|
|
||||||
? m('aside', { class: page ? '' : 'frontpage' }, [
|
|
||||||
m('h5', page ? 'View ' + page.name + ':' : 'Categories'),
|
|
||||||
this.children.map((page) => {
|
|
||||||
return [
|
|
||||||
m(m.route.Link, { class: 'root', href: '/page/' + page.path }, page.name),
|
|
||||||
(page.children && page.children.length
|
|
||||||
? m('ul', page.children.map(function(subpage) {
|
|
||||||
return m('li', m(m.route.Link, { class: 'child', href: '/page/' + subpage.path }, subpage.name))
|
|
||||||
}))
|
|
||||||
: null),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
!page
|
|
||||||
? m('div.asuna.spritesheet')
|
|
||||||
: null,
|
|
||||||
])
|
])
|
||||||
: null,
|
: null,
|
||||||
m('div.container', [
|
m('div.inside', [
|
||||||
(page
|
this.children.length
|
||||||
? media.getArticlePicture(this.picture, false, page.media_path, 'Image for page ' + page.name)
|
? m('aside', [
|
||||||
: null),
|
m('h5', m('div.lb-main')),
|
||||||
(page && page.content
|
m('div.lb-link'),
|
||||||
? m('div.content', page.content.blocks.map(block => {
|
m('ul', [
|
||||||
return m(EditorBlock, { block: block })
|
m('li', m('div.lb-link')),
|
||||||
}))
|
m('li', m('div.lb-link')),
|
||||||
: null),
|
m('li', m('div.lb-link')),
|
||||||
(page && this.data.articles.length
|
]),
|
||||||
? [
|
m('div.lb-link'),
|
||||||
m('h5', 'Latest posts under ' + page.name + ':'),
|
m('ul', [
|
||||||
this.data.articles.map(function(article) {
|
m('li', m('div.lb-link')),
|
||||||
return m(Articleslim, { article: article })
|
m('li', m('div.lb-link')),
|
||||||
}),
|
m('li', m('div.lb-link')),
|
||||||
]
|
]),
|
||||||
: null),
|
])
|
||||||
(!page && this.data.articles.length
|
: null,
|
||||||
? this.data.articles.map(function(article) {
|
!this.path
|
||||||
return m(Article, { article: article })
|
? m('div.container', [
|
||||||
})
|
m('article', [
|
||||||
: null),
|
m('h2.title', m('div.lb-main.lb--long')),
|
||||||
m(Paginator, {
|
m('div.row', [
|
||||||
base: page ? '/page/' + page.path : '/',
|
m('div.lb-main.lb--imgmini'),
|
||||||
page: this.currentPage,
|
m('div', [
|
||||||
perPage: ArticlesPerPage,
|
m('div', m.trust(' ')),
|
||||||
total: this.data.total_articles,
|
m('div.lb-main.lb--long'),
|
||||||
}),
|
m('div.lb-main'),
|
||||||
|
m('div.lb-main.lb--medium'),
|
||||||
|
m('div.lb-main.lb--medium'),
|
||||||
|
m('div.lb-main'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
m('article', [
|
||||||
|
m('h2.title', m('div.lb-main.lb--long')),
|
||||||
|
m('div.row', [
|
||||||
|
m('div.lb-main.lb--imgmini'),
|
||||||
|
m('div', [
|
||||||
|
m('div', m.trust(' ')),
|
||||||
|
m('div.lb-main.lb--long'),
|
||||||
|
m('div.lb-main'),
|
||||||
|
m('div.lb-main.lb--medium'),
|
||||||
|
m('div.lb-main.lb--medium'),
|
||||||
|
m('div.lb-main'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
: null,
|
||||||
|
this.path
|
||||||
|
? m('div.container', [
|
||||||
|
this.treePage.has_media
|
||||||
|
? m('div.cover', m('picture.lb.nobg'))
|
||||||
|
: null,
|
||||||
|
m('h5', m('div.lb-main.lb--long')),
|
||||||
|
m('articleslim', [
|
||||||
|
m('a.cover.nobg.lb'),
|
||||||
|
m('div', [
|
||||||
|
m('div', m('div.lb-link.lb--long')),
|
||||||
|
m('div.lb-main.lb--longest.lb--slim'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
m('articleslim', [
|
||||||
|
m('a.cover.nobg.lb'),
|
||||||
|
m('div', [
|
||||||
|
m('div', m('div.lb-link.lb--long')),
|
||||||
|
m('div.lb-main.lb--longest.lb--slim'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
m('articleslim', [
|
||||||
|
m('a.cover.nobg.lb'),
|
||||||
|
m('div', [
|
||||||
|
m('div', m('div.lb-link.lb--long')),
|
||||||
|
m('div.lb-main.lb--longest.lb--slim'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
m('articleslim', [
|
||||||
|
m('a.cover.nobg.lb'),
|
||||||
|
m('div', [
|
||||||
|
m('div', m('div.lb-link.lb--long')),
|
||||||
|
m('div.lb-main.lb--longest.lb--slim'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
: null,
|
||||||
]),
|
]),
|
||||||
])
|
]
|
||||||
: null),
|
: [
|
||||||
|
this.error === 'Page not found'
|
||||||
|
? NotFoundView.view()
|
||||||
|
: null,
|
||||||
|
this.error && this.error !== 'Page not found'
|
||||||
|
? m('div.wrapper', m('div.error', {
|
||||||
|
onclick: () => {
|
||||||
|
this.error = ''
|
||||||
|
this.fetchPage(vnode)
|
||||||
|
},
|
||||||
|
}, 'Page error: ' + this.error + '. Click here to try again'))
|
||||||
|
: null,
|
||||||
|
(featuredBanner
|
||||||
|
? m(m.route.Link, {
|
||||||
|
class: 'page-banner',
|
||||||
|
href: featuredBanner.path,
|
||||||
|
style: { 'background-image': 'url("' + featuredBanner.preview + '")' },
|
||||||
|
}, [
|
||||||
|
m('div.page-banner-real', {
|
||||||
|
style: { 'background-image': 'url("' + featuredBanner.banner + '")' },
|
||||||
|
}, m('div.inside', m('div.page-banner-title', featuredBanner.name))),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
: null),
|
||||||
|
(!featuredBanner && pageBanner
|
||||||
|
? m('a.page-banner', {
|
||||||
|
href: pageBanner.original,
|
||||||
|
target: '_blank',
|
||||||
|
style: { 'background-image': 'url("' + pageBanner.preview + '")' },
|
||||||
|
},
|
||||||
|
m('div.page-banner-real', {
|
||||||
|
style: { 'background-image': 'url("' + pageBanner.banner + '")' },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
: null),
|
||||||
|
(page
|
||||||
|
? m('.inside.vertical', [
|
||||||
|
m('div.actions', [
|
||||||
|
'« ',
|
||||||
|
m(m.route.Link, {
|
||||||
|
href: page.parent_path
|
||||||
|
? '/page/' + page.parent_path
|
||||||
|
: '/'
|
||||||
|
}, page.parent_name || 'Home'),
|
||||||
|
Authentication.currentUser
|
||||||
|
? [
|
||||||
|
m('div.filler'),
|
||||||
|
'Actions:',
|
||||||
|
m(m.route.Link, { href: '/admin/pages/' + page.id }, 'Edit page'),
|
||||||
|
]
|
||||||
|
: null,
|
||||||
|
]),
|
||||||
|
m('h2.title', page.name)
|
||||||
|
])
|
||||||
|
: null),
|
||||||
|
(page || this.data.articles.length
|
||||||
|
? m('.inside', [
|
||||||
|
this.children.length
|
||||||
|
? m('aside', { class: page ? '' : 'frontpage' }, [
|
||||||
|
m('h5', page ? 'View ' + page.name + ':' : 'Categories'),
|
||||||
|
this.children.map((page) => {
|
||||||
|
return [
|
||||||
|
m(m.route.Link, { class: 'root', href: '/page/' + page.path }, page.name),
|
||||||
|
(page.children && page.children.length
|
||||||
|
? m('ul', page.children.map(function(subpage) {
|
||||||
|
return m('li', m(m.route.Link, { class: 'child', href: '/page/' + subpage.path }, subpage.name))
|
||||||
|
}))
|
||||||
|
: null),
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
!page
|
||||||
|
? m('div.asuna.spritesheet')
|
||||||
|
: null,
|
||||||
|
])
|
||||||
|
: null,
|
||||||
|
m('div.container', [
|
||||||
|
(page
|
||||||
|
? media.getArticlePicture(this.picture, false, page.media_path, 'Image for page ' + page.name)
|
||||||
|
: null),
|
||||||
|
(page && page.content
|
||||||
|
? m('div.content', page.content.blocks.map(block => {
|
||||||
|
return m(EditorBlock, { block: block })
|
||||||
|
}))
|
||||||
|
: null),
|
||||||
|
(page && this.data.articles.length
|
||||||
|
? [
|
||||||
|
m('h5', 'Latest posts under ' + page.name + ':'),
|
||||||
|
this.data.articles.map(function(article) {
|
||||||
|
return m(Articleslim, { article: article })
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: null),
|
||||||
|
(!page && this.data.articles.length
|
||||||
|
? this.data.articles.map(function(article) {
|
||||||
|
return m(Article, { article: article })
|
||||||
|
})
|
||||||
|
: null),
|
||||||
|
m(Paginator, {
|
||||||
|
base: page ? '/page/' + page.path : '/',
|
||||||
|
page: this.currentPage,
|
||||||
|
perPage: ArticlesPerPage,
|
||||||
|
total: this.data.total_articles,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
: null),
|
||||||
|
],
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,6 +306,29 @@ dialogue button.cancel {
|
||||||
min-height: 80px !important;
|
min-height: 80px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes spinner-loader {
|
||||||
|
to {transform: rotate(360deg);}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinner:after {
|
||||||
|
content: '';
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-left: -10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid #ccc;
|
||||||
|
border-top-color: #333;
|
||||||
|
animation: spinner-loader .6s linear infinite;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===================== 3rd party =====================
|
===================== 3rd party =====================
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,858 +1,3 @@
|
||||||
/*
|
|
||||||
===================== Variables =====================
|
|
||||||
*/
|
|
||||||
:root {
|
|
||||||
--content-max-width: 1280px;
|
|
||||||
--primary-darker-bg: #002f6c;
|
|
||||||
--primary-darker-fg: #fff;
|
|
||||||
--primary-darker-fg-light: #999;
|
|
||||||
--primary-darker-link: #ffad42;
|
|
||||||
|
|
||||||
--primary-bg: #3d77c7;
|
|
||||||
--primary-fg: #fff;
|
|
||||||
--primary-fg-light: #999;
|
|
||||||
--primary-link: #f57c00;
|
|
||||||
|
|
||||||
--bg: #fff;
|
|
||||||
--bg-content-alt: #eee;
|
|
||||||
--color: #000;
|
|
||||||
--light: #757575;
|
|
||||||
--link: #bb4d00;
|
|
||||||
--title-bg: #f57c00;
|
|
||||||
--title-fg: #000;
|
|
||||||
--seperator: #ccc;
|
|
||||||
--content-bg: #fff;
|
|
||||||
--content-border: 0px solid transparent;
|
|
||||||
|
|
||||||
--alt-bg: #ccc;
|
|
||||||
--alt-inside-bg: #fff;
|
|
||||||
--alt-inside-border: 1px solid #555;
|
|
||||||
--alt-color: #555;
|
|
||||||
|
|
||||||
--footer-bg: #ccc;
|
|
||||||
--footer-color: #000;
|
|
||||||
--footer-seperator: #fff;
|
|
||||||
--footer-link: #8F3C00;
|
|
||||||
|
|
||||||
--button-border: 1px solid #f57c00;
|
|
||||||
--button-bg: #ffad42;
|
|
||||||
--button-fg: #000;
|
|
||||||
|
|
||||||
--error-bg: red;
|
|
||||||
--error-fg: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nightmode {
|
|
||||||
--content-max-width: 1280px;
|
|
||||||
--primary-darker-bg: #002f6c;
|
|
||||||
--primary-darker-fg: #fff;
|
|
||||||
--primary-darker-fg-light: #999;
|
|
||||||
--primary-darker-link: #ffad42;
|
|
||||||
|
|
||||||
--primary-bg: #28518b;
|
|
||||||
--primary-fg: #fff;
|
|
||||||
--primary-fg-light: #999;
|
|
||||||
--primary-link: #f57c00;
|
|
||||||
|
|
||||||
--bg: black;
|
|
||||||
--bg-content-alt: #333;
|
|
||||||
--color: #d7dadc;
|
|
||||||
--light: #bbb;
|
|
||||||
--link: #e05e00;
|
|
||||||
--title-bg: #e05e00;
|
|
||||||
--title-fg: #000;
|
|
||||||
--title-sublink: #27159C;
|
|
||||||
--seperator: #ccc;
|
|
||||||
--content-bg: #1a1a1b;
|
|
||||||
--content-border: 1px solid #343536;
|
|
||||||
|
|
||||||
--alt-bg: #000;
|
|
||||||
--alt-inside-bg: #343536;
|
|
||||||
--alt-inside-border: 1px solid #808080;
|
|
||||||
--alt-color: #d7dadc;
|
|
||||||
|
|
||||||
--footer-bg: #343536;
|
|
||||||
--footer-color: #d7dadc;
|
|
||||||
--footer-seperator: #666;
|
|
||||||
--footer-link: #fe791b;
|
|
||||||
|
|
||||||
--button-border: 1px solid #f57c00;
|
|
||||||
--button-bg: #ffad42;
|
|
||||||
--button-fg: #000;
|
|
||||||
|
|
||||||
--error-bg: red;
|
|
||||||
--error-fg: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
===================== Reset =====================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Box sizing rules */
|
|
||||||
*, *::before, *::after { box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove default margin */
|
|
||||||
body, h1, h2, h3, h4, p, figure, blockquote, dl, dd {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
min-height: 100vh;
|
|
||||||
text-rendering: optimizeSpeed;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-size: 16px;
|
|
||||||
font-family: 'Inter var', sans-serif;
|
|
||||||
font-feature-settings: "slnt" 0deg, "case", "frac", "tnum", "ss02", "calt", "ccmp", "kern";
|
|
||||||
background: var(--bg);
|
|
||||||
color: var(--color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.italic { font-variation-settings: "slnt" 10deg; }
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration-skip-ink: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 0 auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
input, button, textarea, select {
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
*, *::before, *::after {
|
|
||||||
animation-play-state: paused !important;
|
|
||||||
transition: none !important;
|
|
||||||
scroll-behavior: auto !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2.488rem;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 2.074rem;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: 1.728rem;
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
font-size: 1.44rem;
|
|
||||||
}
|
|
||||||
h5 {
|
|
||||||
font-size: 1.0rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:visited, button {
|
|
||||||
text-decoration: none;
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
font-weight: bold;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text],
|
|
||||||
input[type=password],
|
|
||||||
select {
|
|
||||||
border: 1px solid var(--color);
|
|
||||||
background: var(--bg);
|
|
||||||
color: var(--color);
|
|
||||||
border-radius: 0;
|
|
||||||
padding: 0.25rem;
|
|
||||||
line-height: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-top: 1rem;
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text]:hover,
|
|
||||||
input[type=text]:active,
|
|
||||||
input[type=password]:hover,
|
|
||||||
input[type=password]:active,
|
|
||||||
select:hover,
|
|
||||||
select:active {
|
|
||||||
border-color: var(--link);
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
===================== Common =====================
|
|
||||||
*/
|
|
||||||
|
|
||||||
.inside {
|
|
||||||
width: 100%;
|
|
||||||
max-width: var(--content-max-width);
|
|
||||||
display: flex;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inside.vertical {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
background: var(--error-bg);
|
|
||||||
color: var(--error-fg);
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: center;
|
|
||||||
padding: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
background: var(--alt-bg);
|
|
||||||
color: var(--alt-color);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-height: calc(100vh - 200px);
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filler {
|
|
||||||
flex: 2 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper .inside {
|
|
||||||
flex-direction: column;
|
|
||||||
color: var(--alt-color);
|
|
||||||
background: var(--alt-inside-bg);
|
|
||||||
border: var(--alt-inside-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper .error {
|
|
||||||
border: 1px solid var(--error-bg);
|
|
||||||
color: var(--error-bg);
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spinner-loader {
|
|
||||||
to {transform: rotate(360deg);}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-spinner:after {
|
|
||||||
content: '';
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
margin-top: -10px;
|
|
||||||
margin-left: -10px;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 2px solid #ccc;
|
|
||||||
border-top-color: #333;
|
|
||||||
animation: spinner-loader .6s linear infinite;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notfound {
|
|
||||||
color: var(--light);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 639px){
|
|
||||||
main .inside {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
===================== Header =====================
|
|
||||||
*/
|
|
||||||
|
|
||||||
header {
|
|
||||||
background: var(--primary-darker-bg);
|
|
||||||
color: var(--primary-darker-fg);
|
|
||||||
}
|
|
||||||
|
|
||||||
header a,
|
|
||||||
header a:visited,
|
|
||||||
header button {
|
|
||||||
color: var(--primary-darker-link);
|
|
||||||
}
|
|
||||||
|
|
||||||
header p {
|
|
||||||
color: var(--primary-darker-fg-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
header .title,
|
|
||||||
header .title:visited {
|
|
||||||
min-height: 100px;
|
|
||||||
padding-left: 10px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background: 25px center no-repeat;
|
|
||||||
background-size: auto 91px;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .logo {
|
|
||||||
background-position: -119px 0px;
|
|
||||||
width: 81px;
|
|
||||||
height: 100px;
|
|
||||||
transform: scale(0.9);
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .title h1 {
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--primary-darker-fg);
|
|
||||||
}
|
|
||||||
|
|
||||||
header aside {
|
|
||||||
flex: 2 1 auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-end;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
padding: 0.5rem 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
header aside a,
|
|
||||||
header aside button {
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
header aside p button {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
===================== Nav =====================
|
|
||||||
*/
|
|
||||||
|
|
||||||
nav {
|
|
||||||
background: var(--primary-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav a, nav a:visited, nav .loading-spinner {
|
|
||||||
flex: 2 0 auto;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: 300;
|
|
||||||
padding: 10px 10px 7px 10px;
|
|
||||||
border-bottom: 3px solid var(--primary-bg);
|
|
||||||
color: var(--primary-fg);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav a.active {
|
|
||||||
border-bottom-color: var(--primary-link);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav .loading-spinner {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 639px){
|
|
||||||
nav {
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
===================== main =====================
|
|
||||||
*/
|
|
||||||
|
|
||||||
main {
|
|
||||||
min-height: calc(100vh - 390px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-banner {
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
height: 150px;
|
|
||||||
width: 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-banner-title {
|
|
||||||
color: white;
|
|
||||||
text-align: right;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
font-size: 1.6rem;
|
|
||||||
flex: 2 1 auto;
|
|
||||||
text-shadow: 0 0 .3em #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions a {
|
|
||||||
margin-left: 0.375rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
main a,
|
|
||||||
main a:visited {
|
|
||||||
color: var(--link);
|
|
||||||
}
|
|
||||||
|
|
||||||
main h5 {
|
|
||||||
padding: 0 0.5rem 0.5rem;
|
|
||||||
margin: 0 0 0.75rem;
|
|
||||||
border-bottom: 1px solid var(--seperator);
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
main .loading-spinner {
|
|
||||||
position: fixed;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
main h2.title,
|
|
||||||
.main h2.title {
|
|
||||||
font-size: 1.4rem;
|
|
||||||
background: var(--title-bg);
|
|
||||||
color: var(--title-fg);
|
|
||||||
text-align: center;
|
|
||||||
font-weight: 400;
|
|
||||||
padding: 0.375rem;
|
|
||||||
line-height: 1.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
main .container {
|
|
||||||
flex: 2 1 auto;
|
|
||||||
margin: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
main .cover picture img {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
main button,
|
|
||||||
main input[type=submit] {
|
|
||||||
border: var(--button-border);
|
|
||||||
background: #ffad42;
|
|
||||||
color: #000;
|
|
||||||
align-self: center;
|
|
||||||
padding: 0.25rem 1rem;
|
|
||||||
margin: 1rem 0 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 639px){
|
|
||||||
main .container {
|
|
||||||
margin: 1rem 0.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************** aside ************** */
|
|
||||||
|
|
||||||
main aside {
|
|
||||||
padding: 0.375rem 1rem 0.5rem;
|
|
||||||
margin: 1rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
flex: 0 0 250px;
|
|
||||||
background: var(--content-bg);
|
|
||||||
border: var(--content-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
main aside a {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
main aside h5 {
|
|
||||||
margin: 0 -0.5rem 0.25rem;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
main aside ul {
|
|
||||||
margin: 0 0 0.5rem;
|
|
||||||
padding-left: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
main aside .asuna {
|
|
||||||
margin-top: 2rem;
|
|
||||||
width: 200px;
|
|
||||||
height: 461px;
|
|
||||||
background-position: 0 -150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nightmode main aside .asuna {
|
|
||||||
background-position: -200px -150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.daymode .day {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 1000px){
|
|
||||||
main aside {
|
|
||||||
flex: 0 0 200px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 639px){
|
|
||||||
main aside {
|
|
||||||
margin: 1rem 0.25rem;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
main aside.frontpage {
|
|
||||||
order: 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ************** paginator ************** */
|
|
||||||
|
|
||||||
paginator {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
paginator a {
|
|
||||||
color: var(--link);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
paginator a,
|
|
||||||
paginator div {
|
|
||||||
display: block;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
max-width: 80px;
|
|
||||||
flex-grow: 2;
|
|
||||||
text-align: center;
|
|
||||||
padding: 0.5rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************** articleslim ************** */
|
|
||||||
|
|
||||||
articleslim {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
padding-right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
articleslim p.description {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
articleslim .cover {
|
|
||||||
flex: 0 0 124px;
|
|
||||||
margin-right: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
articleslim .cover picture img {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
articleslim a.nobg {
|
|
||||||
height: 70px;
|
|
||||||
background: var(--seperator);
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
articleslim a.title {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 0.375rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************** article ************** */
|
|
||||||
|
|
||||||
article {
|
|
||||||
background: var(--content-bg);
|
|
||||||
border: var(--content-border);
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
article .row {
|
|
||||||
margin: 1rem 0;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
article .cover {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
margin-right: 1rem;
|
|
||||||
align-self: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
article a.title {
|
|
||||||
flex: 0 0 100%;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
article .description {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
padding: 0 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
article .meta {
|
|
||||||
font-size: 0.625rem;
|
|
||||||
line-height: 0.75rem;
|
|
||||||
color: var(--light);
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 1.25rem 0.25rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
article.fullsize .row {
|
|
||||||
margin: 1rem;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
article.fullsize .cover {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 1000px){
|
|
||||||
article .row {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
article.fullsize .row {
|
|
||||||
margin: 1rem 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
article .cover {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************** fileinfo ************** */
|
|
||||||
|
|
||||||
fileinfo {
|
|
||||||
padding-left: 0.25rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
color: var(--light);
|
|
||||||
line-height: 1rem;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileinfo.slim {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileinfo p span,
|
|
||||||
fileinfo p a {
|
|
||||||
margin-right: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileinfo p a {
|
|
||||||
font-weight: 550;
|
|
||||||
padding-right: 0.25rem;
|
|
||||||
border-right: 1px solid var(--seperator);
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileinfo p span {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileinfo .trimmed {
|
|
||||||
padding: 0.25rem 0 0.25rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileinfo ul {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
padding-left: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
===================== login =====================
|
|
||||||
*/
|
|
||||||
|
|
||||||
.login--first {
|
|
||||||
flex: 0 0 170px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login {
|
|
||||||
align-items: center;
|
|
||||||
font-size: 1rem;
|
|
||||||
padding: 1rem 1rem 2rem;
|
|
||||||
margin: 1rem;
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login .title {
|
|
||||||
font-size: 1.4rem;
|
|
||||||
font-weight: 200;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login input,
|
|
||||||
.login label {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login input[type=submit] {
|
|
||||||
min-width: 150px;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login--asuna {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
width: 180px;
|
|
||||||
height: 494px;
|
|
||||||
background-position: -400px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nightmode .login--asuna {
|
|
||||||
background-position: -580px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 1000px){
|
|
||||||
.login--first {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 639px){
|
|
||||||
.login {
|
|
||||||
order: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login--asuna {
|
|
||||||
max-width: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
===================== content =====================
|
|
||||||
*/
|
|
||||||
|
|
||||||
.content :is(h1, h2, h3, h4, h5, ul, ol, blockquote, p) {
|
|
||||||
margin: 0 0 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content :is(h1, h2, h3, h4, h5) {
|
|
||||||
padding: 0 0.5rem 0.5rem;
|
|
||||||
border-bottom: 1px solid var(--seperator);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content :is(blockquote, pre) {
|
|
||||||
background: var(--bg-content-alt);
|
|
||||||
padding: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content blockquote p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
===================== footer =====================
|
|
||||||
*/
|
|
||||||
|
|
||||||
footer {
|
|
||||||
background: var(--footer-bg);
|
|
||||||
color: var(--footer-color);
|
|
||||||
text-align: center;
|
|
||||||
padding: 1rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 0.625rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer .first {
|
|
||||||
flex: 0 0 119px;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer .middle {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
flex: 2 1 auto;
|
|
||||||
padding: 0 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer .asuna {
|
|
||||||
flex: 0 0 119px;
|
|
||||||
height: 150px;
|
|
||||||
width: 119px;
|
|
||||||
background-position: 0px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer ul {
|
|
||||||
margin: 0 0 0.25rem;
|
|
||||||
padding: 0 0 0.25rem;
|
|
||||||
border-bottom: 1px solid var(--footer-seperator);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
min-width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer ul li {
|
|
||||||
padding: 0 0.25rem;
|
|
||||||
list-style-position: inside;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer a {
|
|
||||||
color: var(--footer-link);
|
|
||||||
margin: 0 0 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 1000px){
|
|
||||||
footer .first {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 639px){
|
|
||||||
footer{
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer .middle {
|
|
||||||
padding: 0 0 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer .asuna {
|
|
||||||
flex: 0 0 150px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
===================== 404 page =====================
|
|
||||||
*/
|
|
||||||
|
|
||||||
.not_found {
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.not_found .asuna {
|
|
||||||
width: 120px;
|
|
||||||
height: 444px;
|
|
||||||
margin: 2rem 0 0rem;
|
|
||||||
background-position: -760px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nightmode .not_found .asuna {
|
|
||||||
background-position: -880px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===================== Large assets =====================
|
===================== Large assets =====================
|
||||||
|
|
1039
nfp_moe/public/assets/app_body.css
Normal file
1039
nfp_moe/public/assets/app_body.css
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3 KiB |
Binary file not shown.
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue