Compare commits
46 Commits
discord_em
...
master
Author | SHA1 | Date |
---|---|---|
Jonatan Nilsson | 852f37dc8d | |
Jonatan Nilsson | 14a3bc3123 | |
Jonatan Nilsson | ab2e7b93c4 | |
Jonatan Nilsson | 2e7b3be8d5 | |
Jonatan Nilsson | 533e279b0b | |
Jonatan Nilsson | e75719682e | |
Jonatan Nilsson | c55f0c9a02 | |
Jonatan Nilsson | 0b686a462f | |
Jonatan Nilsson | 329a3e267c | |
Jonatan Nilsson | ab9ed32196 | |
Jonatan Nilsson | 3a6996bfbb | |
TheThing | bdeeff3794 | |
Jonatan Nilsson | fad7acd5f7 | |
Jonatan Nilsson | 2d7101d666 | |
Jonatan Nilsson | 16b87aabcf | |
Jonatan Nilsson | 857a087410 | |
TheThing | ec7ade938f | |
Jonatan Nilsson | 638e6cc435 | |
Jonatan Nilsson | 531c7acefe | |
TheThing | a5c7e53802 | |
TheThing | d1730974dc | |
TheThing | 4216e036e4 | |
TheThing | 825cd7ef2d | |
Jonatan Nilsson | 5baf1823f4 | |
Jonatan Nilsson | 285ad3dc64 | |
Jonatan Nilsson | 9e56095773 | |
Jonatan Nilsson | de0a8b6f00 | |
Jonatan Nilsson | 86394efb1f | |
Jonatan Nilsson | 3dbcc18da8 | |
Jonatan Nilsson | f6d87c647f | |
Jonatan Nilsson | e5a1432443 | |
Jonatan Nilsson | 324623d717 | |
Jonatan Nilsson | aad55ae9f0 | |
Jonatan Nilsson | 70d14b24aa | |
Jonatan Nilsson | 5e31e785df | |
Jonatan Nilsson | d9ca9bcc52 | |
Jonatan Nilsson | 6f39e97977 | |
Jonatan Nilsson | 972dde2c74 | |
Jonatan Nilsson | ddb66d9836 | |
Jonatan Nilsson | 7f4dce5920 | |
Jonatan Nilsson | 3b1a1b7bf6 | |
Jonatan Nilsson | 840f23908e | |
Jonatan Nilsson | 31f2ecc09b | |
Jonatan Nilsson | aa324cf0f1 | |
Jonatan Nilsson | 9e642afef6 | |
Jonatan Nilsson | 82b5e1bb5a |
|
@ -0,0 +1,79 @@
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: alpine
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- name: Check for new release
|
||||
run: |
|
||||
chmod +x ./7zas
|
||||
echo ""
|
||||
echo "Checking following projects:"
|
||||
for f in *; do
|
||||
[ -d "$f" ] && [ ! -L "$f" ] && [ ! "$f" = "base" ] && echo " * $f";
|
||||
done
|
||||
echo ""
|
||||
|
||||
|
||||
for f in *; do
|
||||
[ ! -d "$f" ] || [ -L "$f" ] || [ "$f" = "base" ] && continue;
|
||||
|
||||
echo ""
|
||||
echo "------------------------------------"
|
||||
echo ""
|
||||
echo "checking $f";
|
||||
cd $f
|
||||
|
||||
CURR_VER="$(cat package.json | jq -r .name)_v$(cat package.json | jq -r .version)"
|
||||
CURR_NAME="$(cat package.json | jq -r .name) v$(cat package.json | jq -r .version)"
|
||||
|
||||
echo "Checking https://git.nfp.is/api/v1/repos/${{ gitea.repository }}/releases for name ${CURR_NAME}"
|
||||
|
||||
if curl -s -X GET -H "Authorization: token ${{ secrets.deploytoken }}" https://git.nfp.is/api/v1/repos/${{ gitea.repository }}/releases | grep -o "\"name\"\:\"${CURR_NAME}\"" > /dev/null; then
|
||||
echo "Skipping ${{ gitea.job }} since $CURR_NAME already exists";
|
||||
cd ..
|
||||
continue;
|
||||
fi
|
||||
|
||||
echo "New release ${CURR_VER} found, running npm install..."
|
||||
|
||||
mv package.json fuck-you-npm-package.json
|
||||
mv build-package.json package.json
|
||||
npm install && npm run build
|
||||
|
||||
mv package.json build-package.json
|
||||
mv fuck-you-npm-package.json package.json
|
||||
|
||||
../7zas a -xr!*.xcf -mx9 "${CURR_VER}_build-sc.7z" package.json index.mjs api base public
|
||||
|
||||
echo "Creating ${CURR_VER} release on gitea"
|
||||
RELEASE_RESULT=$(curl \
|
||||
-X POST \
|
||||
-H "Authorization: token ${{ secrets.deploytoken }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
https://git.nfp.is/api/v1/repos/${{ gitea.repository }}/releases \
|
||||
-d "{\"tag_name\":\"${CURR_VER}\",\"name\":\"${CURR_NAME}\",\"body\":\"Automatic release from Appveyor from ${{ gitea.sha }} :\n\n${{ gitea.event.head_commit.message }}\"}")
|
||||
|
||||
RELEASE_ID=$(echo $RELEASE_RESULT | jq -r .id)
|
||||
echo "Adding ${CURR_VER}_build-sc.7z to release ${RELEASE_ID}"
|
||||
|
||||
curl \
|
||||
-X POST \
|
||||
-H "Authorization: token ${{ secrets.deploytoken }}" \
|
||||
-F "attachment=@${CURR_VER}_build-sc.7z" \
|
||||
https://git.nfp.is/api/v1/repos/${{ gitea.repository }}/releases/$RELEASE_ID/assets
|
||||
|
||||
MAN_PORT=$(cat package.json | jq -r .port)
|
||||
MAN_NAME=$(cat package.json | jq -r .name)
|
||||
|
||||
echo "Deplying to production"
|
||||
echo "curl -X POST http://192.168.93.51:$MAN_PORT/update/$MAN_NAME"
|
||||
curl -X POST http://192.168.93.51:$MAN_PORT/update/$MAN_NAME
|
||||
|
||||
cd ..
|
||||
done
|
127
appveyor.yml
|
@ -1,127 +0,0 @@
|
|||
# version format
|
||||
version: '{build}'
|
||||
deploy: on
|
||||
|
||||
# branches to build
|
||||
branches:
|
||||
# whitelist
|
||||
only:
|
||||
- master
|
||||
|
||||
# Do not build on tags (GitHub, Bitbucket, GitLab, Gitea)
|
||||
skip_tags: true
|
||||
|
||||
# Maximum number of concurrent jobs for the project
|
||||
max_jobs: 1
|
||||
clone_depth: 1
|
||||
|
||||
# Build worker image (VM template)
|
||||
build_cloud: Docker
|
||||
|
||||
environment:
|
||||
docker_image: node:16-alpine
|
||||
npm_config_cache: /appveyor/projects/cache
|
||||
|
||||
build_script:
|
||||
- sh: |
|
||||
chown -R node:node /appveyor/projects
|
||||
chmod -R 777 /appveyor/projects
|
||||
|
||||
apk add curl jq
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Finished installling curl and jq"
|
||||
|
||||
SUCCESS=1
|
||||
|
||||
echo ""
|
||||
echo "Checking following projects:"
|
||||
for f in *; do
|
||||
[ -d "$f" ] && [ ! -L "$f" ] && [ ! "$f" = "base" ] && echo " * $f";
|
||||
done
|
||||
echo ""
|
||||
|
||||
for f in *; do
|
||||
[ ! -d "$f" ] || [ -L "$f" ] || [ "$f" = "base" ] && continue;
|
||||
echo ""
|
||||
echo "------------------------------------"
|
||||
echo ""
|
||||
echo "checking $f";
|
||||
cd $f
|
||||
CURR_VER="$(cat package.json | jq -r .name)_v$(cat package.json | jq -r .version)"
|
||||
CURR_NAME="$(cat package.json | jq -r .name) v$(cat package.json | jq -r .version)"
|
||||
MAN_PORT=$(cat package.json | jq -r .port)
|
||||
MAN_NAME=$(cat package.json | jq -r .name)
|
||||
echo "Checking https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases for name ${CURR_NAME}"
|
||||
curl -s -X GET -H "Authorization: token $deploytoken" https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases | grep -o "\"name\"\:\"${CURR_NAME}\"" > /dev/null
|
||||
|
||||
if [ $? -eq 0 ] ; then
|
||||
echo "Skipping $f since $CURR_NAME already exists";
|
||||
cd ..
|
||||
continue;
|
||||
fi
|
||||
|
||||
rm base
|
||||
cp -Rf ../base ./base
|
||||
|
||||
mv package.json fuck-you-npm-package.json
|
||||
mv build-package.json package.json
|
||||
|
||||
npm install && npm run build
|
||||
CHECK=$?
|
||||
if [ $CHECK -ne 0 ]; then
|
||||
echo "Command failed with error code $CHECK"
|
||||
SUCCESS=0;
|
||||
cd ..
|
||||
continue;
|
||||
fi;
|
||||
|
||||
mv package.json build-package.json
|
||||
mv fuck-you-npm-package.json package.json
|
||||
|
||||
../7zas a -xr!*.xcf -mx9 "${CURR_VER}_build-sc.7z" package.json index.mjs api base public
|
||||
echo "Creating release on gitea"
|
||||
RELEASE_RESULT=$(curl \
|
||||
-X POST \
|
||||
-H "Authorization: token $deploytoken" \
|
||||
-H "Content-Type: application/json" \
|
||||
https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases \
|
||||
-d "{\"tag_name\":\"${CURR_VER}\",\"name\":\"${CURR_NAME}\",\"body\":\"Automatic release from Appveyor from ${APPVEYOR_REPO_COMMIT} :\n\n${APPVEYOR_REPO_COMMIT_MESSAGE}\"}")
|
||||
CHECK=$?
|
||||
if [ $CHECK -ne 0 ]; then
|
||||
echo "Command failed with error code $CHECK"
|
||||
SUCCESS=0;
|
||||
cd ..
|
||||
continue;
|
||||
fi;
|
||||
RELEASE_ID=$(echo $RELEASE_RESULT | jq -r .id)
|
||||
echo "Adding ${CURR_VER}_build-sc.7z to release ${RELEASE_ID}"
|
||||
curl \
|
||||
-X POST \
|
||||
-H "Authorization: token $deploytoken" \
|
||||
-F "attachment=@${CURR_VER}_build-sc.7z" \
|
||||
https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases/$RELEASE_ID/assets
|
||||
CHECK=$?
|
||||
if [ $CHECK -ne 0 ]; then
|
||||
echo "Command failed with error code $CHECK"
|
||||
SUCCESS=0;
|
||||
cd ..
|
||||
continue;
|
||||
fi;
|
||||
|
||||
echo "Deplying to production"
|
||||
echo "curl -X POST http://192.168.93.50:$MAN_PORT/update/$MAN_NAME"
|
||||
curl -X POST http://192.168.93.50:$MAN_PORT/update/$MAN_NAME
|
||||
cd ..
|
||||
done
|
||||
|
||||
if [ $SUCCESS -eq 0 ]; then
|
||||
echo "One or more jobs failed to build"
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
# on build failure
|
||||
on_failure:
|
||||
- sh: echo on_failure
|
|
@ -8,11 +8,14 @@ export default class ArticleRoutes {
|
|||
uploadMedia: uploadMedia,
|
||||
uploadFile: uploadFile,
|
||||
deleteFile: deleteFile,
|
||||
requireAuth: opts.requireAuth,
|
||||
})
|
||||
}
|
||||
|
||||
register(server) {
|
||||
server.flaska.get('/api/articles/:path', this.getArticle.bind(this))
|
||||
if (!this.requireAuth) {
|
||||
server.flaska.get('/api/articles/:path', this.getArticle.bind(this))
|
||||
}
|
||||
server.flaska.get('/api/auth/articles', server.authenticate(), this.auth_getAllArticles.bind(this))
|
||||
server.flaska.get('/api/auth/articles/:id', server.authenticate(), this.auth_getSingleArticle.bind(this))
|
||||
server.flaska.put('/api/auth/articles/:id', [
|
||||
|
@ -74,6 +77,7 @@ export default class ArticleRoutes {
|
|||
params = params.concat(mediaToDatabase(banner, body.remove_banner === 'true'))
|
||||
params = params.concat(mediaToDatabase(media, body.remove_media === 'true'))
|
||||
}
|
||||
|
||||
let res = await ctx.db.safeCallProc('article_auth_get_update_create', params)
|
||||
|
||||
ctx.body = this.private_getUpdateArticle_resOutput(res)
|
||||
|
@ -111,6 +115,10 @@ export default class ArticleRoutes {
|
|||
)
|
||||
}
|
||||
|
||||
if (ctx.req.body.media && ctx.req.body.media.filename && ctx.req.body.media.type && ctx.req.body.media.path && ctx.req.body.media.size) {
|
||||
newMedia = ctx.req.body.media
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
|
||||
return this.private_getUpdateArticle(ctx, ctx.req.body, newBanner, newMedia)
|
||||
|
|
|
@ -18,8 +18,8 @@ export default class AuthenticationRoutes {
|
|||
/** GET: /api/authentication/login */
|
||||
async login(ctx) {
|
||||
let res = await ctx.db.safeCallProc('auth_login', [
|
||||
ctx.req.body.email,
|
||||
ctx.req.body.password,
|
||||
ctx.req.body.email || '',
|
||||
ctx.req.body.password || '',
|
||||
])
|
||||
|
||||
let out = res.results[0][0]
|
||||
|
|
|
@ -53,6 +53,7 @@ nconf.defaults({
|
|||
"iss": "dev",
|
||||
"path": "https://media.nfp.is/media/resize",
|
||||
"filePath": "https://media.nfp.is/media",
|
||||
"directFilePath": "https://media.nfp.is/media/noprefix",
|
||||
"removePath": "https://media.nfp.is/media/",
|
||||
"preview": {
|
||||
"out": "base64",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import MSSQL from 'msnodesqlv8'
|
||||
import { HttpError } from 'flaska'
|
||||
import { HttpErrorInternal } from './error.mjs'
|
||||
|
||||
export function initPool(core, config) {
|
||||
let pool = new MSSQL.Pool(config)
|
||||
|
@ -32,7 +33,10 @@ export function initPool(core, config) {
|
|||
if (err.lineNumber && err.procName) {
|
||||
message = `Error at ${err.procName}:${err.lineNumber} => ${message}`
|
||||
}
|
||||
throw new HttpError(500, message)
|
||||
throw new HttpErrorInternal(message, err, !err.lineNumber ? {
|
||||
name,
|
||||
params
|
||||
} : null)
|
||||
})
|
||||
},
|
||||
promises: pool.promises,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import { HttpError } from 'flaska'
|
||||
|
||||
export class HttpErrorInternal extends HttpError {
|
||||
constructor(message, inner, extra) {
|
||||
super(500, message);
|
||||
|
||||
Error.captureStackTrace(this, HttpError);
|
||||
|
||||
let proto = Object.getPrototypeOf(this);
|
||||
proto.name = 'HttpErrorInternal';
|
||||
|
||||
this.inner = inner
|
||||
this.extra = extra
|
||||
}
|
||||
}
|
|
@ -98,6 +98,10 @@ export default class Client {
|
|||
}
|
||||
|
||||
createJwt(body, secret) {
|
||||
return Client.createJwt(body, secret)
|
||||
}
|
||||
|
||||
static createJwt(body, secret) {
|
||||
let header = {
|
||||
typ: 'JWT',
|
||||
alg: 'HS256',
|
||||
|
|
|
@ -15,8 +15,6 @@ export function uploadMedia(file) {
|
|||
}
|
||||
}
|
||||
|
||||
console.log(media)
|
||||
|
||||
let body = {}
|
||||
|
||||
if (media.preview) {
|
||||
|
|
|
@ -5,9 +5,9 @@ export function mediaToDatabase(media, removeFlag) {
|
|||
media.type,
|
||||
media.path,
|
||||
media.size,
|
||||
media.preview.base64,
|
||||
media.sizes.small.avif.path.replace(/_small\.avif$/, ''),
|
||||
JSON.stringify(media.sizes),
|
||||
media.preview?.base64 || null,
|
||||
media.sizes?.small?.avif?.path?.replace(/_small\.avif$/, '') || null,
|
||||
JSON.stringify(media.sizes || {}),
|
||||
removeFlag ? 1 : 0,
|
||||
]
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Flaska, QueryHandler, JsonHandler, FormidableHandler } from 'flaska'
|
||||
import { Flaska, QueryHandler, JsonHandler, FormidableHandler, HttpError } from 'flaska'
|
||||
import formidable from 'formidable'
|
||||
|
||||
import { initPool } from './db.mjs'
|
||||
import config from './config.mjs'
|
||||
import PageRoutes from './page/routes.mjs'
|
||||
import ArticleRoutes from './article/routes.mjs'
|
||||
|
@ -15,6 +14,7 @@ export default class Server {
|
|||
this.http = http
|
||||
this.port = port
|
||||
this.core = core
|
||||
this.pool = null
|
||||
|
||||
this.flaskaOptions = {
|
||||
appendHeaders: {
|
||||
|
@ -43,22 +43,36 @@ export default class Server {
|
|||
// Create our server
|
||||
this.flaska = new Flaska(this.flaskaOptions, this.http)
|
||||
|
||||
// Create our database pool
|
||||
let pool = this.runCreateDatabase()
|
||||
|
||||
// configure our server
|
||||
if (config.get('NODE_ENV') === 'development') {
|
||||
this.flaska.devMode()
|
||||
}
|
||||
|
||||
this.flaska.onerror((err, ctx) => {
|
||||
if (err instanceof HttpError && err.status !== 500) {
|
||||
ctx.status = err.status
|
||||
ctx.log.warn(err.message)
|
||||
} else {
|
||||
ctx.log.error(err.inner || err)
|
||||
if (err.extra) {
|
||||
ctx.log.error({ extra: err.extra }, 'Database parameters')
|
||||
}
|
||||
ctx.status = 500
|
||||
}
|
||||
ctx.body = {
|
||||
status: ctx.status,
|
||||
message: err.message,
|
||||
}
|
||||
})
|
||||
|
||||
this.flaska.before(function(ctx) {
|
||||
ctx.state.started = new Date().getTime()
|
||||
ctx.req.ip = ctx.req.headers['x-forwarded-for'] || ctx.req.connection.remoteAddress
|
||||
ctx.log = ctx.log.child({
|
||||
id: Math.random().toString(36).substring(2, 14),
|
||||
})
|
||||
ctx.db = pool
|
||||
})
|
||||
ctx.db = this.pool
|
||||
}.bind(this))
|
||||
this.flaska.before(QueryHandler())
|
||||
|
||||
let healthChecks = 0
|
||||
|
@ -109,7 +123,9 @@ export default class Server {
|
|||
}
|
||||
|
||||
runCreateDatabase() {
|
||||
return initPool(this.core, config.get('mssql'))
|
||||
return import('./db.mjs').then(db => {
|
||||
this.pool = db.initPool(this.core, config.get('mssql'))
|
||||
})
|
||||
}
|
||||
|
||||
runStartListen() {
|
||||
|
@ -119,9 +135,13 @@ export default class Server {
|
|||
}
|
||||
|
||||
run() {
|
||||
this.runCreateServer()
|
||||
this.runRegisterRoutes()
|
||||
|
||||
return this.runStartListen()
|
||||
return Promise.all([
|
||||
this.runCreateServer(),
|
||||
this.runCreateDatabase(),
|
||||
]).then(() => {
|
||||
return this.runRegisterRoutes()
|
||||
}).then(() => {
|
||||
return this.runStartListen()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,11 @@ export function decode(base64StringUrlSafe) {
|
|||
export function parseMediaAndBanner(item) {
|
||||
if (item.banner_path) {
|
||||
item.banner_path = 'https://cdn.nfp.is' + item.banner_path
|
||||
item.banner_alt_prefix = 'https://cdn.nfp.is' + item.banner_alt_prefix
|
||||
item.banner_alt_prefix = 'https://cdn.nfp.is' + (item.banner_alt_prefix || '')
|
||||
}
|
||||
if (item.media_path) {
|
||||
item.media_path = 'https://cdn.nfp.is' + item.media_path
|
||||
item.media_alt_prefix = 'https://cdn.nfp.is' + item.media_alt_prefix
|
||||
item.media_alt_prefix = 'https://cdn.nfp.is' + (item.media_alt_prefix || '')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,10 +60,6 @@ Redirecting
|
|||
if (!ctx.req.body.video.startsWith('http')) {
|
||||
return 'Video link has to be a valid full url'
|
||||
}
|
||||
|
||||
if (!ctx.req.body.image && !hasMedia) {
|
||||
return 'Missing image link or file'
|
||||
}
|
||||
|
||||
if (ctx.req.body.image) {
|
||||
if (!ctx.req.body.image.startsWith('http')) {
|
||||
|
|
|
@ -20,7 +20,7 @@ export default class ServeHandler extends Parent {
|
|||
'in_debug',
|
||||
'inputVideo',
|
||||
'inputImage'
|
||||
] })
|
||||
], strip: false })
|
||||
}
|
||||
|
||||
register(server) {
|
||||
|
@ -64,7 +64,7 @@ export default class ServeHandler extends Parent {
|
|||
}
|
||||
|
||||
let videoLink = ctx.query.get('v') || ''
|
||||
let imageLink = ctx.query.get('i') || ''
|
||||
let imageLink = ctx.query.get('i') || (videoLink ? 'https://cdn.nfp.is/av1/empty.png' : '')
|
||||
|
||||
if (!ctx.state.error) {
|
||||
if (ctx.url.match(/^\/[a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9]+$/) && ctx.url.length < 7) {
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
import fs from 'fs'
|
||||
import { ServiceCore } from 'service-core'
|
||||
import * as index from './index.mjs'
|
||||
|
||||
const port = 4120
|
||||
|
||||
var core = new ServiceCore('nfp_moe', import.meta.url, port, '')
|
||||
|
||||
let config = {
|
||||
frontend: {
|
||||
url: 'http://localhost:' + port
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
config = JSON.parse(fs.readFileSync('./config.json'))
|
||||
} catch {}
|
||||
|
||||
config.port = port
|
||||
|
||||
core.setConfig(config)
|
||||
core.init(index).then(function() {
|
||||
return core.run()
|
||||
})
|
|
@ -1,3 +1,5 @@
|
|||
import fs from 'fs'
|
||||
import { pathToFileURL } from 'url'
|
||||
import config from './base/config.mjs'
|
||||
|
||||
export function start(http, port, ctx) {
|
||||
|
@ -9,3 +11,28 @@ export function start(http, port, ctx) {
|
|||
return server.run()
|
||||
})
|
||||
}
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
import('service-core').then(core => {
|
||||
const port = 4120
|
||||
|
||||
var core = new core.ServiceCore('nfp_moe', import.meta.url, port, '')
|
||||
|
||||
let config = {
|
||||
frontend: {
|
||||
url: 'http://localhost:' + port
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
config = JSON.parse(fs.readFileSync('./config.json'))
|
||||
} catch {}
|
||||
|
||||
config.port = port
|
||||
|
||||
core.setConfig(config)
|
||||
core.init({ start }).then(function() {
|
||||
return core.run()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "discord_embed",
|
||||
"version": "1.0.15",
|
||||
"version": "1.0.26",
|
||||
"port": 4120,
|
||||
"description": "AV1 discord server embed helper",
|
||||
"main": "index.js",
|
||||
|
@ -8,8 +8,8 @@
|
|||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node --experimental-modules index.mjs",
|
||||
"dev:server": "node dev.mjs | bunyan",
|
||||
"start": "node index.mjs",
|
||||
"dev:server": "node index.mjs | bunyan",
|
||||
"dev": "npm-watch dev:server"
|
||||
},
|
||||
"watch": {
|
||||
|
@ -37,10 +37,10 @@
|
|||
"dependencies": {
|
||||
"bunyan-lite": "^1.2.1",
|
||||
"dot": "^2.0.0-beta.1",
|
||||
"flaska": "^1.3.2",
|
||||
"flaska": "^1.3.4",
|
||||
"formidable": "^1.2.6",
|
||||
"ioredis": "^5.2.3",
|
||||
"msnodesqlv8": "^2.4.7",
|
||||
"msnodesqlv8": "^4.1.1",
|
||||
"nconf-lite": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Discord Embedder from AV1 server 1.0.15</title>
|
||||
<title>Discord Embedder from AV1 server 1.0.25</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
{{ if (imageLink) { }}
|
||||
|
@ -47,7 +47,7 @@ body {
|
|||
text-rendering: optimizeSpeed;
|
||||
line-height: 1.5;
|
||||
font-size: 16px;
|
||||
padding: 1rem 0.5rem;
|
||||
padding: 1rem 0.5rem 3rem;
|
||||
font-family: sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--color);
|
||||
|
@ -117,14 +117,7 @@ input[type=submit] {
|
|||
background: var(--button-bg);
|
||||
color: var(--button-fg);
|
||||
padding: 0.25rem 1rem;
|
||||
margin: 1rem 0 2rem;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: var(--bg-content-alt);
|
||||
padding: 0.5rem;
|
||||
white-space: break-spaces;
|
||||
word-break: break-all;
|
||||
margin: 0rem 0 2rem;
|
||||
}
|
||||
|
||||
.row {
|
||||
|
@ -174,6 +167,10 @@ h1 {
|
|||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.warning {
|
||||
font-size: 0.9rem;
|
||||
color: #ffe69c;
|
||||
|
@ -182,8 +179,8 @@ h1 {
|
|||
|
||||
.alert {
|
||||
font-size: 0.9rem;
|
||||
color: #f1aeb5;
|
||||
margin-bottom: 0.5rem;
|
||||
margin: 0.5rem 0 1rem;
|
||||
color: hsl(353.7, 70.5%, 87%);
|
||||
}
|
||||
|
||||
.info {
|
||||
|
@ -196,18 +193,36 @@ h1 {
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.row.optional {
|
||||
background: #333;
|
||||
margin: 1rem -0.5rem 0;
|
||||
.optional {
|
||||
margin: 0 -0.5rem 0.5rem;
|
||||
padding: 0rem 0.5rem 1rem;
|
||||
background: var(--bg-content-alt);
|
||||
}
|
||||
|
||||
#testing {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 0.5rem !important;
|
||||
white-space: break-spaces;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
#mainplayer {
|
||||
max-width: calc(100vw - 1rem);
|
||||
max-height: 95vh;
|
||||
}
|
||||
|
||||
#previewVideo {
|
||||
border: 1px solid white;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -217,48 +232,58 @@ h1 {
|
|||
<p>Your link is:</p>
|
||||
<pre>{{=siteUrl}}</pre>
|
||||
<div class="center">
|
||||
<video controls poster="{{=imageLink}}">
|
||||
<video id=mainplayer" crossorigin controls poster="{{=imageLink}}">
|
||||
<source src="{{=videoLink}}">
|
||||
</video>
|
||||
</div>
|
||||
{{ } }}
|
||||
<form action="/" method="post" enctype="multipart/form-data" class="inside">
|
||||
<h1>Create/generate embed url</h1>
|
||||
<p class="warning">Please don't try to use <b>youtube.com</b> links or file sharing sites that don't allow direct link to the video. They will show a download page and won't embed properly (for example <b>mediafire.com</b>, <b>megaupload</b>, <b>drive.google.com</b>, etc.)<br><b>Those will all not work</b>. (You can test if embedding works by clicking play below in the video player after filling out the video link.)</p>
|
||||
<p class="info">Video upload sites that work are sites like <a href="https://catbox.moe/" target="_blank">catbox.moe</a>.</p>
|
||||
<p class="alert">Video files of .mkv <b>will not work</b>! Only files like *.mov, *.mp4 and *.webm will work.</p>
|
||||
<h1>Embed {{ if (videoLink && imageLink) { }} another {{ } }} video for discord:</h1>
|
||||
<p>Use this tool to generate links that forces discord to try and play a video or movie directly inside discord. By default, discord will not show video playback for video links that are too large or not in proper format. This tool forces discord to at least try.</p>
|
||||
|
||||
<p class="warning">Only sites and filesharing sites that have direct link to the video will work and play in discord. Youtube has it's own player and will not work. Many video sites will have their own player and will not work.</p>
|
||||
|
||||
<p class="info">Video upload sites that work are sites like <a href="https://catbox.moe/" target="_blank">catbox.moe</a> and other sites that allow <b>direct link to video file</b> will work.</p>
|
||||
|
||||
{{ if (error) { }}<p class="error">{{=error}}</p>{{ } }}
|
||||
<label>Video link*</label>
|
||||
<p> </p>
|
||||
<label>Link to video file you want to play in discord*</label>
|
||||
<input id="inputVideo" type="text" name="video" value="{{=inputVideo}}">
|
||||
<p class="alert">
|
||||
<b>.mkv</b> video files will <b>not work</b>! <b>HEVC/h.265</b> will also <b>not work</b>! File hosting sites like <b>mediafire/google drive/dropbox</b> will also probably <b>not work</b>!<br>
|
||||
</p>
|
||||
<div class="row optional">
|
||||
<div class="row-item">
|
||||
<label>Optional: Image link (recommended, defaults to black cover)</label>
|
||||
<label>Optional: Link to image to show before discord user presses play</label>
|
||||
<input id="inputImage" type="text" name="image" value="{{=inputImage}}">
|
||||
</div>
|
||||
<span class="row-inbetween">or</span>
|
||||
<div class="row-item">
|
||||
<label>Optional: Upload image file (max 8MiB, recommended)</label>
|
||||
<input type="file" name="media">
|
||||
<label>Optional: Upload image to show before discord user presses play</label>
|
||||
<input type="file" name="media" type="image">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="submit" value="Generate short url">
|
||||
|
||||
<div id="testing" class="hidden">
|
||||
<p>Test area (does the video load and play? If not, it might not work on discord)</p>
|
||||
|
||||
<div class="center">
|
||||
<video class="hidden" id="previewVideo" crossorigin controls poster="" preload="none">
|
||||
<source src="">
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<pre class="optional" id="generateurl">{{=siteUrlBase}}?v=<video link></pre>
|
||||
|
||||
<input type="submit" value="Generate shorter url">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p>
|
||||
Preview:
|
||||
</p>
|
||||
<pre id="generateurl">
|
||||
{{=siteUrlBase}}?v=<video link>
|
||||
</pre>
|
||||
|
||||
<video class="hidden" id="previewVideo" controls poster="" preload="none">
|
||||
<source src="">
|
||||
</video>
|
||||
|
||||
<script type="text/javascript" nonce="{{=nonce}}">
|
||||
var defaultPoster = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQAQAAAADnBuD7AAAAh0lEQVR42u3BMQEAAADCIPuntsROYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkDsTfAAFMFnd/AAAAAElFTkSuQmCC';
|
||||
var baseSite = '{{=siteUrlBase}}';
|
||||
var existingVideoUrl = '{{=inputVideo}}';
|
||||
var testing = document.getElementById('testing');
|
||||
var generateurl = document.getElementById('generateurl');
|
||||
var inputVideo = document.getElementById('inputVideo');
|
||||
var inputImage = document.getElementById('inputImage');
|
||||
|
@ -278,9 +303,20 @@ h1 {
|
|||
previewVideo.classList.add('hidden')
|
||||
} else {
|
||||
generateurl.innerText = baseSite + '?v=' + encodeURIComponent(inputVideo.value) + (inputImage.value ? '&i=' + encodeURIComponent(inputImage.value) : '');
|
||||
previewVideo.poster = inputImage.value || defaultPoster;
|
||||
previewVideo.children[0].src = inputVideo.value;
|
||||
previewVideo.classList.remove('hidden')
|
||||
|
||||
previewVideo.pause();
|
||||
if (inputVideo.value !== existingVideoUrl) {
|
||||
previewVideo.setAttribute('poster', inputImage.value || defaultPoster);
|
||||
previewVideo.children[0].setAttribute('src', inputVideo.value);
|
||||
previewVideo.classList.remove('hidden');
|
||||
testing.classList.remove('hidden');
|
||||
} else {
|
||||
previewVideo.removeAttribute('poster');
|
||||
previewVideo.children[0].removeAttribute('src');
|
||||
previewVideo.classList.add('hidden')
|
||||
testing.classList.add('hidden');
|
||||
}
|
||||
previewVideo.load();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
package-lock=false
|
|
@ -0,0 +1,52 @@
|
|||
import config from '../../base/config.mjs'
|
||||
import Client from '../../base/media/client.mjs'
|
||||
import OriginalArticleRoutes from '../../base/article/routes.mjs'
|
||||
import { deleteFile, uploadFile } from '../../base/media/upload.mjs'
|
||||
import { parseVideos, parseVideo } from './util.mjs'
|
||||
import { RankLevels } from '../../base/authentication/security.mjs'
|
||||
|
||||
export default class ArticleRoutes extends OriginalArticleRoutes {
|
||||
constructor(opts = {}) {
|
||||
opts.requireAuth = true
|
||||
super(opts)
|
||||
|
||||
Object.assign(this, {
|
||||
uploadFile: uploadFile,
|
||||
deleteFile: deleteFile,
|
||||
})
|
||||
}
|
||||
|
||||
register(server) {
|
||||
server.flaska.get('/api/articles', this.getVideos.bind(this))
|
||||
server.flaska.get('/api/articles/:path', this.getArticle.bind(this))
|
||||
server.flaska.put('/api/auth/articles/:id', [server.authenticate(), server.jsonHandler()], this.updateCreateArticle.bind(this))
|
||||
server.flaska.get('/api/auth/uploadToken', server.authenticate(RankLevels.Admin), this.getUploadToken.bind(this))
|
||||
server.flaska.delete('/api/auth/articles/:id', server.authenticate(RankLevels.Admin), this.auth_removeSingleArticle.bind(this))
|
||||
}
|
||||
|
||||
/** GET: /api/auth/articles */
|
||||
async getVideos(ctx) {
|
||||
let res = await ctx.db.safeCallProc('filadelfia_archive.article_get_all', [])
|
||||
|
||||
ctx.body = {
|
||||
videos: parseVideos(res.results[0]),
|
||||
}
|
||||
}
|
||||
|
||||
/** GET: /api/auth/uploadToken */
|
||||
async getUploadToken(ctx) {
|
||||
const media = config.get('media')
|
||||
|
||||
ctx.body = {
|
||||
token: Client.createJwt({ iss: media.iss }, media.secret),
|
||||
path: config.get('media:directFilePath'),
|
||||
resize: config.get('media:path'),
|
||||
delete: config.get('media:removePath'),
|
||||
}
|
||||
}
|
||||
|
||||
/** PUT: /api/auth/articles/:id */
|
||||
async updateCreateArticle(ctx) {
|
||||
return this.private_getUpdateArticle(ctx, ctx.req.body, ctx.req.body.banner, ctx.req.body.media)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { parseMediaAndBanner } from "../../base/util.mjs"
|
||||
|
||||
export function parseVideos(videos) {
|
||||
for (let i = 0; i < videos.length; i++) {
|
||||
parseVideo(videos[i])
|
||||
}
|
||||
return videos
|
||||
}
|
||||
|
||||
export function parseVideo(video) {
|
||||
if (!video) {
|
||||
return null
|
||||
}
|
||||
parseMediaAndBanner(video)
|
||||
video.metadata = JSON.parse(video.metadata || '{}')
|
||||
delete video.banner_alt_prefix
|
||||
delete video.media_alt_prefix
|
||||
return video
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import fs from 'fs/promises'
|
||||
import dot from 'dot'
|
||||
import Parent from '../base/serve.mjs'
|
||||
import path from 'path'
|
||||
|
||||
export default class ServeHandler extends Parent {
|
||||
loadTemplate(indexFile) {
|
||||
this.template = dot.template(indexFile.toString(), { argName: [
|
||||
'siteUrl',
|
||||
] })
|
||||
}
|
||||
|
||||
async serveIndex(ctx) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
let indexFile = await fs.readFile(path.join(this.root, 'index.html'))
|
||||
this.loadTemplate(indexFile)
|
||||
}
|
||||
|
||||
ctx.body = this.template({
|
||||
siteUrl: this.frontend + ctx.url,
|
||||
})
|
||||
ctx.type = 'text/html; charset=utf-8'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import config from '../base/config.mjs'
|
||||
import Parent from '../base/server.mjs'
|
||||
import StaticRoutes from '../base/static_routes.mjs'
|
||||
import ServeHandler from './serve.mjs'
|
||||
import AuthenticationRoutes from '../base/authentication/routes.mjs'
|
||||
import ArticleRoutes from './article/routes.mjs'
|
||||
|
||||
export default class Server extends Parent {
|
||||
init() {
|
||||
super.init()
|
||||
let localUtil = new this.core.sc.Util(import.meta.url)
|
||||
|
||||
this.flaskaOptions.appendHeaders['Content-Security-Policy'] = `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'; connect-src 'self' https://media.nfp.is/; media-src 'self' https://cdn.nfp.is/`,
|
||||
this.flaskaOptions.nonce = []
|
||||
this.routes = {
|
||||
static: new StaticRoutes(),
|
||||
auth: new AuthenticationRoutes(),
|
||||
article: new ArticleRoutes(),
|
||||
}
|
||||
this.routes.serve = new ServeHandler({
|
||||
root: localUtil.getPathFromRoot('../public'),
|
||||
version: this.core.app.running,
|
||||
frontend: config.get('frontend:url'),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
const Authentication = require('./authentication')
|
||||
const lang = require('./lang')
|
||||
|
||||
function safeParseReponse(str, status, url) {
|
||||
if (status === 0) {
|
||||
return new Error(lang.api_down)
|
||||
}
|
||||
if (str.slice(0, 9) === '<!doctype') {
|
||||
if (status === 500) {
|
||||
return new Error('Server is temporarily down, try again later.')
|
||||
}
|
||||
return new Error('Expected JSON but got HTML (' + status + ': ' + url.split('?')[0] + ')')
|
||||
}
|
||||
if (!str) {
|
||||
return {}
|
||||
}
|
||||
try {
|
||||
return JSON.parse(str)
|
||||
} catch (err) {
|
||||
return new Error('Unexpected non-JSON response: ' + err.message)
|
||||
}
|
||||
}
|
||||
|
||||
let requests = new Set()
|
||||
let requestIndex = 0
|
||||
|
||||
function clearLoading(request) {
|
||||
requests.delete(request)
|
||||
if (!requests.size) {
|
||||
api.loading = false
|
||||
window.requestAnimationFrame(m.redraw.bind(m))
|
||||
}
|
||||
}
|
||||
|
||||
const api = {
|
||||
loading: false,
|
||||
sendRequest: function(options, isPagination) {
|
||||
let request = requestIndex++
|
||||
requests.add(request)
|
||||
api.loading = true
|
||||
let token = Authentication.getToken()
|
||||
let pagination = isPagination
|
||||
|
||||
if (token) {
|
||||
options.headers = options.headers || {}
|
||||
options.headers['Authorization'] = 'Bearer ' + token
|
||||
}
|
||||
|
||||
options.extract = function(xhr) {
|
||||
let out = safeParseReponse(xhr.responseText, xhr.status, this.url)
|
||||
|
||||
if (out instanceof Error) {
|
||||
throw out
|
||||
}
|
||||
if (xhr.status >= 300) {
|
||||
if (out.message) {
|
||||
throw out
|
||||
}
|
||||
console.error('Got error ' + xhr.status + ' but no error message:', out)
|
||||
throw new Error('Unknown or empty response from server.')
|
||||
}
|
||||
if (pagination) {
|
||||
let headers = {}
|
||||
|
||||
xhr.getAllResponseHeaders().split('\r\n').forEach(function(item) {
|
||||
let splitted = item.split(': ')
|
||||
headers[splitted[0]] = splitted[1]
|
||||
})
|
||||
|
||||
out = {
|
||||
headers: headers || {},
|
||||
data: out,
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
return m.request(options)
|
||||
.then(function(res) {
|
||||
clearLoading(request)
|
||||
return res
|
||||
})
|
||||
.catch(function (error) {
|
||||
clearLoading(request)
|
||||
window.requestAnimationFrame(m.redraw.bind(m))
|
||||
if (error.status === 403) {
|
||||
Authentication.clearToken()
|
||||
m.route.set('/', { redirect: m.route.get() })
|
||||
}
|
||||
if (error.response && error.response.status) {
|
||||
return Promise.reject(error.response)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
})
|
||||
},
|
||||
|
||||
uploadBanner: function(bannerFile, token, reporter) {
|
||||
let request = requestIndex++
|
||||
requests.add(request)
|
||||
api.loading = true
|
||||
|
||||
var report = reporter || function() {}
|
||||
var data = new FormData()
|
||||
data.append('file', bannerFile)
|
||||
/*data.append('preview', JSON.stringify({
|
||||
"out": "base64",
|
||||
"format": "avif",
|
||||
"resize": {
|
||||
"width": 360,
|
||||
"height": 203,
|
||||
"fit": "cover",
|
||||
"withoutEnlargement": true,
|
||||
"kernel": "mitchell"
|
||||
},
|
||||
"avif": {
|
||||
"quality": 40,
|
||||
"effort": 9
|
||||
}
|
||||
}))*/
|
||||
data.append('medium', JSON.stringify({
|
||||
"format": "avif",
|
||||
"resize": {
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"fit": "cover",
|
||||
"withoutEnlargement": true,
|
||||
"kernel": "mitchell"
|
||||
},
|
||||
"avif": {
|
||||
"quality": 75,
|
||||
"effort": 3
|
||||
}
|
||||
}))
|
||||
|
||||
report(lang.api_banner_upload)
|
||||
|
||||
return api.sendRequest({
|
||||
method: 'POST',
|
||||
url: token.resize + '?token=' + token.token,
|
||||
body: data,
|
||||
})
|
||||
.then(async (banner) => {
|
||||
let preview = null
|
||||
let quality = 60
|
||||
while (!preview && quality > 10) {
|
||||
report(lang.format(lang.api_banner_generate, quality))
|
||||
let check = await api.sendRequest({
|
||||
method: 'POST',
|
||||
url: token.resize + '/' + banner.filename + '?token=' + token.token,
|
||||
body: {
|
||||
"preview": {
|
||||
"out": "base64",
|
||||
"format": "avif",
|
||||
"resize": {
|
||||
"width": 360,
|
||||
"height": 203,
|
||||
"fit": "cover",
|
||||
"withoutEnlargement": true,
|
||||
"kernel": "mitchell"
|
||||
},
|
||||
"avif": {
|
||||
"quality": quality,
|
||||
"effort": 9
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (check.preview.base64.length < 8000) {
|
||||
preview = check.preview.base64
|
||||
} else {
|
||||
quality -= 5
|
||||
}
|
||||
}
|
||||
report(null)
|
||||
|
||||
api.sendRequest({
|
||||
method: 'DELETE',
|
||||
url: token.delete + banner.filename + '?token=' + token.token,
|
||||
}).catch(err => console.error(err))
|
||||
|
||||
return {
|
||||
file: bannerFile,
|
||||
size: bannerFile.size,
|
||||
medium: {
|
||||
filename: banner.medium.filename,
|
||||
path: banner.medium.path,
|
||||
},
|
||||
preview: {
|
||||
base64: preview,
|
||||
},
|
||||
}
|
||||
})
|
||||
.then(
|
||||
function(res) {
|
||||
clearLoading(request)
|
||||
return res
|
||||
},
|
||||
function(err) {
|
||||
clearLoading(request)
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
prettyFormatBytes: function(bytes) {
|
||||
if (bytes < 1024) {
|
||||
return `${bytes} B`
|
||||
}
|
||||
if (bytes < 1024 * 1024) {
|
||||
return `${(bytes / 1024).toFixed(1)} KB`
|
||||
}
|
||||
if (bytes < 1024 * 1024 * 1024) {
|
||||
return `${(bytes / 1024 / 1024).toFixed(1)} MB`
|
||||
}
|
||||
},
|
||||
|
||||
uploadFileProgress: function(options, file, reporter) {
|
||||
let request = requestIndex++
|
||||
requests.add(request)
|
||||
api.loading = true
|
||||
|
||||
return new Promise(function(res, rej) {
|
||||
let report = reporter || function() {}
|
||||
let formdata = new FormData()
|
||||
|
||||
formdata.append('file', file)
|
||||
|
||||
let request = new XMLHttpRequest()
|
||||
|
||||
let finished = false
|
||||
let lastMarker = new Date()
|
||||
let lastMarkerLoaded = 0
|
||||
let lastMarkerSpeed = '...'
|
||||
|
||||
request.abortRequest = function() {
|
||||
finished = true
|
||||
request.abort()
|
||||
}
|
||||
|
||||
request.upload.addEventListener('progress', function (e) {
|
||||
let check = new Date()
|
||||
if (check - lastMarker >= 1000) {
|
||||
let loaded = e.loaded - lastMarkerLoaded
|
||||
lastMarkerSpeed = api.prettyFormatBytes(loaded / ((check - lastMarker) / 1000))
|
||||
lastMarker = check
|
||||
lastMarkerLoaded = e.loaded
|
||||
}
|
||||
report(request, Math.min(e.loaded / file.size * 100, 100), lastMarkerSpeed)
|
||||
})
|
||||
request.addEventListener('abort', function(e) {
|
||||
finished = true
|
||||
window.requestAnimationFrame(m.redraw.bind(m))
|
||||
rej()
|
||||
})
|
||||
|
||||
request.addEventListener('readystatechange', function(e) {
|
||||
if (finished) return
|
||||
if (request.readyState !== 4) return
|
||||
|
||||
finished = true
|
||||
let out = safeParseReponse(request.responseText, request.status, options.url)
|
||||
if (out instanceof Error || request.status >= 300) {
|
||||
return rej(out)
|
||||
}
|
||||
return res(out)
|
||||
})
|
||||
|
||||
request.open(options.method || 'POST', options.url)
|
||||
request.send(formdata)
|
||||
|
||||
report(request, 0)
|
||||
})
|
||||
.then(function(res) {
|
||||
clearLoading(request)
|
||||
return res
|
||||
}, function(err) {
|
||||
clearLoading(request)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = api
|
|
@ -0,0 +1,64 @@
|
|||
const m = require('mithril')
|
||||
const storageName = 'nfp_sites_filadelfia_web_logintoken'
|
||||
|
||||
const Authentication = {
|
||||
currentUser: null,
|
||||
isAdmin: false,
|
||||
loadingListeners: [],
|
||||
authListeners: [],
|
||||
|
||||
updateToken: function(token) {
|
||||
if (!token) return Authentication.clearToken()
|
||||
localStorage.setItem(storageName, token)
|
||||
Authentication.currentUser = JSON.parse(atob(token.split('.')[1]))
|
||||
|
||||
if (Authentication.authListeners.length) {
|
||||
Authentication.authListeners.forEach(function(x) { x(Authentication.currentUser) })
|
||||
}
|
||||
},
|
||||
|
||||
clearToken: function() {
|
||||
Authentication.currentUser = null
|
||||
localStorage.removeItem(storageName)
|
||||
Authentication.isAdmin = false
|
||||
},
|
||||
|
||||
addEvent: function(event) {
|
||||
Authentication.authListeners.push(event)
|
||||
},
|
||||
|
||||
setAdmin: function(item) {
|
||||
Authentication.isAdmin = item
|
||||
},
|
||||
|
||||
getToken: function() {
|
||||
return localStorage.getItem(storageName)
|
||||
},
|
||||
|
||||
getTokenDecoded: function() {
|
||||
let token = Authentication.getToken()
|
||||
var base64Url = token.split('.')[1];
|
||||
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
||||
var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) {
|
||||
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||
}).join(''));
|
||||
|
||||
return JSON.parse(jsonPayload);
|
||||
},
|
||||
|
||||
requiresLogin: function() {
|
||||
if (Authentication.currentUser) return
|
||||
m.route.set('/login')
|
||||
},
|
||||
|
||||
requiresNotLogin: function() {
|
||||
if (!Authentication.currentUser) return
|
||||
m.route.set('/')
|
||||
},
|
||||
}
|
||||
|
||||
Authentication.updateToken(localStorage.getItem(storageName))
|
||||
|
||||
window.Authentication = Authentication
|
||||
|
||||
module.exports = Authentication
|
|
@ -0,0 +1,72 @@
|
|||
const m = require('mithril')
|
||||
const videos = require('./videos')
|
||||
const Authentication = require('./authentication')
|
||||
const lang = require('./lang')
|
||||
|
||||
const Menu = {
|
||||
oninit: function(vnode) {
|
||||
this.currentActive = 'home'
|
||||
this.loading = false
|
||||
if (!videos.Tree.length) {
|
||||
videos.refreshTree()
|
||||
}
|
||||
this.onbeforeupdate()
|
||||
},
|
||||
|
||||
onbeforeupdate: function() {
|
||||
videos.calculateActiveBranches()
|
||||
let currentPath = m.route.get()
|
||||
},
|
||||
|
||||
logOut: function() {
|
||||
Authentication.clearToken()
|
||||
m.route.set('/')
|
||||
},
|
||||
|
||||
view: function() {
|
||||
let tree = videos.Tree
|
||||
let last = videos.Tree[videos.Tree.length - 1]
|
||||
let hasId = m.route.param('id')
|
||||
|
||||
return [
|
||||
m('nav', [
|
||||
m('h4', m(m.route.Link, { href: '/' }, lang.header_title /* Filadelfia archival center */)),
|
||||
m('a.link.changelang', { onclick: lang.langtoggle }, lang.lang_current),
|
||||
Authentication.currentUser?.rank >= 100
|
||||
? m(m.route.Link, { class: 'upload', href: '/upload' }, lang.upload_goto) // Upload
|
||||
: null,
|
||||
Authentication.currentUser
|
||||
? m(m.route.Link, { class: 'link', href: '/logout' }, lang.logout)
|
||||
: m(m.route.Link, { class: 'upload', href: '/login' }, lang.login_submit) // Upload,
|
||||
// m('button.logout', { onclick: this.logOut }, lang.header_logout), // Log out
|
||||
]),
|
||||
videos.error
|
||||
? m('div.error', { onclick: videos.refreshTree }, [
|
||||
videos.error, m('br'), 'Click here to try again'
|
||||
])
|
||||
: null,
|
||||
!videos.error
|
||||
? [
|
||||
m('.nav', m('.inner', tree.map(year => {
|
||||
return m(m.route.Link, {
|
||||
class: [videos.year === year ? 'active' : '',
|
||||
!year.videos.length ? 'empty' : ''].join(' '),
|
||||
href: ['', (videos.year !== year && year !== last) || hasId ? year.title : '' ].filter(x => x).join('/') || '/',
|
||||
}, year.title)
|
||||
}))),
|
||||
videos.year
|
||||
? m('.nav', m('.inner', videos.year.branches.map(month => {
|
||||
return m(m.route.Link, {
|
||||
class: [videos.month === month ? 'active' : '',
|
||||
!month.videos.length ? 'empty' : ''].join(' '),
|
||||
href: ['', videos.year.title, videos.month !== month || hasId ? month.title : null ].filter(x => x).join('/'),
|
||||
}, lang.months[month.title])
|
||||
})))
|
||||
: null,
|
||||
]
|
||||
: null,
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = Menu
|
|
@ -0,0 +1,68 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const HoldButton = {
|
||||
oninit: function(vnode) {
|
||||
this.timer = null
|
||||
this.holding = false
|
||||
this.windowBlur = () => {
|
||||
this.timerStop()
|
||||
m.redraw()
|
||||
}
|
||||
window.addEventListener('blur', this.windowBlur)
|
||||
},
|
||||
|
||||
onremove: function(vnode) {
|
||||
this.timerStop()
|
||||
window.removeEventListener('blur', this.windowBlur)
|
||||
},
|
||||
|
||||
keydown(vnode, e) {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
this.timerStart(vnode)
|
||||
m.redraw()
|
||||
}
|
||||
},
|
||||
|
||||
keyup(e) {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
this.timerStop()
|
||||
m.redraw()
|
||||
}
|
||||
},
|
||||
|
||||
timerStart(vnode) {
|
||||
if (this.timer) return
|
||||
|
||||
this.timer = setTimeout(this.timerConfirmed.bind(this), 2000, vnode)
|
||||
this.holding = true
|
||||
},
|
||||
|
||||
timerStop() {
|
||||
clearTimeout(this.timer)
|
||||
this.timer = null
|
||||
this.holding = false
|
||||
},
|
||||
|
||||
timerConfirmed(vnode) {
|
||||
this.timerStop()
|
||||
vnode.attrs.onclick()
|
||||
m.redraw()
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return m('button.holdbutton', {
|
||||
style: '--hold-bg: var(--error-bg); --hold-color: var(--error); --hold-fill: var(--error);',
|
||||
hidden: vnode.attrs.hidden,
|
||||
class: (vnode.attrs.class || '')
|
||||
+ (this.holding ? ' holdbutton-active' : ''),
|
||||
onpointerdown: this.timerStart.bind(this, vnode),
|
||||
onpointerup: this.timerStop.bind(this),
|
||||
onpointerleave: this.timerStop.bind(this),
|
||||
onkeydown: this.keydown.bind(this, vnode),
|
||||
onkeyup: this.keyup.bind(this),
|
||||
onclick: e => { return false },
|
||||
}, m('div.inner', vnode.attrs.text))
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = HoldButton
|
|
@ -0,0 +1,62 @@
|
|||
const m = require('mithril')
|
||||
const Authentication = require('./authentication')
|
||||
const Header = require('./header')
|
||||
const Login = require('./page_login')
|
||||
const Logout = require('./page_logout')
|
||||
const Browse = require('./page_browse')
|
||||
const Upload = require('./page_upload')
|
||||
const Article = require('./page_article')
|
||||
window.m = m
|
||||
|
||||
let css = [
|
||||
'/assets/app.css?v=2',
|
||||
'/assets/tempus-dominus.css',
|
||||
'/assets/fontawesome.css',
|
||||
]
|
||||
|
||||
for (let item of css) {
|
||||
var fileref = document.createElement("link");
|
||||
fileref.setAttribute("rel", "stylesheet");
|
||||
fileref.setAttribute("type", "text/css");
|
||||
fileref.setAttribute("href", item);
|
||||
document.head.appendChild(fileref)
|
||||
}
|
||||
|
||||
m.route.setOrig = m.route.set
|
||||
m.route.set = function(path, data, options){
|
||||
m.route.setOrig(path, data, options)
|
||||
window.scrollTo(0, 0)
|
||||
}
|
||||
|
||||
m.route.linkOrig = m.route.link
|
||||
m.route.link = function(vnode){
|
||||
m.route.linkOrig(vnode)
|
||||
window.scrollTo(0, 0)
|
||||
}
|
||||
|
||||
m.route.prefix = ''
|
||||
|
||||
const allRoutes = {
|
||||
'/': Browse,
|
||||
'/login': Login,
|
||||
'/logout': Logout,
|
||||
'/upload': Upload,
|
||||
'/:year': Browse,
|
||||
'/:year/:month': Browse,
|
||||
'/:year/:month/:path': Article,
|
||||
}
|
||||
|
||||
// Wait until we finish checking avif support, some views render immediately and will ask for this immediately before the callback gets called.
|
||||
|
||||
/*
|
||||
* imgsupport.js from leechy/imgsupport
|
||||
*/
|
||||
const AVIF = new Image();
|
||||
AVIF.onload = AVIF.onerror = function () {
|
||||
window.supportsavif = (AVIF.height === 2)
|
||||
document.body.className = document.body.className + ' ' + (window.supportsavif ? 'avifsupport' : 'jpegonly')
|
||||
|
||||
m.mount(document.getElementById('header'), Header)
|
||||
m.route(document.getElementById('main'), '/', allRoutes)
|
||||
}
|
||||
AVIF.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABcAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAABAA0ABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgAPkgIaDQgMgkf8AAAQAAAr3A=';
|
|
@ -0,0 +1,143 @@
|
|||
const m = require('mithril')
|
||||
const api = require('./api')
|
||||
const tempus = require('@eonasdan/tempus-dominus')
|
||||
|
||||
const tempusLocalization = {
|
||||
locale: 'is',
|
||||
startOfTheWeek: 0,
|
||||
hourCycle: 'h23',
|
||||
dateFormats: {
|
||||
LTS: 'H:mm:ss',
|
||||
LT: 'H:mm',
|
||||
L: 'dd.MM.yyyy',
|
||||
LL: 'd [de] MMMM [de] yyyy',
|
||||
LLL: 'd [de] MMMM [de] yyyy H:mm',
|
||||
LLLL: 'dddd, d [de] MMMM [de] yyyy H:mm',
|
||||
},
|
||||
}
|
||||
|
||||
const Input = {
|
||||
oninit: function(vnode) {
|
||||
this.tempus = null
|
||||
this.subscription = null
|
||||
this.input = null
|
||||
this.preview = null
|
||||
},
|
||||
|
||||
onremove: function(vnode) {
|
||||
if (this.subscription) this.subscription.unsubscribe()
|
||||
if (this.tempus) {
|
||||
this.tempus.dispose()
|
||||
this.tempus = null
|
||||
}
|
||||
if (this.preview) {
|
||||
this.preview.clear()
|
||||
}
|
||||
},
|
||||
|
||||
onupdate: function(vnode) {
|
||||
if (this.tempus && vnode.attrs.form[vnode.attrs.formKey]) {
|
||||
if (vnode.attrs.form[vnode.attrs.formKey].getTime() !== this.tempus.viewDate?.getTime()) {
|
||||
this.tempus.dates.setValue(new tempus.DateTime(vnode.attrs.form[vnode.attrs.formKey]))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
imageChanged: function(vnode, e) {
|
||||
let file = e.currentTarget.files?.[0] || null
|
||||
this.updateValue(vnode, file)
|
||||
if (this.preview) {
|
||||
this.preview.clear()
|
||||
this.preview = null
|
||||
}
|
||||
if (!file) return
|
||||
|
||||
if (file.type.startsWith('image')) {
|
||||
this.preview = {
|
||||
file: file,
|
||||
preview: URL.createObjectURL(file),
|
||||
clear: function() {
|
||||
URL.revokeObjectURL(this.preview)
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updateValue: function(vnode, value) {
|
||||
vnode.attrs.form[vnode.attrs.formKey] = value
|
||||
if (typeof(vnode.attrs.oninput) === 'function') {
|
||||
vnode.attrs.oninput(vnode.attrs.form[vnode.attrs.formKey])
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
getInput: function(vnode) {
|
||||
switch (vnode.attrs.utility) {
|
||||
case 'file':
|
||||
return m('div.form-row', [
|
||||
m('input', {
|
||||
type: 'text',
|
||||
disabled: api.loading,
|
||||
value: vnode.attrs.form[vnode.attrs.formKey]?.name || '',
|
||||
}),
|
||||
m('button.fal', { class: vnode.attrs.button || 'file' }),
|
||||
m('input.cover', {
|
||||
type: 'file',
|
||||
accept: vnode.attrs.accept,
|
||||
disabled: api.loading,
|
||||
oninput: (e) => this.updateValue(vnode, e.currentTarget.files?.[0] || null),
|
||||
}),
|
||||
])
|
||||
case 'datetime':
|
||||
return m('div.form-row', [
|
||||
m('input', {
|
||||
type: 'text',
|
||||
disabled: api.loading,
|
||||
oncreate: (e) => {
|
||||
this.tempus = new tempus.TempusDominus(e.dom, {
|
||||
localization: tempusLocalization,
|
||||
})
|
||||
this.tempus.dates.setValue(new tempus.DateTime(vnode.attrs.form[vnode.attrs.formKey]))
|
||||
this.subscription = this.tempus.subscribe(tempus.Namespace.events.change, (e) => {
|
||||
this.updateValue(vnode, e.date)
|
||||
});
|
||||
},
|
||||
}),
|
||||
m('button.fal.fa-calendar', {
|
||||
onclick: () => { this.tempus.toggle(); return false },
|
||||
})
|
||||
])
|
||||
case 'image':
|
||||
let imageLink = this.preview && this.preview.preview || vnode.attrs.form[vnode.attrs.formKey]
|
||||
|
||||
return m('div.form-row.image-banner', {
|
||||
style: {
|
||||
'background-image': typeof imageLink === 'string' ? 'url("' + (imageLink) + '")' : null,
|
||||
},
|
||||
}, [
|
||||
m('input.cover', {
|
||||
type: 'file',
|
||||
accept: vnode.attrs.accept,
|
||||
disabled: api.loading,
|
||||
onchange: this.imageChanged.bind(this, vnode),
|
||||
}),
|
||||
])
|
||||
default:
|
||||
return m('input', {
|
||||
disabled: api.loading,
|
||||
type: vnode.attrs.type || 'text',
|
||||
value: vnode.attrs.form[vnode.attrs.formKey],
|
||||
oninput: (e) => this.updateValue(vnode, e.currentTarget.value),
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
vnode.attrs.label ? m('label', vnode.attrs.label) : null,
|
||||
this.getInput(vnode),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = Input
|
|
@ -0,0 +1,156 @@
|
|||
const out = {
|
||||
currentlang: 'en',
|
||||
}
|
||||
|
||||
const i18n = {
|
||||
lang_change_long: ['Skipta yfir á íslensku',
|
||||
'Change to english'],
|
||||
lang_current: ['en',
|
||||
'is'],
|
||||
header_title: ['Fíladelfia archival center',
|
||||
'Fíladelfia myndhvelfing'],
|
||||
header_logout: ['Log out',
|
||||
'Skrá út'],
|
||||
title: ['Title',
|
||||
'Titill'],
|
||||
date: ['Date',
|
||||
'Dagsetning'],
|
||||
language: ['EN',
|
||||
'IS'],
|
||||
upload_goto: ['Upload',
|
||||
'Upphlaða'],
|
||||
login_error: ['Error while logging in: {0}',
|
||||
'Villa við innskráningu: {0}'],
|
||||
login_error_auth: ['Unknown error from server. Try again later.',
|
||||
'Óþekkt villa frá vefþjóni. Reyndu aftur seinna.'],
|
||||
login_missing_email: ['Email is missing',
|
||||
'Email eða nafn vantar'],
|
||||
login_missing_password:['Password is missing',
|
||||
'Lykilorð vantar'],
|
||||
login_email: ['Email or name',
|
||||
'Email eða nafn'],
|
||||
login_password: ['Password',
|
||||
'Lykilorð'],
|
||||
login_submit: ['Log in',
|
||||
'Skrá inn'],
|
||||
logout: ['Log out',
|
||||
'Skrá út'],
|
||||
upload_missing_title: ['Title is missing',
|
||||
'Titill vantar'],
|
||||
upload_missing_date: ['Date is missing',
|
||||
'Dagsetning vantar'],
|
||||
upload_missing_file: ['Video file missing',
|
||||
'Myndaskrá vantar'],
|
||||
upload_missing_banner: ['Poster image missing',
|
||||
'Mynd vantar'],
|
||||
upload_error: ['Error while uploading: {0}',
|
||||
'Villa við að hlaða upp myndefni: {0}'],
|
||||
unsplash: ['Photo by {0} on {1}',
|
||||
'Mynd eftir {0} frá {1}'],
|
||||
api_down: ['No internet or browser blocked the request.',
|
||||
'Ekkert net eða vafri blockaði fyrirspurn.'],
|
||||
edit: ['Edit',
|
||||
'Breyta'],
|
||||
delete: ['Delete',
|
||||
'Eyða'],
|
||||
article_speaker: ['Speaker',
|
||||
'Ræðumaður'],
|
||||
delete_error: ['Error while deleting: {0}',
|
||||
'Villa við að eyða efni: {0}'],
|
||||
article_error: ['Error while saving: {0}',
|
||||
'Villa við að vista: {0}'],
|
||||
api_banner_upload: ['Uploading banner image',
|
||||
'Er að senda mynd'],
|
||||
api_banner_generate:['Generating preview, testing quality {0}%',
|
||||
'Bý til forsíðumynd, prufa {0}% gæði'],
|
||||
months: {
|
||||
'1': ['January',
|
||||
'Janúar'],
|
||||
'2': ['February',
|
||||
'Febrúar'],
|
||||
'3': ['March',
|
||||
'Mars'],
|
||||
'4': ['April',
|
||||
'Apríl'],
|
||||
'5': ['May',
|
||||
'Maí'],
|
||||
'6': ['June',
|
||||
'Júní'],
|
||||
'7': ['July',
|
||||
'Júlí'],
|
||||
'8': ['August',
|
||||
'Ágúst'],
|
||||
'9': ['September',
|
||||
'September'],
|
||||
'10': ['Oktober',
|
||||
'Október'],
|
||||
'11': ['November',
|
||||
'Nóvember'],
|
||||
'12': ['December',
|
||||
'Desember'],
|
||||
},
|
||||
}
|
||||
const langs = {
|
||||
'en': 0,
|
||||
'is': 1,
|
||||
}
|
||||
|
||||
const regexNumber = new RegExp('^\\d+$')
|
||||
|
||||
out.printdate = function(date) {
|
||||
let day = date.getDate().toString()
|
||||
if (out.currentlang === 'en') {
|
||||
let last = day[day.length - 1]
|
||||
if (last === '1') {
|
||||
day += 'st'
|
||||
} else if (last === '2') {
|
||||
day += 'nd'
|
||||
} else if (last === '3') {
|
||||
day += 'rd'
|
||||
} else {
|
||||
day += 'th'
|
||||
}
|
||||
} else {
|
||||
day += '.'
|
||||
}
|
||||
return `${day} ${out.months[date.getMonth() + 1]} ${date.getFullYear()}, ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
||||
}
|
||||
|
||||
out.langset = function(lang) {
|
||||
out.currentlang = lang
|
||||
let index = langs[lang]
|
||||
|
||||
for (let key of Object.keys(i18n)) {
|
||||
if (!Array.isArray(i18n[key])) {
|
||||
out[key] = {}
|
||||
for (let subKey of Object.keys(i18n[key])) {
|
||||
out[key][subKey] = i18n[key][subKey][index]
|
||||
}
|
||||
} else {
|
||||
out[key] = i18n[key][index]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.langtoggle = function() {
|
||||
out.langset(out.currentlang === 'en' ? 'is' : 'en')
|
||||
return false
|
||||
}
|
||||
|
||||
out.format = function(str, ...args) {
|
||||
return out.mformat(str, ...args).join('')
|
||||
}
|
||||
|
||||
out.mformat = function(str, ...args) {
|
||||
let split = (str || '').split(/\{|\}/)
|
||||
return split.map(function(item) {
|
||||
if (regexNumber.test(item)) {
|
||||
return args[Number(item)] || item
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
out.langset('is')
|
||||
|
||||
module.exports = out
|
|
@ -0,0 +1,265 @@
|
|||
const m = require('mithril')
|
||||
const api = require('./api')
|
||||
const Authentication = require('./authentication')
|
||||
const Input = require('./input')
|
||||
const lang = require('./lang')
|
||||
const videos = require('./videos')
|
||||
const HoldButton = require('./holdbutton')
|
||||
|
||||
const Article = {
|
||||
oninit: function(vnode) {
|
||||
this.error = ''
|
||||
this.path = ''
|
||||
this.data = null
|
||||
this.editing = false
|
||||
this.cacheImage = null
|
||||
this.form = {
|
||||
title: 'Sunnudagssamkoma',
|
||||
date: new Date(),
|
||||
banner: null,
|
||||
metadata: {
|
||||
speaker: '',
|
||||
},
|
||||
}
|
||||
this.onbeforeupdate(vnode)
|
||||
},
|
||||
|
||||
onbeforeupdate: function(vnode) {
|
||||
let path = m.route.param('year').padStart(4, '0')
|
||||
+ '-' + m.route.param('month').padStart(2, '0')
|
||||
+ '-' + m.route.param('path')
|
||||
if (this.path === path) return
|
||||
|
||||
this.fetchArticle(vnode, path)
|
||||
},
|
||||
|
||||
fetchArticle: function(vnode, path) {
|
||||
this.error = ''
|
||||
this.data = null
|
||||
this.path = path
|
||||
this.cacheImage = null
|
||||
this.editing = false
|
||||
|
||||
api.sendRequest({
|
||||
method: 'GET',
|
||||
url: '/api/articles/' + this.path,
|
||||
})
|
||||
.then((result) => {
|
||||
this.data = result.article
|
||||
this.gotArticle(vnode)
|
||||
}, (err) => {
|
||||
this.error = err.message
|
||||
})
|
||||
},
|
||||
|
||||
gotArticle: function(vnode) {
|
||||
if (!this.data) {
|
||||
return this.error = 'Article not found'
|
||||
}
|
||||
this.form.title = this.data.name
|
||||
this.form.date = new Date(this.data.publish_at)
|
||||
this.form.banner = this.data.banner_path
|
||||
this.form.metadata.speaker = this.data.content.speaker
|
||||
},
|
||||
|
||||
updatevideo: function(vnode, e) {
|
||||
this.error = ''
|
||||
|
||||
if (!this.form.title) this.error = lang.upload_missing_title // Title is missing
|
||||
if (!this.form.date) this.error = lang.upload_missing_date // Date is missing
|
||||
if (this.error) return false
|
||||
|
||||
let promise = Promise.resolve()
|
||||
|
||||
if (Authentication.currentUser && (typeof(this.form.banner) !== 'string' && this.cacheImage?.file !== this.form.banner)) {
|
||||
promise = api.sendRequest({
|
||||
method: 'GET',
|
||||
url: '/api/auth/uploadToken',
|
||||
})
|
||||
.then(res => {
|
||||
return api.uploadBanner(this.form.banner, res)
|
||||
.then(imageData => {
|
||||
this.cacheImage = imageData
|
||||
|
||||
if (this.data.banner_path) {
|
||||
api.sendRequest({
|
||||
method: 'DELETE',
|
||||
url: res.delete + this.data.banner_path.slice(this.data.banner_path.lastIndexOf('/') + 1) + '?token=' + res.token,
|
||||
}).catch(err => console.error(err))
|
||||
}
|
||||
|
||||
return res
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
promise.then(() => {
|
||||
return api.sendRequest({
|
||||
method: 'PUT',
|
||||
url: '/api/auth/articles/' + this.data.id,
|
||||
body: {
|
||||
name: this.form.title,
|
||||
page_id: 'null',
|
||||
path: this.form.date.toISOString().replace('T', '_').replace(/:/g, '').split('.')[0],
|
||||
content: JSON.stringify(this.form.metadata),
|
||||
publish_at: this.form.date,
|
||||
admin_id: Authentication.getTokenDecoded().user_id,
|
||||
is_featured: false,
|
||||
media: null,
|
||||
banner: this.cacheImage ? {
|
||||
filename: this.cacheImage.medium.filename,
|
||||
path: this.cacheImage.medium.path,
|
||||
type: 'image/avif',
|
||||
size: this.cacheImage.size,
|
||||
preview: {
|
||||
base64: this.cacheImage.preview.base64,
|
||||
},
|
||||
} : null,
|
||||
},
|
||||
})
|
||||
})
|
||||
.then(res => {
|
||||
this.fetchArticle(vnode, this.path)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
this.error = lang.format(lang.article_error, error.message) // Error while saving:
|
||||
})
|
||||
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
deletevideo: function(vnode) {
|
||||
api.sendRequest({
|
||||
method: 'GET',
|
||||
url: '/api/auth/uploadToken',
|
||||
body: this.form,
|
||||
})
|
||||
.then(res => {
|
||||
return Promise.all([
|
||||
this.data.banner_path ? api.sendRequest({
|
||||
method: 'DELETE',
|
||||
url: res.delete + this.data.banner_path.slice(this.data.banner_path.lastIndexOf('/') + 1) + '?token=' + res.token,
|
||||
}).catch(err => console.error(err)) : Promise.resolve(),
|
||||
this.data.media_path ? api.sendRequest({
|
||||
method: 'DELETE',
|
||||
url: res.delete + this.data.media_path.slice(this.data.media_path.lastIndexOf('/') + 1) + '?token=' + res.token,
|
||||
}).catch(err => console.error(err)) : Promise.resolve(),
|
||||
])
|
||||
})
|
||||
.then(() => {
|
||||
return api.sendRequest({
|
||||
method: 'DELETE',
|
||||
url: '/api/auth/articles/' + this.data.id,
|
||||
})
|
||||
})
|
||||
.then(res => {
|
||||
videos.removeArticle(this.data.id)
|
||||
m.route.set('/')
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!error) return
|
||||
|
||||
this.error = lang.format(lang.delete_error, error.message) // Error while uploading:
|
||||
})
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
api.loading && !this.data ? m('div.loading-spinner') : null,
|
||||
this.data ? [
|
||||
this.data.media_path
|
||||
? [
|
||||
m('.player', [
|
||||
m('video', {
|
||||
crossorigin: '',
|
||||
controls: true,
|
||||
preload: 'none',
|
||||
poster: this.data.banner_path || '/assets/placeholder.avif',
|
||||
}, [
|
||||
m('source', {
|
||||
src: this.data.media_path
|
||||
})
|
||||
]),
|
||||
]),
|
||||
]
|
||||
: null,
|
||||
this.editing
|
||||
? m('form.article', {
|
||||
onsubmit: this.updatevideo.bind(this, vnode),
|
||||
}, [
|
||||
m('div.form-row', [
|
||||
m('div.form-columns', [
|
||||
m(Input, {
|
||||
label: '',
|
||||
type: 'file',
|
||||
accept: 'image/*',
|
||||
utility: 'image',
|
||||
form: this.form,
|
||||
formKey: 'banner',
|
||||
}),
|
||||
]),
|
||||
m('div.form-columns.article-name', [
|
||||
m(Input, {
|
||||
label: 'Title',
|
||||
form: this.form,
|
||||
formKey: 'title',
|
||||
}),
|
||||
m(Input, {
|
||||
label: 'Date (dd.mm.yyyy)',
|
||||
type: 'text',
|
||||
utility: 'datetime',
|
||||
form: this.form,
|
||||
formKey: 'date',
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
m('p.separator', 'Optional'),
|
||||
m(Input, {
|
||||
label: 'Speaker',
|
||||
form: this.form.metadata,
|
||||
formKey: 'speaker',
|
||||
}),
|
||||
this.error ? m('div.full-error', this.error) : null,
|
||||
m('div.row', [
|
||||
m('input.spinner', {
|
||||
hidden: api.loading,
|
||||
type: 'submit',
|
||||
value: lang.edit,
|
||||
}),
|
||||
m('div.filler', {
|
||||
hidden: api.loading,
|
||||
}),
|
||||
api.loading ? m('div.loading-spinner') : null,
|
||||
m(HoldButton, {
|
||||
class: 'button spinner',
|
||||
onclick: () => this.deletevideo(vnode),
|
||||
hidden: api.loading,
|
||||
text: lang.delete,
|
||||
}),
|
||||
]),
|
||||
])
|
||||
: m('div.article', [
|
||||
m('h1', this.data.name),
|
||||
m('p', [
|
||||
lang.printdate(this.form.date),
|
||||
]),
|
||||
m('div.table', [
|
||||
m('div.table-row', [
|
||||
m('div.table-item', lang.article_speaker),
|
||||
m('div.table-item', this.data.content.speaker || '...'),
|
||||
]),
|
||||
]),
|
||||
Authentication.currentUser?.rank >= 10
|
||||
? m('button.button', { onclick: () => this.editing = true },lang.edit)
|
||||
: null,
|
||||
]),
|
||||
] : [
|
||||
this.error ? m('div.full-error', this.error) : null,
|
||||
],
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = Article
|
|
@ -0,0 +1,62 @@
|
|||
const m = require('mithril')
|
||||
const api = require('./api')
|
||||
const Authentication = require('./authentication')
|
||||
const videos = require('./videos')
|
||||
const lang = require('./lang')
|
||||
|
||||
const Browse = {
|
||||
oninit: function(vnode) {
|
||||
},
|
||||
|
||||
mArticles: function(vnode, articles) {
|
||||
return articles.map(article => {
|
||||
return m(m.route.Link, {
|
||||
href: ['', article.publish_at.getFullYear(), article.publish_at.getMonth() + 1, article.path_short].join('/'),
|
||||
style: article.avif_preview ? `background-image: url('${article.avif_preview}')` : null,
|
||||
}, [
|
||||
m('span', lang.printdate(article.publish_at)),
|
||||
m('span', article.name),
|
||||
])
|
||||
})
|
||||
},
|
||||
|
||||
mMonth: function(vnode, year) {
|
||||
return year.branches.map(month => {
|
||||
return [
|
||||
m('.gallery-month', lang.months[month.title]),
|
||||
m('.group', this.mArticles(vnode, month.videos))
|
||||
]
|
||||
})
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
let articles = videos.month?.videos || videos.year?.videos || videos.Articles
|
||||
|
||||
return [
|
||||
api.loading ? m('div.loading-spinner') : null,
|
||||
videos.error
|
||||
? m('div.full-error', { onclick: videos.refreshTree }, [
|
||||
videos.error, m('br'), 'Click here to try again'
|
||||
])
|
||||
: null,
|
||||
m('.gallery', [
|
||||
videos.month
|
||||
? m('.group', this.mArticles(vnode, articles))
|
||||
: null,
|
||||
videos.year && !videos.month
|
||||
? this.mMonth(vnode, videos.year)
|
||||
: null,
|
||||
!videos.year
|
||||
? videos.Tree.slice(-1).map(year => {
|
||||
return [
|
||||
m('.gallery-year', year.title),
|
||||
this.mMonth(vnode, year),
|
||||
]
|
||||
})
|
||||
: null
|
||||
]),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = Browse
|
|
@ -0,0 +1,86 @@
|
|||
const m = require('mithril')
|
||||
const Authentication = require('./authentication')
|
||||
const api = require('./api')
|
||||
const Input = require('./input')
|
||||
const lang = require('./lang')
|
||||
const videos = require('./videos')
|
||||
|
||||
const Login = {
|
||||
oninit: function(vnode) {
|
||||
this.redirect = vnode.attrs.redirect || ''
|
||||
Authentication.requiresNotLogin()
|
||||
|
||||
this.error = ''
|
||||
this.form = {
|
||||
email: '',
|
||||
password: '',
|
||||
}
|
||||
},
|
||||
|
||||
loginuser: function(vnode, e) {
|
||||
e.preventDefault()
|
||||
this.error = ''
|
||||
|
||||
if (!this.form.email) this.error = lang.login_missing_email // Email is missing
|
||||
if (!this.form.password) this.error = lang.login_missing_password // Password is missing
|
||||
|
||||
if (this.error) return false
|
||||
|
||||
api.sendRequest({
|
||||
method: 'POST',
|
||||
url: '/api/authentication/login',
|
||||
body: this.form,
|
||||
})
|
||||
.then((result) => {
|
||||
if (!result.token) return Promise.reject(new Error(lang.login_error_auth)) // Unknown error from server. Try again later
|
||||
Authentication.updateToken(result.token)
|
||||
m.route.set(this.redirect || '/')
|
||||
videos.refreshTree()
|
||||
})
|
||||
.catch((error) => {
|
||||
this.error = lang.format(lang.login_error, error.message) // Error while logging in:
|
||||
this.form.password = ''
|
||||
})
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m('div.page.page-login', [
|
||||
m('div.modal', [
|
||||
m('form', {
|
||||
onsubmit: this.loginuser.bind(this, vnode),
|
||||
}, [
|
||||
m('h3', lang.header_title /* Filadelfia archival center */),
|
||||
this.error ? m('p.error', this.error) : null,
|
||||
m(Input, {
|
||||
label: lang.login_email, // Email or name
|
||||
form: this.form,
|
||||
formKey: 'email',
|
||||
}),
|
||||
m(Input, {
|
||||
label: lang.login_password, // Password
|
||||
type: 'password',
|
||||
form: this.form,
|
||||
formKey: 'password',
|
||||
}),
|
||||
m('input.spinner', {
|
||||
hidden: api.loading,
|
||||
type: 'submit',
|
||||
value: lang.login_submit, // Log in
|
||||
}),
|
||||
api.loading ? m('div.loading-spinner') : null,
|
||||
]),
|
||||
]),
|
||||
m('footer', lang.mformat(
|
||||
lang.unsplash, // Photo by X on Y
|
||||
m('a', { href: 'https://unsplash.com/@franhotchin?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash', target: '_blank' }, 'Francesca Hotchin'),
|
||||
m('a', { href: 'https://unsplash.com/photos/landscape-photo-of-mountain-covered-with-snow-FN-cedy6NHA?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash', target: '_blank' }, 'Unsplash'),
|
||||
)),
|
||||
]),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = Login
|
|
@ -0,0 +1,15 @@
|
|||
const m = require('mithril')
|
||||
const Authentication = require('./authentication')
|
||||
|
||||
const Logout = {
|
||||
oninit: function(vnode) {
|
||||
Authentication.clearToken()
|
||||
m.route.set(vnode.attrs.redirect || '/')
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return []
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = Logout
|
|
@ -0,0 +1,227 @@
|
|||
const m = require('mithril')
|
||||
const Authentication = require('./authentication')
|
||||
const api = require('./api')
|
||||
const Input = require('./input')
|
||||
const lang = require('./lang')
|
||||
const videos = require('./videos')
|
||||
|
||||
const Upload = {
|
||||
oninit: function(vnode) {
|
||||
Authentication.requiresLogin()
|
||||
this.error = ''
|
||||
let d = new Date()
|
||||
d.setDate(d.getDate() - d.getDay())
|
||||
d.setHours(11)
|
||||
d.setMinutes(0)
|
||||
d.setSeconds(0)
|
||||
d.setMilliseconds(0)
|
||||
|
||||
this.cacheVideo = null
|
||||
this.cacheImage = null
|
||||
this.uploading = null
|
||||
this.bannerStatus = null
|
||||
this.form = {
|
||||
title: 'Sunnudagssamkoma',
|
||||
date: d,
|
||||
file: null,
|
||||
banner: null,
|
||||
metadata: {
|
||||
speaker: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
uploadvideo: function(vnode, e) {
|
||||
this.error = ''
|
||||
|
||||
if (!this.form.title) this.error = lang.upload_missing_title // Title is missing
|
||||
if (!this.form.date) this.error = lang.upload_missing_date // Date is missing
|
||||
if (!this.form.file) this.error = lang.upload_missing_file // Video file missing
|
||||
if (!this.form.banner) this.error = lang.upload_missing_banner // Poster image missing
|
||||
|
||||
if (this.error) return false
|
||||
|
||||
api.sendRequest({
|
||||
method: 'GET',
|
||||
url: '/api/auth/uploadToken',
|
||||
})
|
||||
.then(res => {
|
||||
if (this.cacheImage?.file === this.form.banner) {
|
||||
return this.cacheImage
|
||||
}
|
||||
|
||||
return api.uploadBanner(this.form.banner, res, (status) => {
|
||||
this.bannerStatus = status
|
||||
m.redraw()
|
||||
})
|
||||
.then(imageData => {
|
||||
this.cacheImage = imageData
|
||||
return res
|
||||
})
|
||||
})
|
||||
.then(res => {
|
||||
if (this.cacheVideo?.file === this.form.file) {
|
||||
return this.cacheVideo
|
||||
}
|
||||
|
||||
return api.uploadFileProgress({
|
||||
url: res.path + '?token=' + res.token,
|
||||
}, this.form.file, (xhr, progress, perSecond) => {
|
||||
this.uploading = {
|
||||
progress,
|
||||
xhr,
|
||||
perSecond
|
||||
}
|
||||
m.redraw()
|
||||
})
|
||||
})
|
||||
.then(res => {
|
||||
this.cacheVideo = {
|
||||
file: this.form.file,
|
||||
filename: res.filename,
|
||||
path: res.path,
|
||||
}
|
||||
this.uploading = null
|
||||
|
||||
return api.sendRequest({
|
||||
method: 'PUT',
|
||||
url: '/api/auth/articles/0',
|
||||
body: {
|
||||
name: this.form.title,
|
||||
page_id: 'null',
|
||||
path: this.form.date.toISOString().replace('T', '_').replace(/:/g, '').split('.')[0],
|
||||
content: JSON.stringify(this.form.metadata),
|
||||
publish_at: this.form.date,
|
||||
admin_id: Authentication.getTokenDecoded().user_id,
|
||||
is_featured: false,
|
||||
media: {
|
||||
filename: res.filename,
|
||||
path: res.path,
|
||||
type: this.form.file.type,
|
||||
size: this.form.file.size,
|
||||
},
|
||||
banner: {
|
||||
filename: this.cacheImage.medium.filename,
|
||||
path: this.cacheImage.medium.path,
|
||||
type: 'image/avif',
|
||||
size: this.cacheImage.size,
|
||||
preview: {
|
||||
base64: this.cacheImage.preview.base64,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
.then(res => {
|
||||
videos.refreshTree()
|
||||
m.route.set('/')
|
||||
})
|
||||
.catch((error) => {
|
||||
this.bannerStatus = null
|
||||
this.uploading = null
|
||||
if (!error) return
|
||||
|
||||
this.error = lang.format(lang.upload_error, error.message) // Error while uploading:
|
||||
})
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
cancelUpload(e) {
|
||||
e.stopPropagation()
|
||||
this.uploading.xhr.abortRequest()
|
||||
this.uploading = null
|
||||
return false
|
||||
},
|
||||
|
||||
filechanged(file) {
|
||||
if (!file || !file.name) return
|
||||
|
||||
let matches = /^(\d{4})-(\d\d)-(\d\d)_(\d\d)-(\d\d)/.exec(file.name)
|
||||
if (!matches) return
|
||||
|
||||
var date = new Date(matches.slice(1, 4).join('-') + 'T' + matches.slice(4,6).join(':') + ':00')
|
||||
if (isNaN(date.getTime())) return
|
||||
|
||||
if (date.getMinutes() >= 30 || date.getHours() === 10) {
|
||||
date.setHours(date.getHours() + 1)
|
||||
}
|
||||
date.setMinutes(0)
|
||||
this.form.date = date
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m('div.page.page-upload', [
|
||||
m('div.modal', [
|
||||
m('form', {
|
||||
onsubmit: this.uploadvideo.bind(this, vnode),
|
||||
}, [
|
||||
m('h3', 'Upload new video'),
|
||||
this.error ? m('p.error', this.error) : null,
|
||||
m(Input, {
|
||||
label: 'Title',
|
||||
form: this.form,
|
||||
formKey: 'title',
|
||||
}),
|
||||
m(Input, {
|
||||
label: 'Date (dd.mm.yyyy)',
|
||||
type: 'text',
|
||||
utility: 'datetime',
|
||||
form: this.form,
|
||||
formKey: 'date',
|
||||
}),
|
||||
m(Input, {
|
||||
label: 'Video',
|
||||
type: 'file',
|
||||
accept: '.webm',
|
||||
utility: 'file',
|
||||
button: 'fa-video',
|
||||
form: this.form,
|
||||
formKey: 'file',
|
||||
oninput: (file) => this.filechanged(file),
|
||||
}),
|
||||
m(Input, {
|
||||
label: 'Mynd',
|
||||
type: 'file',
|
||||
accept: 'image/*',
|
||||
utility: 'image',
|
||||
form: this.form,
|
||||
formKey: 'banner',
|
||||
}),
|
||||
m('p.separator', 'Optional'),
|
||||
m(Input, {
|
||||
label: 'Speaker',
|
||||
form: this.form.metadata,
|
||||
formKey: 'speaker',
|
||||
}),
|
||||
m('input.spinner', {
|
||||
hidden: api.loading,
|
||||
type: 'submit',
|
||||
value: 'Begin upload',
|
||||
}),
|
||||
api.loading ? m('div.loading-spinner') : null,
|
||||
this.bannerStatus ? [
|
||||
m('p', this.bannerStatus),
|
||||
m('.loading-bar', { style: `--progress: 0%` }),
|
||||
] : null,
|
||||
this.uploading ? [
|
||||
m('p', `${Math.floor(this.uploading.progress)}% (${this.uploading.perSecond}/s)`),
|
||||
m('.loading-bar', { style: `--progress: ${this.uploading.progress}%` }),
|
||||
m('button.button.button-alert', {
|
||||
onclick: this.cancelUpload.bind(this),
|
||||
}, 'Cancel upload'),
|
||||
] : null,
|
||||
]),
|
||||
]),
|
||||
m('footer', lang.mformat(
|
||||
lang.unsplash, // Photo by X on Y
|
||||
m('a', { href: 'https://unsplash.com/@franhotchin?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash', target: '_blank' }, 'Francesca Hotchin'),
|
||||
m('a', { href: 'https://unsplash.com/photos/landscape-photo-of-mountain-covered-with-snow-FN-cedy6NHA?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash', target: '_blank' }, 'Unsplash'),
|
||||
)),
|
||||
]),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = Upload
|
|
@ -0,0 +1,120 @@
|
|||
const m = require('mithril')
|
||||
const api = require('./api')
|
||||
|
||||
const Tree = []
|
||||
const Articles = []
|
||||
|
||||
exports.Tree = Tree
|
||||
exports.Articles = Articles
|
||||
|
||||
exports.loading = false
|
||||
exports.error = ''
|
||||
exports.year = null
|
||||
exports.month = null
|
||||
|
||||
const matcher = /\/(\d+)(\/\d+)?/
|
||||
|
||||
function calculateActiveBranches() {
|
||||
let path = matcher.exec(m.route.get())
|
||||
if (path && path[1] !== exports.year?.title) {
|
||||
for (let year of Tree) {
|
||||
if (year.title === path[1]) {
|
||||
exports.year = year
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if (!path && m.route.get() === '/') {
|
||||
exports.year = Tree[Tree.length - 1]
|
||||
} else if (!path) {
|
||||
exports.year = null
|
||||
}
|
||||
if (path && exports.year && path[2]) {
|
||||
exports.month = exports.year.branches[Number(path[2].slice(1)) - 1] || null
|
||||
} else if (!path?.[2]) {
|
||||
exports.month = null
|
||||
}
|
||||
}
|
||||
|
||||
function rebuildTree() {
|
||||
Tree.splice(0, Tree.length)
|
||||
|
||||
if (!Articles.length) return
|
||||
|
||||
let startYear = Articles[0].publish_at
|
||||
let target = new Date()
|
||||
let articleIndex = 0
|
||||
|
||||
for (let year = startYear.getFullYear(); year <= target.getFullYear(); year++) {
|
||||
let branchYear = {
|
||||
title: year.toString(),
|
||||
type: 'year',
|
||||
branches: [],
|
||||
videos: []
|
||||
}
|
||||
Tree.push(branchYear)
|
||||
let lastMonth = year === target.getFullYear() ? target.getMonth() + 1 : 12
|
||||
|
||||
for (let month = 1; month <= lastMonth; month++) {
|
||||
let branchMonth = {
|
||||
title: month.toString(),
|
||||
type: 'month',
|
||||
branches: [],
|
||||
videos: []
|
||||
}
|
||||
branchYear.branches.push(branchMonth)
|
||||
|
||||
let start = new Date(year, month - 1)
|
||||
let end = new Date(year, month)
|
||||
|
||||
for (; Articles[articleIndex] && Articles[articleIndex].publish_at >= start && Articles[articleIndex].publish_at < end; articleIndex++) {
|
||||
branchYear.videos.push(Articles[articleIndex])
|
||||
branchMonth.videos.push(Articles[articleIndex])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeArticle(id) {
|
||||
let index = Articles.findIndex(article => article.id === id)
|
||||
if (index >= 0) {
|
||||
Articles.splice(index, 1)
|
||||
rebuildTree()
|
||||
}
|
||||
}
|
||||
|
||||
function refreshTree() {
|
||||
exports.error = ''
|
||||
|
||||
if (exports.loading) return Promise.resolve()
|
||||
|
||||
exports.loading = true
|
||||
|
||||
m.redraw()
|
||||
|
||||
return api.sendRequest({
|
||||
method: 'GET',
|
||||
url: '/api/articles',
|
||||
})
|
||||
.then(result => {
|
||||
result.videos.forEach(video => {
|
||||
video.publish_at = new Date(video.publish_at)
|
||||
video.path_short = video.path.split('-')[2]
|
||||
})
|
||||
|
||||
Articles.splice(0, Articles.length)
|
||||
Articles.push.apply(Articles, result.videos)
|
||||
|
||||
rebuildTree()
|
||||
}, err => {
|
||||
exports.error = 'Error fetching videos: ' + err.message
|
||||
})
|
||||
.then(() => {
|
||||
exports.loading = false
|
||||
m.redraw()
|
||||
})
|
||||
}
|
||||
|
||||
exports.removeArticle = removeArticle
|
||||
exports.rebuildTree = rebuildTree
|
||||
exports.refreshTree = refreshTree
|
||||
exports.calculateActiveBranches = calculateActiveBranches
|
|
@ -0,0 +1 @@
|
|||
../base
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"scripts": {
|
||||
"build": "esbuild app/index.js --bundle --outfile=public/assets/app.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@eonasdan/tempus-dominus": "^6.7.19",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"eltro": "^1.4.4",
|
||||
"esbuild": "^0.19.5",
|
||||
"mithril": "^2.2.2",
|
||||
"service-core": "^3.0.0-beta.17"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import fs from 'fs'
|
||||
import { pathToFileURL } from 'url'
|
||||
import config from './base/config.mjs'
|
||||
|
||||
export function start(http, port, ctx) {
|
||||
config.sources[1].store = ctx.config
|
||||
|
||||
return import('./api/server.mjs')
|
||||
.then(function(module) {
|
||||
let server = new module.default(http, port, ctx)
|
||||
return server.run()
|
||||
})
|
||||
}
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
import('service-core').then(core => {
|
||||
const port = 4130
|
||||
|
||||
var core = new core.ServiceCore('filadelfia_web', import.meta.url, port, '')
|
||||
|
||||
let config = {
|
||||
frontend: {
|
||||
url: 'http://localhost:' + port
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
config = JSON.parse(fs.readFileSync('./config.json'))
|
||||
} catch {}
|
||||
|
||||
config.port = port
|
||||
|
||||
core.setConfig(config)
|
||||
core.init({ start }).then(function() {
|
||||
return core.run()
|
||||
})
|
||||
})
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"name": "filadelfia_archive",
|
||||
"version": "1.0.8",
|
||||
"port": 4130,
|
||||
"description": "Filadelfia archive",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node index.mjs",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build:prod": "asbundle app/index.js public/assets/app.js",
|
||||
"build": "esbuild app/index.js --bundle --outfile=public/assets/app.js",
|
||||
"dev:build": "eltro --watch build --npm build",
|
||||
"dev:server": "eltro --watch server --npm server",
|
||||
"dev:build:old": "npm-watch build",
|
||||
"dev:server:old": "npm-watch server",
|
||||
"server": "node index.mjs | bunyan"
|
||||
},
|
||||
"watch": {
|
||||
"server": {
|
||||
"patterns": [
|
||||
"api",
|
||||
"base",
|
||||
"../base"
|
||||
],
|
||||
"extensions": "js,mjs",
|
||||
"quiet": true,
|
||||
"inherit": true
|
||||
},
|
||||
"build": {
|
||||
"patterns": [
|
||||
"app"
|
||||
],
|
||||
"extensions": "js",
|
||||
"quiet": true,
|
||||
"inherit": true
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.nfp.is/nfp/nfp_sites.git"
|
||||
},
|
||||
"author": "Jonatan Nilsson",
|
||||
"license": "WTFPL",
|
||||
"bugs": {
|
||||
"url": "https://git.nfp.is/nfp/nfp_sites/issues"
|
||||
},
|
||||
"homepage": "https://git.nfp.is/nfp/nfp_sites",
|
||||
"dependencies": {
|
||||
"dot": "^2.0.0-beta.1",
|
||||
"flaska": "^1.3.2",
|
||||
"formidable": "^1.2.6",
|
||||
"msnodesqlv8": "^4.1.1",
|
||||
"nconf-lite": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eonasdan/tempus-dominus": "^6.7.19",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"eltro": "^1.4.4",
|
||||
"esbuild": "^0.19.5",
|
||||
"mithril": "^2.2.2",
|
||||
"service-core": "^3.0.0-beta.17"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 3.1 KiB |
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="26.656599mm"
|
||||
height="26.65659mm"
|
||||
viewBox="0 0 26.656599 26.65659"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.1 (91b66b0783, 2023-11-16)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#111111"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showguides="true"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4.2083894"
|
||||
inkscape:cx="-36.712382"
|
||||
inkscape:cy="66.890197"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1377"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
<sodipodi:guide
|
||||
position="21.496619,297"
|
||||
orientation="0,-1"
|
||||
id="guide1"
|
||||
inkscape:locked="false" />
|
||||
<inkscape:grid
|
||||
id="grid2"
|
||||
units="mm"
|
||||
originx="-90.44421"
|
||||
originy="-127.62701"
|
||||
spacingx="0.99999998"
|
||||
spacingy="1"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="5"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="false" />
|
||||
<sodipodi:guide
|
||||
position="-2.3731349e-07,282.22545"
|
||||
orientation="1,0"
|
||||
id="guide2"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="26.656599,279.39628"
|
||||
orientation="1,0"
|
||||
id="guide3"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="8.8910958,270.34341"
|
||||
orientation="0,-1"
|
||||
id="guide4"
|
||||
inkscape:locked="false" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-90.444211,-127.62701)">
|
||||
<path
|
||||
id="path1"
|
||||
d="M 90.444211,154.2836 H 117.10081 V 127.62701 H 90.444211 Z"
|
||||
style="fill:#18597d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.159661" />
|
||||
<path
|
||||
id="path224"
|
||||
d="m 103.77216,127.62701 c 7.36141,0 13.32865,5.96724 13.32865,13.3283 0,7.36106 -5.96724,13.32829 -13.32865,13.32829 -7.360713,0 -13.327947,-5.96723 -13.327947,-13.32829 0,-7.36106 5.967234,-13.3283 13.327947,-13.3283"
|
||||
style="fill:#18597d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.352778" />
|
||||
<path
|
||||
id="path225"
|
||||
d="m 103.77216,128.3629 c -6.954663,0 -12.592405,5.63774 -12.592405,12.5924 0,6.95466 5.637742,12.5924 12.592405,12.5924 6.95501,0 12.59275,-5.63774 12.59275,-12.5924 0,-6.95466 -5.63774,-12.5924 -12.59275,-12.5924"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.352778" />
|
||||
<path
|
||||
id="path226"
|
||||
d="m 103.74393,129.68123 c 6.19478,0 11.21586,5.02179 11.21586,11.21622 0,6.19477 -5.02108,11.21621 -11.21586,11.21621 -6.19442,0 -11.216219,-5.02144 -11.216219,-11.21621 0,-6.19443 5.021799,-11.21622 11.216219,-11.21622"
|
||||
style="fill:#18597d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.352778" />
|
||||
<path
|
||||
id="path227"
|
||||
d="m 102.98229,131.02708 v 5.04931 h 3.34469 c 0.56162,-1.17581 2.25918,-2.76684 3.26989,-3.64631 l -0.45332,1.06045 c 0.0794,-0.067 -0.70238,1.29046 0.009,2.81799 0.63217,1.35713 1.07032,2.09867 1.2894,3.73097 0.35278,-0.94015 0.56691,-1.62736 1.04034,-2.17628 0.27552,-0.31856 0.31538,-0.93698 0.27023,-1.63971 0.38488,0.42192 0.93556,2.09126 0.82726,3.4364 -0.0536,0.67134 0.10936,2.83175 -2.17523,3.46358 -0.36794,1.33879 -1.24283,2.57704 -2.68993,3.37961 -1.78646,0.9906 -2.58057,0.20249 -3.81987,2.15512 -0.0596,-0.33691 -0.0677,-0.71932 -0.0773,-0.99554 -0.12065,-0.1584 -0.20885,-0.26988 -0.26494,-0.57397 -0.0603,-0.16052 0.001,-0.51082 0.26423,-0.57256 -0.0127,-1.36984 1.36384,-1.15641 1.60585,-1.95474 0.52422,-1.24848 -1.57445,-1.39877 -2.44052,-2.12513 v 6.70877 c 0.30127,0.17286 0.58526,0.44062 0.84772,0.85478 1.29082,-2.03412 3.09175,-0.54151 4.95265,-1.57339 l 1.83021,0.63077 -0.1337,0.1076 c -2.52412,1.23119 -4.95406,-0.67381 -6.70772,2.0902 -1.78223,-2.80846 -4.264368,-0.79586 -6.832603,-2.15229 l 1.937463,-0.67628 c 0.670632,0.37183 1.33315,0.41593 1.96744,0.41522 v -10.6306 h -5.036613 c -0.20179,0.0473 -0.21449,-2.18899 0,-2.13713 l 5.036613,0.002 -0.001,-5.04931 c -0.0483,-0.21449 2.18969,-0.19614 2.1396,0 z m 3.31576,7.18397 h -1.01177 c 0.004,0.32773 10e-4,0.62194 10e-4,0.85901 0,0.78317 1.59526,1.75789 2.0387,1.95827 0.14958,-1.2125 -0.12029,-1.60549 -0.8128,-2.50049 -0.0832,-0.10689 -0.15451,-0.21167 -0.21519,-0.31679"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.352778" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.0 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="100.749" height="100.749" viewBox="0 0 26.657 26.657"><path d="M103.772 127.627c7.362 0 13.329 5.967 13.329 13.328s-5.967 13.329-13.329 13.329c-7.36 0-13.328-5.968-13.328-13.329 0-7.36 5.967-13.328 13.328-13.328" style="fill:#18597d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.352778" transform="translate(-90.444 -127.627)"/><path d="M103.772 128.363c-6.955 0-12.592 5.638-12.592 12.592 0 6.955 5.637 12.593 12.592 12.593 6.955 0 12.593-5.638 12.593-12.593 0-6.954-5.638-12.592-12.593-12.592" style="fill:#fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.352778" transform="translate(-90.444 -127.627)"/><path d="M103.744 129.681c6.195 0 11.216 5.022 11.216 11.216 0 6.195-5.021 11.217-11.216 11.217-6.194 0-11.216-5.022-11.216-11.217 0-6.194 5.022-11.216 11.216-11.216" style="fill:#18597d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.352778" transform="translate(-90.444 -127.627)"/><path d="M102.982 131.027v5.05h3.345c.562-1.176 2.26-2.767 3.27-3.647l-.453 1.06c.079-.066-.703 1.291.009 2.819.632 1.357 1.07 2.098 1.289 3.73.353-.94.567-1.627 1.04-2.176.276-.318.316-.937.27-1.64.385.422.936 2.092.828 3.437-.054.671.11 2.832-2.175 3.463-.368 1.34-1.243 2.578-2.69 3.38-1.787.99-2.581.203-3.82 2.155-.06-.337-.068-.72-.078-.995-.12-.159-.208-.27-.264-.574-.06-.16 0-.511.264-.573-.013-1.37 1.364-1.156 1.606-1.955.524-1.248-1.575-1.398-2.44-2.125v6.709c.3.173.584.44.847.855 1.29-2.034 3.092-.542 4.952-1.574l1.83.631-.133.108c-2.524 1.231-4.954-.674-6.708 2.09-1.782-2.808-4.264-.796-6.832-2.152l1.937-.677c.67.372 1.333.416 1.968.416v-10.63h-5.037c-.202.046-.215-2.19 0-2.138l5.037.002-.001-5.05c-.049-.214 2.19-.196 2.14 0zm3.316 7.184h-1.012c.004.328.001.622.001.86 0 .782 1.596 1.757 2.039 1.957.15-1.212-.12-1.605-.813-2.5a2.774 2.774 0 0 1-.215-.317" style="fill:#fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:.352778" transform="translate(-90.444 -127.627)"/></svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,700 @@
|
|||
.visually-hidden, .tempus-dominus-widget [data-action]::after {
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
padding: 0 !important;
|
||||
margin: -1px !important;
|
||||
overflow: hidden !important;
|
||||
clip: rect(0, 0, 0, 0) !important;
|
||||
white-space: nowrap !important;
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.tempus-dominus-widget {
|
||||
list-style: none;
|
||||
padding: 4px;
|
||||
width: 19rem;
|
||||
border-radius: 4px;
|
||||
display: none;
|
||||
z-index: 9999;
|
||||
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.tempus-dominus-widget.calendarWeeks {
|
||||
width: 21rem;
|
||||
}
|
||||
.tempus-dominus-widget.calendarWeeks .date-container-days {
|
||||
grid-auto-columns: 12.5%;
|
||||
grid-template-areas: "a a a a a a a a";
|
||||
}
|
||||
.tempus-dominus-widget [data-action] {
|
||||
cursor: pointer;
|
||||
}
|
||||
.tempus-dominus-widget [data-action]::after {
|
||||
content: attr(title);
|
||||
}
|
||||
.tempus-dominus-widget [data-action].disabled, .tempus-dominus-widget [data-action].disabled:hover {
|
||||
background: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.tempus-dominus-widget .arrow {
|
||||
display: none;
|
||||
}
|
||||
.tempus-dominus-widget.show {
|
||||
display: block;
|
||||
}
|
||||
.tempus-dominus-widget.show.date-container {
|
||||
min-height: 315px;
|
||||
}
|
||||
.tempus-dominus-widget.show.time-container {
|
||||
min-height: 217px;
|
||||
}
|
||||
.tempus-dominus-widget .td-collapse:not(.show) {
|
||||
display: none;
|
||||
}
|
||||
.tempus-dominus-widget .td-collapsing {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
transition: height 0.35s ease;
|
||||
}
|
||||
@media (min-width: 576px) {
|
||||
.tempus-dominus-widget.timepicker-sbs {
|
||||
width: 38em;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.tempus-dominus-widget.timepicker-sbs {
|
||||
width: 38em;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.tempus-dominus-widget.timepicker-sbs {
|
||||
width: 38em;
|
||||
}
|
||||
}
|
||||
.tempus-dominus-widget.timepicker-sbs .td-row {
|
||||
display: flex;
|
||||
}
|
||||
.tempus-dominus-widget.timepicker-sbs .td-row .td-half {
|
||||
flex: 0 0 auto;
|
||||
width: 50%;
|
||||
}
|
||||
.tempus-dominus-widget div[data-action]:active {
|
||||
box-shadow: none;
|
||||
}
|
||||
.tempus-dominus-widget .timepicker-hour,
|
||||
.tempus-dominus-widget .timepicker-minute,
|
||||
.tempus-dominus-widget .timepicker-second {
|
||||
width: 54px;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
margin: 0;
|
||||
}
|
||||
.tempus-dominus-widget button[data-action] {
|
||||
padding: 6px;
|
||||
}
|
||||
.tempus-dominus-widget .toggleMeridiem {
|
||||
text-align: center;
|
||||
height: 38px;
|
||||
}
|
||||
.tempus-dominus-widget .calendar-header {
|
||||
display: grid;
|
||||
grid-template-areas: "a a a";
|
||||
margin-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tempus-dominus-widget .calendar-header .next {
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.tempus-dominus-widget .calendar-header .previous {
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.tempus-dominus-widget .calendar-header .picker-switch {
|
||||
text-align: center;
|
||||
}
|
||||
.tempus-dominus-widget .toolbar {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-rows: 40px;
|
||||
}
|
||||
.tempus-dominus-widget .toolbar div {
|
||||
border-radius: 999px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
}
|
||||
.tempus-dominus-widget .date-container-days {
|
||||
display: grid;
|
||||
grid-template-areas: "a a a a a a a";
|
||||
grid-auto-rows: 40px;
|
||||
grid-auto-columns: 14.2857142857%;
|
||||
}
|
||||
.tempus-dominus-widget .date-container-days .range-in {
|
||||
background-color: #01419e !important;
|
||||
border: none;
|
||||
border-radius: 0 !important;
|
||||
box-shadow: -5px 0 0 #01419e, 5px 0 0 #01419e;
|
||||
}
|
||||
.tempus-dominus-widget .date-container-days .range-end {
|
||||
border-radius: 0 50px 50px 0 !important;
|
||||
}
|
||||
.tempus-dominus-widget .date-container-days .range-start {
|
||||
border-radius: 50px 0 0 50px !important;
|
||||
}
|
||||
.tempus-dominus-widget .date-container-days .dow {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
.tempus-dominus-widget .date-container-days .cw {
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
font-size: 0.8em;
|
||||
line-height: 20px;
|
||||
cursor: default;
|
||||
}
|
||||
.tempus-dominus-widget .date-container-decades,
|
||||
.tempus-dominus-widget .date-container-years,
|
||||
.tempus-dominus-widget .date-container-months {
|
||||
display: grid;
|
||||
grid-template-areas: "a a a";
|
||||
grid-auto-rows: calc((19rem - 8px) / 7);
|
||||
}
|
||||
.tempus-dominus-widget .time-container-hour,
|
||||
.tempus-dominus-widget .time-container-minute,
|
||||
.tempus-dominus-widget .time-container-second {
|
||||
display: grid;
|
||||
grid-template-areas: "a a a a";
|
||||
grid-auto-rows: calc((19rem - 8px) / 7);
|
||||
}
|
||||
.tempus-dominus-widget .time-container-clock {
|
||||
display: grid;
|
||||
grid-auto-rows: calc((19rem - 8px) / 7);
|
||||
}
|
||||
.tempus-dominus-widget .time-container-clock .no-highlight {
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
}
|
||||
.tempus-dominus-widget .date-container-decades div:not(.no-highlight),
|
||||
.tempus-dominus-widget .date-container-years div:not(.no-highlight),
|
||||
.tempus-dominus-widget .date-container-months div:not(.no-highlight),
|
||||
.tempus-dominus-widget .date-container-days div:not(.no-highlight),
|
||||
.tempus-dominus-widget .time-container-clock div:not(.no-highlight),
|
||||
.tempus-dominus-widget .time-container-hour div:not(.no-highlight),
|
||||
.tempus-dominus-widget .time-container-minute div:not(.no-highlight),
|
||||
.tempus-dominus-widget .time-container-second div:not(.no-highlight) {
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
border-radius: 999px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
}
|
||||
.tempus-dominus-widget .date-container-decades div:not(.no-highlight).disabled, .tempus-dominus-widget .date-container-decades div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget .date-container-years div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget .date-container-years div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget .date-container-months div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget .date-container-months div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget .date-container-days div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget .date-container-days div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget .time-container-clock div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget .time-container-clock div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget .time-container-hour div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget .time-container-hour div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget .time-container-minute div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget .time-container-minute div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget .time-container-second div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget .time-container-second div:not(.no-highlight).disabled:hover {
|
||||
background: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.tempus-dominus-widget .date-container-decades div:not(.no-highlight).today,
|
||||
.tempus-dominus-widget .date-container-years div:not(.no-highlight).today,
|
||||
.tempus-dominus-widget .date-container-months div:not(.no-highlight).today,
|
||||
.tempus-dominus-widget .date-container-days div:not(.no-highlight).today,
|
||||
.tempus-dominus-widget .time-container-clock div:not(.no-highlight).today,
|
||||
.tempus-dominus-widget .time-container-hour div:not(.no-highlight).today,
|
||||
.tempus-dominus-widget .time-container-minute div:not(.no-highlight).today,
|
||||
.tempus-dominus-widget .time-container-second div:not(.no-highlight).today {
|
||||
position: relative;
|
||||
}
|
||||
.tempus-dominus-widget .date-container-decades div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget .date-container-years div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget .date-container-months div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget .date-container-days div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget .time-container-clock div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget .time-container-hour div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget .time-container-minute div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget .time-container-second div:not(.no-highlight).today:before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
border: solid transparent;
|
||||
border-width: 0 0 7px 7px;
|
||||
position: absolute;
|
||||
bottom: 6px;
|
||||
right: 6px;
|
||||
}
|
||||
.tempus-dominus-widget .time-container {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.tempus-dominus-widget button {
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 1rem;
|
||||
border-radius: 0.25rem;
|
||||
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td.day,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td.hour,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td.minute,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td.second,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=incrementHours],
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=incrementMinutes],
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=incrementSeconds],
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=decrementHours],
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=decrementMinutes],
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=decrementSeconds],
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=showHours],
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=showMinutes],
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=showSeconds],
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=togglePeriod] {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td.day:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td.hour:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td.minute:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td.second:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=incrementHours]:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=incrementMinutes]:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=incrementSeconds]:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=decrementHours]:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=decrementMinutes]:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=decrementSeconds]:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=showHours]:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=showMinutes]:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=showSeconds]:hover,
|
||||
.tempus-dominus-widget.tempus-dominus-widget-readonly table td [data-action=togglePeriod]:hover {
|
||||
background: none;
|
||||
}
|
||||
.tempus-dominus-widget.light {
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
}
|
||||
.tempus-dominus-widget.light [data-action].disabled, .tempus-dominus-widget.light [data-action].disabled:hover {
|
||||
color: #6c757d;
|
||||
}
|
||||
.tempus-dominus-widget.light .toolbar div:hover {
|
||||
background: #e9ecef;
|
||||
}
|
||||
.tempus-dominus-widget.light .date-container-days .dow {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.tempus-dominus-widget.light .date-container-days .cw {
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
.tempus-dominus-widget.light .date-container-decades div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.light .date-container-years div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.light .date-container-months div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.light .date-container-days div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.light .time-container-clock div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.light .time-container-hour div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.light .time-container-minute div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.light .time-container-second div:not(.no-highlight):hover {
|
||||
background: #e9ecef;
|
||||
}
|
||||
.tempus-dominus-widget.light .date-container-decades div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.light .date-container-years div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.light .date-container-months div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.light .date-container-days div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.light .date-container-days div.range-in:not(.no-highlight),
|
||||
.tempus-dominus-widget.light .date-container-days div.range-end:not(.no-highlight),
|
||||
.tempus-dominus-widget.light .date-container-days div.range-start:not(.no-highlight),
|
||||
.tempus-dominus-widget.light .time-container-clock div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.light .time-container-hour div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.light .time-container-minute div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.light .time-container-second div:not(.no-highlight).active {
|
||||
background-color: #0d6efd;
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.tempus-dominus-widget.light .date-container-decades div:not(.no-highlight).active.old, .tempus-dominus-widget.light .date-container-decades .date-container-days div.range-in:not(.no-highlight).old, .tempus-dominus-widget.light .date-container-days .date-container-decades div.range-in:not(.no-highlight).old, .tempus-dominus-widget.light .date-container-decades .date-container-days div.range-end:not(.no-highlight).old, .tempus-dominus-widget.light .date-container-days .date-container-decades div.range-end:not(.no-highlight).old, .tempus-dominus-widget.light .date-container-decades .date-container-days div.range-start:not(.no-highlight).old, .tempus-dominus-widget.light .date-container-days .date-container-decades div.range-start:not(.no-highlight).old, .tempus-dominus-widget.light .date-container-decades div:not(.no-highlight).active.new, .tempus-dominus-widget.light .date-container-decades .date-container-days div.range-in:not(.no-highlight).new, .tempus-dominus-widget.light .date-container-days .date-container-decades div.range-in:not(.no-highlight).new, .tempus-dominus-widget.light .date-container-decades .date-container-days div.range-end:not(.no-highlight).new, .tempus-dominus-widget.light .date-container-days .date-container-decades div.range-end:not(.no-highlight).new, .tempus-dominus-widget.light .date-container-decades .date-container-days div.range-start:not(.no-highlight).new, .tempus-dominus-widget.light .date-container-days .date-container-decades div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-years div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.light .date-container-years .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-years div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-years .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-years div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-years .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-years div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-years div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.light .date-container-years .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-years div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-years .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-years div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-years .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-years div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-months div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.light .date-container-months .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-months div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-months .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-months div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-months .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-months div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-months div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.light .date-container-months .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-months div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-months .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-months div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-months .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .date-container-months div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.light .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.light .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-clock div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.light .time-container-clock .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-clock div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-clock .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-clock div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-clock .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-clock div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-clock div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.light .time-container-clock .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-clock div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-clock .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-clock div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-clock .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-clock div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-hour div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.light .time-container-hour .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-hour div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-hour .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-hour div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-hour .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-hour div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-hour div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.light .time-container-hour .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-hour div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-hour .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-hour div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-hour .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-hour div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-minute div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.light .time-container-minute .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-minute div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-minute .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-minute div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-minute .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-minute div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-minute div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.light .time-container-minute .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-minute div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-minute .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-minute div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-minute .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-minute div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-second div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.light .time-container-second .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-second div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-second .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-second div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-second .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-second div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-second div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.light .time-container-second .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-second div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-second .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-second div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-second .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days .time-container-second div.range-start:not(.no-highlight).new {
|
||||
color: #fff;
|
||||
}
|
||||
.tempus-dominus-widget.light .date-container-decades div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.light .date-container-years div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.light .date-container-months div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.light .date-container-days div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.light .date-container-days div.range-in:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.light .date-container-days div.range-end:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.light .date-container-days div.range-start:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.light .time-container-clock div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.light .time-container-hour div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.light .time-container-minute div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.light .time-container-second div:not(.no-highlight).active.today:before {
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.tempus-dominus-widget.light .date-container-decades div:not(.no-highlight).old, .tempus-dominus-widget.light .date-container-decades div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-years div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-years div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-months div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-months div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .date-container-days div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .date-container-days div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-clock div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-clock div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-hour div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-hour div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-minute div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-minute div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.light .time-container-second div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.light .time-container-second div:not(.no-highlight).new {
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
.tempus-dominus-widget.light .date-container-decades div:not(.no-highlight).disabled, .tempus-dominus-widget.light .date-container-decades div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.light .date-container-years div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.light .date-container-years div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.light .date-container-months div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.light .date-container-months div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.light .date-container-days div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.light .date-container-days div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.light .time-container-clock div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.light .time-container-clock div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.light .time-container-hour div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.light .time-container-hour div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.light .time-container-minute div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.light .time-container-minute div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.light .time-container-second div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.light .time-container-second div:not(.no-highlight).disabled:hover {
|
||||
color: #6c757d;
|
||||
}
|
||||
.tempus-dominus-widget.light .date-container-decades div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.light .date-container-years div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.light .date-container-months div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.light .date-container-days div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.light .time-container-clock div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.light .time-container-hour div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.light .time-container-minute div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.light .time-container-second div:not(.no-highlight).today:before {
|
||||
border-bottom-color: #0d6efd;
|
||||
border-top-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.tempus-dominus-widget.light button {
|
||||
color: #fff;
|
||||
background-color: #0d6efd;
|
||||
border-color: #0d6efd;
|
||||
}
|
||||
.tempus-dominus-widget.dark {
|
||||
color: #e3e3e3;
|
||||
background-color: #1b1b1b;
|
||||
}
|
||||
.tempus-dominus-widget.dark [data-action].disabled, .tempus-dominus-widget.dark [data-action].disabled:hover {
|
||||
color: #6c757d;
|
||||
}
|
||||
.tempus-dominus-widget.dark .toolbar div:hover {
|
||||
background: rgb(35, 38, 39);
|
||||
}
|
||||
.tempus-dominus-widget.dark .date-container-days .dow {
|
||||
color: rgba(232, 230, 227, 0.5);
|
||||
}
|
||||
.tempus-dominus-widget.dark .date-container-days .range-in {
|
||||
background-color: #0071c7 !important;
|
||||
box-shadow: -5px 0 0 #0071c7, 5px 0 0 #0071c7;
|
||||
}
|
||||
.tempus-dominus-widget.dark .date-container-days .cw {
|
||||
color: rgba(232, 230, 227, 0.38);
|
||||
}
|
||||
.tempus-dominus-widget.dark .date-container-decades div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.dark .date-container-years div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.dark .date-container-months div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.dark .date-container-days div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.dark .time-container-clock div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.dark .time-container-hour div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.dark .time-container-minute div:not(.no-highlight):hover,
|
||||
.tempus-dominus-widget.dark .time-container-second div:not(.no-highlight):hover {
|
||||
background: rgb(35, 38, 39);
|
||||
}
|
||||
.tempus-dominus-widget.dark .date-container-decades div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.dark .date-container-years div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.dark .date-container-months div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.dark .date-container-days div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-in:not(.no-highlight),
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-end:not(.no-highlight),
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-start:not(.no-highlight),
|
||||
.tempus-dominus-widget.dark .time-container-clock div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.dark .time-container-hour div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.dark .time-container-minute div:not(.no-highlight).active,
|
||||
.tempus-dominus-widget.dark .time-container-second div:not(.no-highlight).active {
|
||||
background-color: #4db2ff;
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(232, 230, 227, 0.25);
|
||||
}
|
||||
.tempus-dominus-widget.dark .date-container-decades div:not(.no-highlight).active.old, .tempus-dominus-widget.dark .date-container-decades .date-container-days div.range-in:not(.no-highlight).old, .tempus-dominus-widget.dark .date-container-days .date-container-decades div.range-in:not(.no-highlight).old, .tempus-dominus-widget.dark .date-container-decades .date-container-days div.range-end:not(.no-highlight).old, .tempus-dominus-widget.dark .date-container-days .date-container-decades div.range-end:not(.no-highlight).old, .tempus-dominus-widget.dark .date-container-decades .date-container-days div.range-start:not(.no-highlight).old, .tempus-dominus-widget.dark .date-container-days .date-container-decades div.range-start:not(.no-highlight).old, .tempus-dominus-widget.dark .date-container-decades div:not(.no-highlight).active.new, .tempus-dominus-widget.dark .date-container-decades .date-container-days div.range-in:not(.no-highlight).new, .tempus-dominus-widget.dark .date-container-days .date-container-decades div.range-in:not(.no-highlight).new, .tempus-dominus-widget.dark .date-container-decades .date-container-days div.range-end:not(.no-highlight).new, .tempus-dominus-widget.dark .date-container-days .date-container-decades div.range-end:not(.no-highlight).new, .tempus-dominus-widget.dark .date-container-decades .date-container-days div.range-start:not(.no-highlight).new, .tempus-dominus-widget.dark .date-container-days .date-container-decades div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-years div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.dark .date-container-years .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-years div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-years .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-years div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-years .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-years div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-years div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.dark .date-container-years .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-years div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-years .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-years div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-years .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-years div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-months div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.dark .date-container-months .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-months div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-months .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-months div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-months .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-months div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-months div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.dark .date-container-months .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-months div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-months .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-months div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-months .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .date-container-months div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-clock div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.dark .time-container-clock .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-clock div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-clock .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-clock div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-clock .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-clock div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-clock div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.dark .time-container-clock .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-clock div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-clock .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-clock div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-clock .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-clock div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-hour div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.dark .time-container-hour .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-hour div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-hour .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-hour div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-hour .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-hour div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-hour div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.dark .time-container-hour .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-hour div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-hour .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-hour div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-hour .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-hour div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-minute div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.dark .time-container-minute .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-minute div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-minute .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-minute div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-minute .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-minute div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-minute div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.dark .time-container-minute .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-minute div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-minute .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-minute div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-minute .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-minute div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-second div:not(.no-highlight).active.old,
|
||||
.tempus-dominus-widget.dark .time-container-second .date-container-days div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-second div.range-in:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-second .date-container-days div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-second div.range-end:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-second .date-container-days div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-second div.range-start:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-second div:not(.no-highlight).active.new,
|
||||
.tempus-dominus-widget.dark .time-container-second .date-container-days div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-second div.range-in:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-second .date-container-days div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-second div.range-end:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-second .date-container-days div.range-start:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days .time-container-second div.range-start:not(.no-highlight).new {
|
||||
color: #fff;
|
||||
}
|
||||
.tempus-dominus-widget.dark .date-container-decades div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.dark .date-container-years div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.dark .date-container-months div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.dark .date-container-days div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-in:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-end:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.dark .date-container-days div.range-start:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.dark .time-container-clock div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.dark .time-container-hour div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.dark .time-container-minute div:not(.no-highlight).active.today:before,
|
||||
.tempus-dominus-widget.dark .time-container-second div:not(.no-highlight).active.today:before {
|
||||
border-bottom-color: #1b1b1b;
|
||||
}
|
||||
.tempus-dominus-widget.dark .date-container-decades div:not(.no-highlight).old, .tempus-dominus-widget.dark .date-container-decades div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-years div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-years div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-months div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-months div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .date-container-days div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .date-container-days div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-clock div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-clock div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-hour div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-hour div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-minute div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-minute div:not(.no-highlight).new,
|
||||
.tempus-dominus-widget.dark .time-container-second div:not(.no-highlight).old,
|
||||
.tempus-dominus-widget.dark .time-container-second div:not(.no-highlight).new {
|
||||
color: rgba(232, 230, 227, 0.38);
|
||||
}
|
||||
.tempus-dominus-widget.dark .date-container-decades div:not(.no-highlight).disabled, .tempus-dominus-widget.dark .date-container-decades div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.dark .date-container-years div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.dark .date-container-years div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.dark .date-container-months div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.dark .date-container-months div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.dark .date-container-days div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.dark .date-container-days div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.dark .time-container-clock div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.dark .time-container-clock div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.dark .time-container-hour div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.dark .time-container-hour div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.dark .time-container-minute div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.dark .time-container-minute div:not(.no-highlight).disabled:hover,
|
||||
.tempus-dominus-widget.dark .time-container-second div:not(.no-highlight).disabled,
|
||||
.tempus-dominus-widget.dark .time-container-second div:not(.no-highlight).disabled:hover {
|
||||
color: #6c757d;
|
||||
}
|
||||
.tempus-dominus-widget.dark .date-container-decades div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.dark .date-container-years div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.dark .date-container-months div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.dark .date-container-days div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.dark .time-container-clock div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.dark .time-container-hour div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.dark .time-container-minute div:not(.no-highlight).today:before,
|
||||
.tempus-dominus-widget.dark .time-container-second div:not(.no-highlight).today:before {
|
||||
border-bottom-color: #4db2ff;
|
||||
border-top-color: rgba(232, 230, 227, 0.2);
|
||||
}
|
||||
.tempus-dominus-widget.dark button {
|
||||
color: #fff;
|
||||
background-color: #4db2ff;
|
||||
border-color: #4db2ff;
|
||||
}
|
||||
/*# sourceMappingURL=tempus-dominus.css.map */
|
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,213 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Filadelfia myndhvelfing</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="/assets/favicon.png">
|
||||
<style>
|
||||
|
||||
[hidden] { display: none !important; }
|
||||
|
||||
:root { --bg: #fff; --bg-component: #f3f7ff; --bg-component-half: #f3f7ff77; --bg-component-alt: #ffd99c; --color: #031131; --color-alt: #7a9ad3; --main: #18597d; --main-fg: #fff; --error: red; --error-bg: hsl(0, 75%, 80%); } /* 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', Helvetica, Arial, sans-serif; font-variation-settings: "slnt" 0; font-feature-settings: "case", "frac", "tnum", "ss02", "calt", "ccmp", "kern"; background: var(--bg); color: var(--color); display: flex; flex-direction: column; }
|
||||
|
||||
.italic { font-variation-settings: "slnt" 10deg; }
|
||||
|
||||
input, button, textarea, select { font: inherit; }
|
||||
|
||||
h1 { font-size: 1.88rem; }
|
||||
h2 { font-size: 1.66rem; }
|
||||
h3 { font-size: 1.44rem; }
|
||||
h4 { font-size: 1.22rem; }
|
||||
h5 { font-size: 1.0rem; }
|
||||
|
||||
a, a:visited, button { text-decoration: underline; border: none; padding: 0; margin: 0; font-weight: bold; cursor: pointer; color: var(--main); background: transparent; }
|
||||
h1 { margin-bottom: 1rem; }
|
||||
#main { flex: 2 1 auto; display: flex; flex-direction: column; }
|
||||
.page { flex: 2 1 auto; display: flex; flex-direction: column; }
|
||||
.modal { flex: 2 1 auto; display: flex; flex-direction: column; justify-content: center; align-items: center; }
|
||||
.modal h3 { text-align: center; margin-bottom: 1rem; }
|
||||
.error { color: var(--error); }
|
||||
.modal form { background: var(--bg-component); border-radius: 20px; width: 100%; max-width: 500px; margin: 2rem; padding: 1rem; display: flex; flex-direction: column; }
|
||||
.loading-spinner { display: inline-block; width: 80px; height: 80px; margin-top: 0.5rem; align-self: center; }
|
||||
.loading-spinner:after { content: " "; display: block; width: 64px; height: 64px; margin: 8px; border-radius: 50%; border: 6px solid var(--main); border-color: var(--main) transparent var(--main) transparent; animation: loading-spinner 1.2s linear infinite; }
|
||||
@keyframes loading-spinner { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
||||
|
||||
/* Common components */
|
||||
|
||||
.row { display: flex; }
|
||||
.column { display: flex; flex-direction: column; }
|
||||
.filler { flex-grow: 2; }
|
||||
|
||||
input[type=text],
|
||||
input[type=password],
|
||||
input[type=datetime] { border: 1px solid var(--main); background: #fff; color: var(--color); border-radius: 0; padding: 0.25rem; line-height: 1rem; outline: none; width: 100%; }
|
||||
|
||||
input[type=text]:disabled,
|
||||
input[type=password]:disabled,
|
||||
input[type=datetime]:disabled { background: var(--bg-component); border-color: var(--color-alt); color: var(--color-alt); }
|
||||
|
||||
.form-row input:disabled + button { border-color: var(--color-alt); }
|
||||
.form-row { display: flex; position: relative; flex-wrap: wrap; }
|
||||
.form-row input { flex: 2 1 auto; }
|
||||
.form-row > input { width: auto; }
|
||||
.form-row .form-column { display: flex; flex-direction: column; justify-content: space-around; }
|
||||
.form-row button { min-width: 30px; text-align: center; border: 1px solid var(--main); border-left: none; background: var(--bg-component); text-decoration: none; }
|
||||
.form-row.image-banner { background-size: cover; background-color: white; border: 2px dashed var(--main); aspect-ratio: 16 / 9; align-self: center; width: 100%; max-width: 360px; }
|
||||
.form-row .cover { position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
|
||||
|
||||
input[type=text]:focus,
|
||||
input[type=password]:focus,
|
||||
input[type=datetime]:focus { outline: 1px solid var(--main); }
|
||||
.button, input[type=submit] { background: var(--main); color:var(--main-fg); border-radius: 10px; padding: 0.25rem 1rem; border: none; margin: 1rem 0 2rem; align-self: center; cursor: pointer; text-decoration: none; }
|
||||
|
||||
.button.spinner, input[type=submit].spinner { height: 2rem; margin-top: 2rem; margin-bottom: 1.5rem; }
|
||||
.button-alert { background: var(--error-bg); color: var(--color); }
|
||||
|
||||
.loading-bar { border: 1px solid var(--main); background: var(--bg-component); }
|
||||
.loading-bar::after { height: 1rem; background: var(--main); min-width: 1px; content: ''; display: block; width: var(--progress); transition: width 3s; }
|
||||
|
||||
form p, label { font-size: 0.75rem; font-weight: 500; margin: 0.75rem 0 0.5rem 0; display: block; }
|
||||
form p.separator { color: var(--color-alt); margin-top: 1.5rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--color-alt); }
|
||||
|
||||
/* Nav */
|
||||
|
||||
#header { background: var(--bg-component); }
|
||||
|
||||
#header nav { display: flex; }
|
||||
#header nav,
|
||||
#header .nav { text-align: center; padding: 0.5rem 1rem 0.5rem 0; justify-content: flex-end; flex-wrap: wrap; }
|
||||
|
||||
#header nav a,
|
||||
#header nav button { margin-left: 1rem; }
|
||||
|
||||
#header h4 { flex: 2 1 auto; margin: -0.5rem; }
|
||||
|
||||
#header h4 a,
|
||||
#header h4 a:visited { background: url('/assets/logo_nobg.svg') left center no-repeat; background-size: auto calc(3rem - 4px); height: 3rem; display: inline-block; padding-left: 3rem; line-height: 3rem; }
|
||||
|
||||
#header .link { font-size: 1.0rem; line-height: 2rem; font-weight: normal; padding: 0 0.5rem; }
|
||||
#header .changelang { font-size: 1.2rem; }
|
||||
#header .logout,
|
||||
#header .upload { padding: 0.25rem 1.5rem; border-radius: 2rem; text-decoration: none; }
|
||||
|
||||
#header .logout { background: var(--bg-component-alt); color: var(--color); }
|
||||
#header .upload { background: var(--main); color: var(--main-fg); }
|
||||
#header .nav { overflow-x: hidden; padding: 0.75rem 1rem 0.25rem; min-height: 4rem; align-items: flex-start; border-bottom: 1px solid #0001; }
|
||||
#header .nav:hover { overflow-x: auto; }
|
||||
#header .nav .inner { padding-top: 0.5rem; }
|
||||
#header .nav a { margin: 0 0.25rem; padding: 0.25rem 1.5rem; border-radius: 3rem; }
|
||||
#header .nav a.empty { opacity: 0.5; }
|
||||
#header .nav a.active { background: var(--bg-component-alt); color: var(--color); text-decoration: none; }
|
||||
#header .error { background: var(--error-bg); color: var(--color); font-size: 0.8rem; text-align: center; padding: 0.25rem; cursor: pointer; }
|
||||
|
||||
/* Main */
|
||||
|
||||
.full-error { background: var(--error-bg); color: var(--color); font-size: 0.8rem; text-align: center; padding: 0.25rem; cursor: pointer; flex: 2 1 auto; display: flex; justify-content: center; align-items: center; }
|
||||
|
||||
footer { text-align: center; padding: 1rem; }
|
||||
|
||||
footer a { font-size: 0.8rem; }
|
||||
|
||||
/* login */
|
||||
/* upload */
|
||||
|
||||
.page-login,
|
||||
.page-upload { background-image: url('./assets/bg.avif'); background-repeat: no-repeat; background-position: center; background-size: cover; }
|
||||
.page-login .modal form,
|
||||
.page-upload .modal form { backdrop-filter: blur(10px); background: var(--bg-component-half); }
|
||||
|
||||
/* browse */
|
||||
|
||||
.gallery { margin: 1rem; display: flex; flex-direction: column; }
|
||||
.gallery-year { margin: 1rem; padding: 0 0 1rem; border-bottom: 1px solid var(--main); text-align: center; font-size: 2rem; }
|
||||
.gallery-month { margin: 0rem 1rem 1rem; font-size: 1.2rem; border-bottom: 1px solid #0003; }
|
||||
.gallery .group { display: flex; flex-wrap: wrap; }
|
||||
.gallery .group a { width: calc(50vw - 4rem); max-width: 320px; aspect-ratio: 16 / 9; display: flex; flex-direction: column; justify-content: flex-end; background: url('./assets/placeholder.avif') center no-repeat; background-size: cover; margin: 0 1rem 1rem; text-align: center; border: 1px solid var(--main); }
|
||||
|
||||
.gallery .group a span { align-self: stretch; text-align: center; background: #fffb; }
|
||||
|
||||
/* Player */
|
||||
|
||||
.player { background: black; text-align: center; margin-bottom: 1rem; }
|
||||
.player video { margin: 0 auto; width: 1280px; max-width: 100%; aspect-ratio: 16 / 9; }
|
||||
|
||||
/* article */
|
||||
|
||||
.article { width: 100%; max-width: 1280px; padding: 0.5rem; align-self: center; margin-bottom: 5rem; }
|
||||
.article .full-error { margin-top: 1rem; }
|
||||
.article-name { flex: 2 1 auto; margin-left: 1rem; }
|
||||
.article h1 { margin: 0; }
|
||||
.article h1,
|
||||
.article p { padding: 0 0.5rem 0.5rem; }
|
||||
|
||||
/* table */
|
||||
|
||||
.table { display: grid; column-gap: 0; row-gap: 0; grid-template-columns: minmax(150px, 1.33fr) minmax(150px, 2.33fr); }
|
||||
.table-row { display: contents; }
|
||||
.table-row:nth-child(odd) .table-item { background: #f8f6ff; }
|
||||
.table-item { padding: 0.5rem; }
|
||||
|
||||
/* holdbutton */
|
||||
.holdbutton {
|
||||
--hold-bg: var(--bg-component);
|
||||
--hold-color: var(--main);
|
||||
--hold-fill: var(--main);
|
||||
--hold-fill-fg: white;
|
||||
display: inline-block;
|
||||
background: var(--hold-bg);
|
||||
position: relative;
|
||||
padding: 0;
|
||||
}
|
||||
.holdbutton .inner {
|
||||
padding: 0.25rem 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
background: var(--hold-color);
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
.holdbutton.holdbutton-active {
|
||||
background: linear-gradient( var(--hold-fill) , var(--hold-fill)) var(--hold-bg) no-repeat 0 0;
|
||||
background-size: 0 100%;
|
||||
animation: stripes 2s linear 1 forwards;
|
||||
}
|
||||
.holdbutton.holdbutton-active div.inner {
|
||||
background: linear-gradient( var(--hold-fill-fg), var(--hold-fill-fg)) var(--hold-color) no-repeat 0 0;
|
||||
background-size: 0 100%;
|
||||
animation: stripes 2s linear 1 forwards;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
@keyframes stripes { to { background-size: 100% 100%; } }
|
||||
|
||||
@media (pointer:coarse) {
|
||||
#header .nav { overflow-x: scroll; }
|
||||
}
|
||||
@media screen and (max-width: 700px){
|
||||
.gallery, .gallery-year { margin: 0.25rem; }
|
||||
.gallery-month { margin: 0rem 0.25rem 0.25rem; }
|
||||
.gallery .group a { margin: 0 0.25rem 0.25rem; width: calc(50vw - 1rem); font-size: min(3vw, 1rem); }
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header"></div>
|
||||
<main id="main"></main>
|
||||
<script type="text/javascript" src="/assets/app.js?v=2"></script>
|
||||
</body>
|
||||
</html>
|
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 435 KiB |
|
@ -0,0 +1,144 @@
|
|||
const m = require('mithril')
|
||||
const util = require('./util')
|
||||
|
||||
let activeBox = null
|
||||
let boxIndex = 1
|
||||
|
||||
document.body.addEventListener('click', function() {
|
||||
activeBox = null
|
||||
m.redraw()
|
||||
})
|
||||
|
||||
const Combobox = {
|
||||
oninit: function(vnode) {
|
||||
this.filtered = []
|
||||
this.open = false
|
||||
this.input = null
|
||||
this.id = boxIndex++
|
||||
this.onbeforeupdate(vnode)
|
||||
this.focus = this.onFocus.bind(this, vnode)
|
||||
},
|
||||
|
||||
onbeforeupdate: function(vnode) {
|
||||
if (!vnode.attrs.value) {
|
||||
this.filtered = vnode.attrs.items || []
|
||||
return
|
||||
}
|
||||
|
||||
let val = vnode.attrs.value.toLocaleLowerCase()
|
||||
this.filtered = vnode.attrs.items.filter(item => {
|
||||
return item.toLocaleLowerCase().indexOf(val) >= 0
|
||||
})
|
||||
},
|
||||
|
||||
onInput: function(vnode, e) {
|
||||
this.smartOpen(vnode)
|
||||
if (vnode.attrs.oninput) {
|
||||
vnode.attrs.oninput(e)
|
||||
}
|
||||
},
|
||||
|
||||
onDone: function(vnode) {
|
||||
if (vnode.attrs.ondone) {
|
||||
vnode.attrs.ondone()
|
||||
}
|
||||
},
|
||||
|
||||
onFocus: function(vnode) {
|
||||
this.input.focus()
|
||||
},
|
||||
|
||||
selectText: function(vnode, text) {
|
||||
this.input.value = text
|
||||
this.onInput(vnode, { target: this.input })
|
||||
activeBox = null
|
||||
this.onDone(vnode)
|
||||
return false
|
||||
},
|
||||
|
||||
onKeyPress: function(vnode, e) {
|
||||
if (e.key === 'ArrowDown') {
|
||||
if (e.target.dataset.type === 'input' && e.target.nextElementSibling && e.target.nextElementSibling.childNodes) {
|
||||
e.target.nextElementSibling.childNodes[0].focus()
|
||||
} else if (e.target.dataset.type === 'item') {
|
||||
if (e.target.nextElementSibling) {
|
||||
e.target.nextElementSibling.focus()
|
||||
} else {
|
||||
this.input.focus()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (e.key === 'ArrowUp') {
|
||||
if (e.target.dataset.type === 'input' && e.target.nextElementSibling && e.target.nextElementSibling.lastChild) {
|
||||
e.target.nextElementSibling.scrollTop = e.target.nextElementSibling.scrollHeight
|
||||
e.target.nextElementSibling.lastChild.focus()
|
||||
} else if (e.target.dataset.type === 'item') {
|
||||
if (e.target.previousElementSibling) {
|
||||
e.target.previousElementSibling.focus()
|
||||
} else {
|
||||
this.input.focus()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (e.key === 'Enter') {
|
||||
let newVal = ''
|
||||
if (e.target.dataset.type === 'input' && this.filtered.length) {
|
||||
if (e.target.value && e.target.value !== this.filtered[0]) {
|
||||
return this.selectText(vnode, this.filtered[0])
|
||||
}
|
||||
this.onDone(vnode)
|
||||
return false
|
||||
}
|
||||
if (e.target.dataset.type === 'item') {
|
||||
return this.selectText(vnode, e.target.dataset.value)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
smartOpen: function(vnode) {
|
||||
if (this.input.value && this.input.value === this.filtered[0]) {
|
||||
activeBox = null
|
||||
} else {
|
||||
activeBox = this.id
|
||||
}
|
||||
m.redraw()
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
closeBox = false
|
||||
|
||||
return m('div.form-item.combobox', {
|
||||
class: vnode.attrs.class,
|
||||
onclick: util.cancelPropagation,
|
||||
}, [
|
||||
m('label', vnode.attrs.label),
|
||||
m('input', {
|
||||
'data-type': 'input',
|
||||
oncreate: (e) => { this.input = e.dom },
|
||||
onkeydown: (e) => this.onKeyPress(vnode, e),
|
||||
type: vnode.attrs.type || 'text',
|
||||
value: vnode.attrs.value,
|
||||
onfocus: () => this.smartOpen(vnode),
|
||||
placeholder: vnode.attrs.placeholder || '',
|
||||
oninput: this.onInput.bind(this, vnode),
|
||||
}),
|
||||
activeBox === this.id
|
||||
? m('div.combobox-list', [
|
||||
this.filtered.slice(0, 50).map(item => {
|
||||
return m('div.combobox-list-item', {
|
||||
'data-type': 'item',
|
||||
'data-value': item,
|
||||
onkeydown: (e) => this.onKeyPress(vnode, e),
|
||||
onclick: (e) => this.selectText(vnode, item),
|
||||
tabindex: '0',
|
||||
}, item)
|
||||
})
|
||||
])
|
||||
: null,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Combobox
|
|
@ -11,9 +11,8 @@ const Header = {
|
|||
|
||||
onbeforeupdate: function() {
|
||||
let currentPath = m.route.get()
|
||||
console.log(currentPath)
|
||||
|
||||
if (currentPath === '/') this.currentActive = 'home'
|
||||
if (!currentPath || currentPath === '/' || currentPath.startsWith('/#')) this.currentActive = 'home'
|
||||
else if (currentPath === '/login') this.currentActive = 'login'
|
||||
},
|
||||
|
||||
|
@ -22,6 +21,17 @@ const Header = {
|
|||
m.route.set('/')
|
||||
},
|
||||
|
||||
scrollToView: function(e, name) {
|
||||
if (this.currentActive === 'home') {
|
||||
var el = document.getElementById(name)
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth' })
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
view: function() {
|
||||
return [
|
||||
m(m.route.Link,
|
||||
|
@ -34,7 +44,11 @@ const Header = {
|
|||
m('div.links', [
|
||||
m(m.route.Link, { href: '/#Heim' }, 'Heim'),
|
||||
m(m.route.Link, { href: '/#GoodAdvice' }, 'Góð ráð'),
|
||||
//m(m.route.Link, { class: 'button-active', href: '/#Subscribe' }, 'Subscribe'),
|
||||
m(m.route.Link, {
|
||||
class: 'button-active',
|
||||
href: '/#subscribe',
|
||||
onclick: (e) => { this.scrollToView(e, 'subscribe') },
|
||||
}, 'Subscribe'),
|
||||
]),
|
||||
]
|
||||
},
|
||||
|
|
|
@ -1,8 +1,90 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const Combobox = require('./combobox')
|
||||
const Constants = require('./consts')
|
||||
|
||||
const Frontpage = {
|
||||
oninit: function(vnode) {
|
||||
this.error = ''
|
||||
this.showAddLocation = true
|
||||
this.form = {
|
||||
city: '',
|
||||
zip: '',
|
||||
street_name: '',
|
||||
locations: [
|
||||
// 'Hverfisgata, 101 - Reykjavík',
|
||||
],
|
||||
type: [ true, false, false, false, false ],
|
||||
size: [ true, false, false, false, false ],
|
||||
rooms: [ true, false, false, false, false ],
|
||||
}
|
||||
this.values = {
|
||||
type: [ 'Alveg sama', 'Einbýli', 'Fjölbýli', 'Rað/Parhús', 'Hæð/Íbuð' ],
|
||||
size: [ 'Alveg sama', '0 - 50fm', '50 - 80fm', '80 - 120fm', '120fm +'],
|
||||
rooms: [ 'Alveg sama', 'Stúdíó', '2 - 3 herb.', '3 - 4 herb.', '5 + herb.' ],
|
||||
}
|
||||
this.inputs = {
|
||||
zip: null,
|
||||
street: null,
|
||||
}
|
||||
this.cities = Object.keys(Constants.Locations)
|
||||
this.zips = Object.keys(Constants.Streets)
|
||||
this.streets = []
|
||||
},
|
||||
|
||||
onFormUpdate: function(vnode, key, index, event) {
|
||||
if (['city', 'zip', 'street_name'].includes(key)) {
|
||||
this.form[key] = event.target.value
|
||||
if (key === 'city') {
|
||||
this.zips = Constants.Locations[this.form.city] || []
|
||||
} else if (key === 'zip') {
|
||||
this.streets = Constants.Streets[this.form.zip] || []
|
||||
}
|
||||
} else if (key === 'type' || key === 'size' || key === 'rooms') {
|
||||
if (index > 0) {
|
||||
this.form[key][0] = false
|
||||
this.form[key][index] = true
|
||||
} else {
|
||||
for (let i = 1; i < this.form[key].length; i++) {
|
||||
this.form[key][i] = false
|
||||
}
|
||||
this.form[key][0] = true
|
||||
}
|
||||
}
|
||||
console.log(key, event.target.value)
|
||||
},
|
||||
|
||||
onLocationAdd: function(vnode, event) {
|
||||
if (!this.form.city) return false
|
||||
this.showAddLocation = false
|
||||
let entry = this.form.city
|
||||
if (this.form.zip) {
|
||||
entry = this.form.zip + ' - ' + this.form.city
|
||||
}
|
||||
if (this.form.street_name) {
|
||||
if (entry !== this.form.city) {
|
||||
entry = ', ' + entry
|
||||
} else {
|
||||
entry = ' - ' + entry
|
||||
}
|
||||
entry = this.form.street_name + entry
|
||||
}
|
||||
this.form.city = ''
|
||||
this.form.zip = ''
|
||||
this.form.street_name = ''
|
||||
this.form.locations.push(entry)
|
||||
this.cities = Object.keys(Constants.Locations)
|
||||
this.zips = Object.keys(Constants.Streets)
|
||||
this.streets = []
|
||||
return false
|
||||
},
|
||||
|
||||
removeLocationIndex: function(index) {
|
||||
this.form.locations.splice(index, 1)
|
||||
if (!this.form.locations.length) {
|
||||
this.showAddLocation = true
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
|
@ -16,53 +98,85 @@ const Frontpage = {
|
|||
m('div.filler'),
|
||||
m('div.house1'),
|
||||
]),
|
||||
m('div.form', [
|
||||
m('div.form#subscribe', [
|
||||
m('h5', 'What are you looking for?'),
|
||||
m('form.form-group', [
|
||||
m('div.form-item', [
|
||||
m('label', 'City*'),
|
||||
m('input', { type: 'text', placeholder: 'Reykjavík' }),
|
||||
]),
|
||||
m('div.form-item', [
|
||||
m('label', 'Postal code (optional)'),
|
||||
m('input', { type: 'text', placeholder: '000' }),
|
||||
]),
|
||||
m('div.form-item.form-fill', [
|
||||
m('label', 'Street name (optional)'),
|
||||
m('input', { type: 'text', placeholder: 'Enter your dream street adress' }),
|
||||
]),
|
||||
m('div.form-item.form-small.form-no-label', [
|
||||
m('input.button-flat.disabled', { type: 'submit', value: 'Add location' }),
|
||||
m('form.form-list-vertical', { hidden: !this.form.locations.length }, [
|
||||
m('div.form-item.no-margin', [
|
||||
m('label', 'Location'),
|
||||
]),
|
||||
this.form.locations.map((location, i) => {
|
||||
return m('div.form-item', [
|
||||
m('div.fake-input', [
|
||||
m('p', location),
|
||||
m('button.remove-item', { onclick: (e) => this.removeLocationIndex(i) })
|
||||
])
|
||||
])
|
||||
}),
|
||||
]),
|
||||
m('button', { hidden: true }, [
|
||||
m('i.ic-plus'),
|
||||
m('span', 'Add more locations')
|
||||
m('form.form-group', {
|
||||
hidden: !this.showAddLocation,
|
||||
onsubmit: (e) => this.onLocationAdd(e),
|
||||
}, [
|
||||
m(Combobox, {
|
||||
label: 'City*',
|
||||
items: this.cities,
|
||||
value: this.form.city,
|
||||
placeholder: 'Reykjavík',
|
||||
oninput: (e) => this.onFormUpdate(vnode, 'city', null, e),
|
||||
ondone: () => { this.inputs.zip.state.focus() },
|
||||
}),
|
||||
m(Combobox, {
|
||||
label: 'Postal code (optional)',
|
||||
items: this.zips,
|
||||
value: this.form.zip,
|
||||
placeholder: '000',
|
||||
oninput: (e) => this.onFormUpdate(vnode, 'zip', null, e),
|
||||
oncreate: (e) => { this.inputs.zip = e },
|
||||
ondone: () => { this.inputs.street.state.focus() },
|
||||
}),
|
||||
m(Combobox, {
|
||||
class: 'form-fill',
|
||||
label: 'Street name (optional)',
|
||||
items: this.streets,
|
||||
value: this.form.street_name,
|
||||
placeholder: 'Enter your dream street adress',
|
||||
oninput: (e) => this.onFormUpdate(vnode, 'street_name', null, e),
|
||||
oncreate: (e) => { this.inputs.street = e },
|
||||
ondone: () => { this.onLocationAdd(vnode) },
|
||||
}),
|
||||
m('div.form-item.form-small.form-no-label', [
|
||||
m('input', {
|
||||
class: this.form.city ? 'button-active' : 'button-outline',
|
||||
type: 'submit',
|
||||
value: 'Add location',
|
||||
}),
|
||||
]),
|
||||
]
|
||||
),
|
||||
m('div.form-group', { hidden: this.showAddLocation }, [
|
||||
m('div.form-item', [
|
||||
m('button.button-flat', {
|
||||
onclick: () => { this.showAddLocation = !this.showAddLocation }
|
||||
}, [
|
||||
m('i.ic-plus'),
|
||||
m('span', 'Add more locations')
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
m('div.form-group', [
|
||||
m('div.form-item', [
|
||||
m('label', 'Tegund'),
|
||||
m('div.form-list', [
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', 'Alveg sama'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', 'Einbýli'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', 'Fjölbýli'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', 'Rað/Parhús'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', 'Hæð/Íbuð'),
|
||||
]),
|
||||
this.values.type.map((type, i) => {
|
||||
return m('label.checkbox', [
|
||||
m('input', {
|
||||
type: 'checkbox',
|
||||
checked: this.form.type[i],
|
||||
oninput: (e) => this.onFormUpdate(vnode, 'type', i, e),
|
||||
}),
|
||||
m('div.button-checkbox', type),
|
||||
])
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
@ -70,30 +184,16 @@ const Frontpage = {
|
|||
m('div.form-item', [
|
||||
m('label', 'Size'),
|
||||
m('div.form-list', [
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox', checked: true, }),
|
||||
m('div.button-checkbox', 'Alveg sama'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', '0 - 50fm'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', '50 - 80fm'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', '80 - 120fm'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', '120fm +'),
|
||||
]),
|
||||
/*m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', 'velja svið'),
|
||||
]),*/
|
||||
this.values.size.map((size, i) => {
|
||||
return m('label.checkbox', [
|
||||
m('input', {
|
||||
type: 'checkbox',
|
||||
checked: this.form.size[i],
|
||||
oninput: (e) => this.onFormUpdate(vnode, 'size', i, e),
|
||||
}),
|
||||
m('div.button-checkbox', size),
|
||||
])
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
@ -101,34 +201,21 @@ const Frontpage = {
|
|||
m('div.form-item', [
|
||||
m('label', 'Rooms'),
|
||||
m('div.form-list', [
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox', checked: true, }),
|
||||
m('div.button-checkbox', 'Alveg sama'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', 'Stúdíó'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', '2 - 3 herb.'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', '3 - 4 herb.'),
|
||||
]),
|
||||
m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', '5 + herb.'),
|
||||
]),
|
||||
/*m('label.checkbox', [
|
||||
m('input', { type: 'checkbox' }),
|
||||
m('div.button-checkbox', 'velja svið'),
|
||||
]),*/
|
||||
this.values.rooms.map((rooms, i) => {
|
||||
return m('label.checkbox', [
|
||||
m('input', {
|
||||
type: 'checkbox',
|
||||
checked: this.form.rooms[i],
|
||||
oninput: (e) => this.onFormUpdate(vnode, 'rooms', i, e),
|
||||
}),
|
||||
m('div.button-checkbox', rooms),
|
||||
])
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
||||
|
||||
m('div.form-group', [
|
||||
m('div.form-small.form-no-label', [
|
||||
m('button.button-inactive', { disabled: true }, 'Continue')
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export function cancelPropagation(event) {
|
||||
event.stopPropagation()
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
101 Reykjavík Reykjavík (Miðborg) Þéttbýli Hagatorgi 1
|
||||
102 Reykjavík Reykjavík (Vatnsmýri og Skerjafjörður) Þéttbýli Hagatorgi 1
|
||||
103 Reykjavík Reykjavík (Háaleitis- og Bústaðahverfi) Þéttbýli Síðumúla 3-5, 108 Reykjavík
|
||||
104 Reykjavík Reykjavík (Laugardalur) Þéttbýli Síðumúla 3-5, 108 Reykjavík
|
||||
105 Reykjavík Reykjavík (Hlíðar) Þéttbýli Síðumúla 3-5, 108 Reykjavík
|
||||
107 Reykjavík Reykjavík (Vesturbær) Þéttbýli Hagatorgi 1
|
||||
108 Reykjavík Reykjavík (Múlar) Þéttbýli Síðumúla 3-5, 108 Reykjavík
|
||||
109 Reykjavík Reykjavík (Breiðholt) Þéttbýli Þönglabakka 4
|
||||
110 Reykjavík Reykjavík (Árbær) Þéttbýli Höfðabakka 9, 110 Reykjavík
|
||||
111 Reykjavík Reykjavík (Breiðholt) Þéttbýli Þönglabakka 4, 109 Reykjavík
|
||||
112 Reykjavík Reykjavík (Grafarvogur) Þéttbýli Höfðabakka 9, 110 Reykjavík
|
||||
113 Reykjavík Reykjavík (Grafarholt og Úlfarsárdalur) Þéttbýli Höfðabakka 9, 110 Reykjavík
|
||||
116 Reykjavík Reykjavík (Grundarhverfi) Þéttbýli Háholti 14, 270 Mosfellsbæ
|
||||
121 Reykjavík Reykjavík, pósthólf Pósthólf Pósthússtræti 5, 101 Reykjavík
|
||||
123 Reykjavík Reykjavík, pósthólf Pósthólf Síðumúla 3-5, 108 Reykjavík
|
||||
124 Reykjavík Reykjavík, pósthólf Pósthólf Síðumúla 3-5, 108 Reykjavík
|
||||
125 Reykjavík Reykjavík, pósthólf Pósthólf Síðumúla 3-5, 108 Reykjavík
|
||||
127 Reykjavík Reykjavík, pósthólf Pósthólf Eiðistorgi 15, 170 Seltjarnarnesi
|
||||
128 Reykjavík Reykjavík, pósthólf Pósthólf Síðumúli 3-5, 108 Reykjavík
|
||||
129 Reykjavík Reykjavík, pósthólf Pósthólf Þönglabakka 4, 109 Reykjavík
|
||||
130 Reykjavík Reykjavík, pósthólf Pósthólf Höfðabakka 9, 110 Reykjavík
|
||||
132 Reykjavík Reykjavík, pósthólf Pósthólf Hverafold 1-3, 112 Reykjavík
|
||||
150 Reykjavík Annað Opinberar stofnanir, eins og ráðuneyti og ríkisstofnanir.
|
||||
155 Reykjavík Annað Einkafyrirtæki, eins og viðskiptabankar.
|
||||
161 Reykjavík Reykjavík, dreifbýli (ofan Elliðavatns) Dreifbýli Höfðabakka 9, 110 Reykjavík
|
||||
162 Reykjavík - Dreifbýli Kjalarnes, dreifbýli Dreifbýli Höfðabakka 9, 110 Reykjavík
|
||||
170 Seltjarnarnesi Seltjarnarnes Þéttbýli Hagatorg 1
|
||||
172 Seltjarnarnesi Seltjarnarnes, pósthólf Pósthólf Hagatorg 1
|
||||
200 Kópavogi Kópavogur (Miðbær) Þéttbýli Dalvegi 18, 201 Kópavogi
|
||||
201 Kópavogi Kópavogur (Smárar, Lindir, Salir) Þéttbýli Dalvegi 18
|
||||
202 Kópavogi Kópavogur, pósthólf Pósthólf Dalvegi 18, 201 Kópavogi
|
||||
203 Kópavogi Kópavogur (Hvörf, Kórar) Þéttbýli Dalvegi 18, 201 Kópavogi
|
||||
206 Kópavogi Kópavogur, dreifbýli Dreifbýli Dalvegi 18
|
||||
210 Garðabæ Garðabær Þéttbýli Fjarðargötu 13-15, 220 Hafnarfirði
|
||||
212 Garðabæ Garðabær, pósthólf Pósthólf Fjarðargötu 13-15, 220 Hafnarfirði
|
||||
220 Hafnarfirði Hafnarfjörður (Miðbær) Þéttbýli Fjarðargötu 13-15
|
||||
221 Hafnarfirði Hafnarfjörður (Vellir) Þéttbýli Fjarðargötu 13-15, 220 Hafnarfirði
|
||||
222 Hafnarfirði Hafnarfjörður, pósthólf Pósthólf Fjarðargötu 13-15, 220 Hafnarfirði
|
||||
225 Garðabæ Garðabær (Álftanes) Þéttbýli Fjarðargötu 13-15, 220 Hafnarfirði
|
||||
270 Mosfellsbæ Mosfellsbær Þéttbýli Höfðabakka 9, 110 Reykjavík
|
||||
271 Mosfellsbæ Mosfellssveit, dreifbýli Dreifbýli Höfðabakka 9, 110 Reykjavík
|
||||
276 Mosfellsbæ Hvalfjörður og Kjós, dreifbýli Dreifbýli Höfðabakka 9, 110 Reykjavík
|
||||
190 Vogum Vogar Þéttbýli Hafnargötu 89, 230 Reykjanesbæ
|
||||
191 Vogum Vatnsleysuströnd, dreifbýli Dreifbýli Hafnargötu 89, 230 Reykjanesbæ
|
||||
230 Reykjanesbæ Reykjanesbær (Keflavík) Þéttbýli Hafnargötu 89, 230 Reykjanesbæ
|
||||
232 Reykjanesbæ Reykjanesbær, pósthólf Pósthólf Hafnargötu 89, 230 Reykjanesbæ
|
||||
233 Reykjanesbæ Reykjanesbær (Hafnir) Þéttbýli Hafnargötu 89, 230 Reykjanesbæ
|
||||
235 Reykjanesbæ Keflavíkurflugvöllur Þéttbýli Hafnargötu 89, 230 Reykjanesbæ
|
||||
240 Grindavík Grindavík Þéttbýli Víkurbraut 25
|
||||
241 Grindavík Grindavík, dreifbýli Dreifbýli Víkurbraut 25, 240 Grindavík
|
||||
245 Suðurnesjabæ Sandgerði Þéttbýli Suðurgötu 2-4
|
||||
246 Suðurnesjabæ Sandgerði, dreifbýli Dreifbýli Suðurgötu 2-4, 245 Sandgerði
|
||||
250 Suðurnesjabæ Garður Þéttbýli Garðbraut 69
|
||||
251 Suðurnesjabæ Garður, dreifbýli Dreifbýli Garðbraut 69, 250 Garði
|
||||
260 Reykjanesbæ Reykjanesbær (Njarðvík) Þéttbýli Hafnargötu 89, 230 Reykjanesbæ
|
||||
262 Reykjanesbæ Reykjanesbær (Ásbrú) Þéttbýli Hafnargötu 89, 230 Reykjanesbæ
|
||||
300 Akranesi Akranes Þéttbýli Smiðjuvöllum 30
|
||||
301 Akranesi Akranes, dreifbýli Dreifbýli Smiðjuvöllum 30, 300 Akranesi
|
||||
302 Akranesi Akranes, pósthólf Pósthólf Smiðjuvöllum 30, 300 Akranesi
|
||||
310 Borgarnesi Borgarnes Þéttbýli Borgarbraut 12
|
||||
311 Borgarnesi Borgarnes, dreifbýli Dreifbýli Borgarbraut 12, 310 Borgarnesi
|
||||
320 Reykholti í Borgarfirði Reykholt í Borgarfirði, dreifbýli Dreifbýli Borgarbraut 12, 310 Borgarnesi
|
||||
340 Stykkishólmi Stykkishólmur Þéttbýli Aðalgötu 31
|
||||
341 Stykkishólmi Stykkishólmur, dreifbýli Dreifbýli Aðalgötu 31, 340 Stykkilshólmi
|
||||
342 Stykkishólmi Eyja og Miklaholtshreppur Dreifbýli Aðalgötu 31, 340 Stykkilshólmi
|
||||
345 Flatey á Breiðafirði Flatey á Breiðafirði Dreifbýli Aðalgötu 31, 340 Stykkishólmi
|
||||
350 Grundarfirði Grundarfjörður Þéttbýli Grundargötu 50
|
||||
351 Grundarfirði Grundarfjörður, dreifbýli Dreifbýli Grundargötu 50, 350 Grundarfirði
|
||||
355 Ólafsvík Ólafsvík Þéttbýli Bæjartúni 5
|
||||
356 Snæfellsbæ Snæfellsbær, dreifbýli Dreifbýli Bæjartúni 5, 355 Ólafsvík
|
||||
360 Hellissandi Hellissandur Þéttbýli Bæjartúni 5, 355 Ólafsvík
|
||||
370 Búðardal Búðardalur Þéttbýli Miðbraut 13
|
||||
371 Búðardal Búðardalur, dreifbýli Dreifbýli Miðbraut 13, 370 Búðardal
|
||||
380 Reykhólahreppi Reykhólar Þéttbýli Miðbraut 13, 370 Búðardal
|
||||
381 Reykhólahreppi Reykhólahreppur, dreifbýli Dreifbýli Miðbraut 13, 370 Búðardal
|
||||
400 Ísafirði Ísafjörður Þéttbýli Hafnarstræti 9-13
|
||||
401 Ísafirði Ísafjarðardjúp, dreifbýli (frá Ögri til Laugarholts) Dreifbýli Hafnarstræti 9-13, 400 Ísafirði
|
||||
410 Hnífsdal Hnífsdalur Þéttbýli Hafnarstræti 9-13, 400 Ísafirði
|
||||
415 Bolungarvík Bolungarvík Þéttbýli Aðalstræti 14
|
||||
416 Bolungarvík Bolungarvík, dreifbýli Dreifbýli Aðalstræti 14, 415 Bolungarvík
|
||||
420 Súðavík Súðavík Þéttbýli Grundarstræti 3-5
|
||||
421 Súðavík Súðavík, dreifbýli Dreifbýli Grundarstræti 3-5, Súðavík
|
||||
425 Flateyri Flateyri Þéttbýli Hafnarstræti 9-13, Ísafirði
|
||||
426 Flateyri Flateyri, dreifbýli Dreifbýli Hafnarstræti 9-13, Ísafirði
|
||||
430 Suðureyri Suðureyri Þéttbýli Hafnarstræti 9-13, Ísafirði
|
||||
431 Suðureyri Súgandafjörður, dreifbýli Dreifbýli Hafnarstræti 9-13, Ísafirði
|
||||
450 Patreksfirði Patreksfjörður Þéttbýli Bjarkargötu 4
|
||||
451 Patreksfirði Patreksfjörður, dreifbýli Dreifbýli Bjarkargötu 4, Patreksfirði
|
||||
460 Tálknafirði Tálknafjörður Þéttbýli Bjarkargötu 4, Patreksfirði
|
||||
461 Tálknafirði Tálknafjörður, dreifbýli Dreifbýli Bjarkargötu 4, Patreksfirði
|
||||
465 Bíldudal Bíldudalur Þéttbýli Bjarkargötu 4, Patreksfirði
|
||||
466 Bíldudal Bíldudalur, dreifbýli Dreifbýli Bjarkargötu 4, Patreksfirði
|
||||
470 Þingeyri Þingeyri Þéttbýli Hafnarstræti 9-13, Ísafirði
|
||||
471 Þingeyri Dýrafjörður, dreifbýli Dreifbýli Hafnarstræti 9-13, Ísafirði
|
||||
500 Stað Staður Dreifbýli Lækjargötu 2, 530 Hvammstanga
|
||||
510 Hólmavík Hólmavík Þéttbýli Hafnarbraut 19
|
||||
511 Hólmavík Hólmavík, dreifbýli Dreifbýli Hafnarbraut 19, 510 Hólmavík
|
||||
512 Hólmavík Ísafjarðardjúp, dreifbýli (nær Hólmavík) Dreifbýli Hafnarbraut 19, 510 Hólmavík
|
||||
520 Drangsnesi Drangsnes Þéttbýli Hafnarbraut 19, 510 Hólmavík
|
||||
524 Árneshreppi Árneshreppur Dreifbýli Hafnarbraut 19, 510 Hólmavík
|
||||
530 Hvammstanga Hvammstangi Þéttbýli Lækjargötu 2
|
||||
531 Hvammstanga Hvammstangi, dreifbýli Dreifbýli Lækjargötu 2, Hvammstanga
|
||||
540 Blönduósi Blönduós Þéttbýli Hnjúkabyggð 32
|
||||
541 Blönduósi Blönduós, dreifbýli Dreifbýli Hnjúkabyggð 32, Blönduósi
|
||||
545 Skagaströnd Skagaströnd Þéttbýli Höfða
|
||||
546 Skagaströnd Skagaströnd, dreifbýli Dreifbýli Hnjúkabyggð 32, Blönduósi
|
||||
550 Sauðárkróki Sauðárkrókur Þéttbýli Kirkjutorgi 5
|
||||
551 Sauðárkróki Sauðárkrókur, dreifbýli Dreifbýli Kirkjutorgi 5, Sauðárkróki
|
||||
560 Varmahlíð Varmahlíð Þéttbýli Kirkjutorgi 5, Sauðárkróki
|
||||
561 Varmahlíð Varmahlíð, dreifbýli Dreifbýli Kirkjutorgi 5, Sauðárkróki
|
||||
565 Hofsósi Hofsós Þéttbýli Kirkjutorgi 5, Sauðárkróki
|
||||
566 Hofsósi Hofsós, dreifbýli Dreifbýli Kirkjutorgi 5, Sauðárkróki
|
||||
570 Fljótum Fljót Dreifbýli Kirkjutorgi 5, Sauðárkróki
|
||||
580 Siglufirði Siglufjörður Þéttbýli Aðalgötu 24
|
||||
581 Siglufirði Siglufjörður, dreifbýli Dreifbýli Aðalgötu 24, Siglufirði
|
||||
600 Akureyri Akureyri Þéttbýli Strandgötu 3
|
||||
601 Akureyri Akureyri, dreifbýli Dreifbýli Strandgötu 3, 600 Akureyri
|
||||
602 Akureyri Akureyri, pósthólf Pósthólf Strandgötu 3, 600 Akureyri
|
||||
603 Akureyri Akureyri Þéttbýli Norðurtanga 3, 600 Akureyri
|
||||
604 Akureyri Akureyri, dreifbýli (Hörgársveit) Dreifbýli Norðurtanga 3
|
||||
605 Akureyri Akureyri, dreifbýli (Eyjafjarðarsveit) Dreifbýli Norðurtanga 3
|
||||
606 Akureyri Akureyri, dreifbýli (Svalbarðsströnd) Dreifbýli Norðurtanga 3
|
||||
607 Akureyri Akureyri, dreifbýli (Þingeyjarsveit) Dreifbýli Norðurtanga 3
|
||||
610 Grenivík Grenivík Þéttbýli Túngötu 3
|
||||
611 Grímsey Grímsey Þéttbýli Vallargata 9
|
||||
616 Grenivík Grenivík, dreifbýli Dreifbýli Túngötu 3, 610 Grenivík
|
||||
620 Dalvík Dalvík Þéttbýli Hafnarbraut 26
|
||||
621 Dalvík Dalvík, dreifbýli Dreifbýli Hafnarbraut 26, 620 Dalvík
|
||||
625 Ólafsfirði Ólafsfjörður Þéttbýli Aðalgötu 14
|
||||
626 Ólafsfirði Ólafsfjörður, dreifbýli Dreifbýli Aðalgötu 14, 625 Ólafsfirði
|
||||
630 Hrísey Hrísey Þéttbýli Norðurvegi 6-8
|
||||
640 Húsavík Húsavík Þéttbýli Garðarsbraut 70
|
||||
641 Húsavík Húsavík, dreifbýli Dreifbýli Garðarsbraut 70, 640 Húsavík
|
||||
645 Fosshóli Fosshóll, dreifbýli Dreifbýli Garðarsbraut 70, 640 Húsavík
|
||||
650 Laugum Laugar Þéttbýli Kjarna
|
||||
660 Mývatni Mývatn Dreifbýli Helluhrauni 3
|
||||
670 Kópaskeri Kópasker Þéttbýli Bakkagötu 2
|
||||
671 Kópaskeri Kópasker, dreifbýli Dreifbýli Bakkagötu 2, 670 Kópaskeri
|
||||
675 Raufarhöfn Raufarhöfn Þéttbýli Aðalbraut 19
|
||||
676 Raufarhöfn Raufarhöfn, dreifbýli Dreifbýli Aðalbraut 19, 675 Raufarhöfn
|
||||
680 Þórshöfn Þórshöfn Þéttbýli Fjarðarvegi 5
|
||||
681 Þórshöfn Þórshöfn, dreifbýli Dreifbýli Fjarðarvegi 5, 680 Þórshöfn
|
||||
685 Bakkafirði Bakkafjörður Þéttbýli Fjarðarvegi 5, 680 Þórshöfn
|
||||
686 Bakkafirði Bakkafjörður, dreifbýli Dreifbýli Fjarðarvegi 5, 680 Þórshöfn
|
||||
690 Vopnafirði Vopnafjörður Þéttbýli Kolbeinsgötu 10
|
||||
691 Vopnafirði Vopnafjörður, dreifbýli Dreifbýli Kolbeinsgötu 10, 690 Vopnafirði
|
||||
700 Egilsstöðum Egilsstaðir Þéttbýli Kaupvangi 6
|
||||
701 Egilsstöðum Egilsstaðir, dreifbýli Dreifbýli Kaupvangi 6, 700 Egilsstöðum
|
||||
710 Seyðisfirði Seyðisfjörður Þéttbýli Hafnargötu 4
|
||||
711 Seyðisfirði Seyðisfjörður, dreifbýli Dreifbýli Hafnargötu 6, 710 Seyðisfirði
|
||||
715 Mjóafirði Mjóifjörður, dreifbýli Dreifbýli Brekku
|
||||
720 Borgarfirði (eystri) Bakkagerði Þéttbýli Kaupvangi 6, 700 Egilsstöðum
|
||||
721 Borgarfirði (eystri) Borgarfjörður eystri Dreifbýli Kaupvangi 6, 700 Egilsstöðum
|
||||
730 Reyðarfirði Reyðarfjörður Þéttbýli Búðareyri 35
|
||||
731 Reyðarfirði Reyðarfjörður, dreifbýli Dreifbýli Búðareyri 35, 720 Reyðarfirði
|
||||
735 Eskifirði Eskifjörður Þéttbýli Strandgötu 55
|
||||
736 Eskifirði Eskifjörður, dreifbýli Dreifbýli Strandgötu 55, 735 Eskifirði
|
||||
740 Neskaupstað Neskaupstaður Þéttbýli Miðstræti 26
|
||||
741 Neskaupstað Neskaupstaður, dreifbýli Dreifbýli Miðstræti 26, 740 Neskaupstað
|
||||
750 Fáskrúðsfirði Fáskrúðsfjörður Þéttbýli Skólavegi 59
|
||||
751 Fáskrúðsfirði Fáskrúðsfjörður, dreifbýli Dreifbýli Skólavegi 59, 750, Fáskrúðsfirði
|
||||
755 Stöðvarfirði Stöðvarfjörður Þéttbýli Búðareyri 35, 720 Reiðarfirði
|
||||
756 Stöðvarfirði Stöðvarfjörður, dreifbýli Dreifbýli Búðareyri 35, 720 Reiðarfirði
|
||||
760 Breiðdalsvík Breiðdalsvík Þéttbýli Selnesi 38
|
||||
761 Breiðdalsvík Breiðdalsvík, dreifbýli Dreifbýli Selnesi 38, 760 Breiðdalsvík
|
||||
765 Djúpavogi Djúpivogur Þéttbýli Kambi 1
|
||||
766 Djúpavogi Djúpivogur, dreifbýli Dreifbýli Kambi 1, 765 Djúpavog
|
||||
780 Höfn í Hornafirði Höfn Þéttbýli Hafnarbraut 21
|
||||
781 Höfn í Hornafirði Höfn, dreifbýli Dreifbýli Hafnarbraut 21, 780 Höfn
|
||||
785 Öræfum Öræfi, dreifbýli Dreifbýli Hafnarbraut 21, 780 Höfn
|
||||
800 Selfossi Selfoss Þéttbýli Larsenstræti 1
|
||||
801 Selfossi Selfoss, dreifbyli (Árborg) Dreifbýli Larsenstræti 1, 800 Selfossi
|
||||
802 Selfossi Selfoss, pósthólf Pósthólf Larsenstræti 1, 800 Selfossi
|
||||
803 Selfossi Selfoss, dreifbýli (Flóahreppur) Dreifbýli Larsenstræti 1, 800 Selfossi
|
||||
804 Selfossi Selfoss, dreifbýli (Skeiða- og Gnúpverjahreppur) Dreifbýli Larsenstræti 1, 800 Selfossi
|
||||
805 Selfossi Selfoss, dreifbýli (Grímsnes- og Grafningshreppur) Dreifbýli Larsenstræti 1, 800 Selfossi
|
||||
806 Selfossi Selfoss (Bláskógabyggð) Dreifbýli Larsenstræti 1, 800 Selfossi
|
||||
810 Hveragerði Hveragerði Þéttbýli Sunnumörk 2-4
|
||||
815 Þorlákshöfn Þorlákshöfn Þéttbýli Hafnarberg 1
|
||||
816 Ölfusi Ölfus, dreifbýli Dreifbýli Larsenstræti 1, 800 Selfossi
|
||||
820 Eyrarbakka Eyrarbakki Þéttbýli Larsenstræti 1, 800 Selfossi
|
||||
825 Stokkseyri Stokkseyri Þéttbýli Larsenstræti 1, 800 Selfossi
|
||||
840 Laugarvatni Laugarvatn Þéttbýli Larsenstræti 1, 800 Selfossi
|
||||
845 Flúðum Flúðir Þéttbýli Larsenstræti 1, 800 Selfossi
|
||||
846 Flúðum Flúðir, dreifbýli Dreifbýli Larsenstræti 1, 800 Selfossi
|
||||
850 Hellu Hella Þéttbýli Þrúðvangi 10
|
||||
851 Hellu Hella, dreifbyli Dreifbýli Þrúðvangi 10, 850 Hella
|
||||
860 Hvolsvelli Hvolsvöllur Þéttbýli Austurvegi 2
|
||||
861 Hvolsvelli Hvolsvöllur, dreifbýli Dreifbýli Austurvegi 2, 860 Hvolsvelli
|
||||
870 Vík Vík Þéttbýli Austurvegi 2, 860 Hvolsvelli
|
||||
871 Vík Vík, dreifbýli Dreifbýli Austurvegi 2, 860 Hvolsvelli
|
||||
880 Kirkjubæjarklaustri Kirkjubæjarklaustur Þéttbýli Austurvegi 2, 860 Hvolsvelli
|
||||
881 Kirkjubæjarklaustri Kirkjubæjarklaustur, dreifbýli Dreifbýli Austurvegi 2, 860 Hvolsvelli
|
||||
900 Vestmannaeyjum Vestmannaeyjar Þéttbýli Vestmannabraut 22
|
||||
902 Vestmannaeyjum Vestmannaeyjar, pósthólf Pósthólf Vestmannabraut 22, 900 Vestmannaeyjar
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"scripts": {
|
||||
"build": "echo done;"
|
||||
},
|
||||
"dependencies": {
|
||||
"service-core": "^3.0.0-beta.17"
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@
|
|||
"dot": "^2.0.0-beta.1",
|
||||
"flaska": "^1.3.0",
|
||||
"formidable": "^1.2.6",
|
||||
"msnodesqlv8": "^2.4.7",
|
||||
"msnodesqlv8": "^2.7.0",
|
||||
"nconf-lite": "^2.0.0",
|
||||
"striptags": "^3.2.0"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 12.9412C24 19.5686 18.6274 24.9412 12 24.9412C5.37259 24.9412 0 19.5686 0 12.9412C0 6.31375 5.37259 0.941162 12 0.941162C18.6274 0.941162 24 6.31375 24 12.9412ZM12 6.51259C12.7101 6.51259 13.2857 7.08823 13.2857 7.79831V11.6554H17.1429C17.8529 11.6554 18.4286 12.2311 18.4286 12.9412C18.4286 13.6512 17.8529 14.2269 17.1429 14.2269H13.2857V18.084C13.2857 18.7941 12.7101 19.3697 12 19.3697C11.2899 19.3697 10.7143 18.7941 10.7143 18.084V14.2269H6.85714C6.14707 14.2269 5.57143 13.6512 5.57143 12.9412C5.57143 12.2311 6.14707 11.6554 6.85714 11.6554H10.7143V7.79831C10.7143 7.08823 11.2899 6.51259 12 6.51259Z" fill="#EF6C10"/>
|
||||
</svg>
|
After Width: | Height: | Size: 783 B |
|
@ -0,0 +1,3 @@
|
|||
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.5146 21.4558C15.8283 26.1421 8.23031 26.1421 3.54402 21.4558C-1.14227 16.7696 -1.14228 9.17158 3.54402 4.48528C8.2303 -0.201006 15.8283 -0.201006 20.5146 4.48528C25.2009 9.17158 25.2009 16.7696 20.5146 21.4558ZM16.575 8.42488C17.0771 8.92697 17.0771 9.74105 16.575 10.2432L13.8476 12.9706L16.575 15.698C17.0771 16.2001 17.0771 17.0142 16.575 17.5162C16.0729 18.0183 15.2588 18.0183 14.7567 17.5162L12.0293 14.7888L9.30189 17.5162C8.7998 18.0183 7.98571 18.0183 7.48361 17.5162C6.98151 17.0142 6.98153 16.2001 7.48361 15.698L10.211 12.9706L7.48361 10.2432C6.98151 9.74105 6.98151 8.92697 7.48361 8.42488C7.98571 7.92278 8.79979 7.92278 9.30189 8.42488L12.0293 11.1523L14.7567 8.42488C15.2588 7.92278 16.0729 7.92278 16.575 8.42488Z" fill="#EF6C10"/>
|
||||
</svg>
|
After Width: | Height: | Size: 905 B |
|
@ -58,6 +58,8 @@ body, h1, h2, h3, h4, p, figure, blockquote, dl, dd {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
[hidden] { display: none !important; }
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
text-rendering: optimizeSpeed;
|
||||
|
@ -142,7 +144,9 @@ header, main, footer {
|
|||
max-width: var(--content-max-width);
|
||||
}
|
||||
|
||||
|
||||
.button-flat,
|
||||
.button-outline,
|
||||
.button-inactive,
|
||||
.button-active,
|
||||
.button-white {
|
||||
|
@ -150,9 +154,18 @@ header, main, footer {
|
|||
padding: 0.9rem 2rem;
|
||||
display: inline-block;
|
||||
align-self: flex-start;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.button-flat {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: #909090;
|
||||
background: transparent;
|
||||
padding: 0.9rem 0;
|
||||
}
|
||||
|
||||
.button-outline {
|
||||
background: transparent;
|
||||
border: var(--form-border);
|
||||
color: var(--main-fg-light);
|
||||
|
@ -173,6 +186,14 @@ header, main, footer {
|
|||
color: var(--section-blue-bg);
|
||||
}
|
||||
|
||||
.button-flat i,
|
||||
.button-outline i,
|
||||
.button-inactive i,
|
||||
.button-active i,
|
||||
.button-white i {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
/* ---------------- header ---------------- */
|
||||
|
||||
header {
|
||||
|
@ -253,6 +274,13 @@ section.title .form {
|
|||
z-index: 2;
|
||||
}
|
||||
|
||||
section.title .remove-item {
|
||||
height: 2.5rem;
|
||||
width: 2.5rem;
|
||||
background: url('/assets/img/remove.svg') center no-repeat;
|
||||
background-size: 1.5rem auto;
|
||||
}
|
||||
|
||||
section.title .form h5 {
|
||||
color: var(--main-accent);
|
||||
margin: 2rem 0;
|
||||
|
@ -446,6 +474,10 @@ footer button {
|
|||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.form-item.no-margin {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.form-fill {
|
||||
flex: 2 1 60%;
|
||||
}
|
||||
|
@ -468,6 +500,11 @@ footer button {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.form-list-vertical {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
label.checkbox {
|
||||
display: block;
|
||||
position: relative;
|
||||
|
@ -505,6 +542,67 @@ label.checkbox input[type=checkbox]:checked ~ .button-checkbox {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.form-item .fake-input {
|
||||
border-radius: var(--form-radius);
|
||||
border: none;
|
||||
padding: 0.5rem 0.5rem;
|
||||
width: 100%;
|
||||
background: rgba(237, 101, 3, 0.1);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.form-item .fake-input p {
|
||||
padding: 0.5rem;
|
||||
line-height: 1.5rem;
|
||||
font-size: 0.8rem;
|
||||
flex: 2 1 auto;
|
||||
}
|
||||
|
||||
/* ---------------- icons ---------------- */
|
||||
|
||||
i {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
i.ic-plus {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
background: url('/assets/img/orange_circle_plus.svg') center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
/* ---------------- icons ---------------- */
|
||||
|
||||
.combobox {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.combobox-list {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
border-radius: var(--form-radius);
|
||||
border: var(--form-border);
|
||||
background: var(--main-bg);
|
||||
z-index: 5;
|
||||
max-height: 530px;
|
||||
overflow-y: scroll;
|
||||
box-shadow: 0px 3px 10px #0004;
|
||||
}
|
||||
|
||||
.combobox-list-item {
|
||||
padding: 0.9rem 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.combobox-list-item:hover,
|
||||
.combobox-list-item:active,
|
||||
.combobox-list-item:focus {
|
||||
background: var(--main-accent);
|
||||
color: var(--main-accent-fg);
|
||||
}
|
||||
|
||||
</style>
|
||||
<link id="headstyle" rel="Stylesheet" href="/assets/app.css?v=2" type="text/css" media="none" />
|
||||
</head>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
package-lock=false
|
|
@ -0,0 +1,20 @@
|
|||
import path from 'path'
|
||||
import Parent from '../base/serve.mjs'
|
||||
import fs from 'fs/promises'
|
||||
import config from '../base/config.mjs'
|
||||
|
||||
export default class ServeHandler extends Parent {
|
||||
loadTemplate(indexFile) {
|
||||
this.template = indexFile.toString()
|
||||
}
|
||||
|
||||
async serveIndex(ctx) {
|
||||
if (config.get('NODE_ENV') === 'development') {
|
||||
let indexFile = await fs.readFile(path.join(this.root, 'index.html'))
|
||||
this.loadTemplate(indexFile)
|
||||
}
|
||||
|
||||
ctx.body = this.template
|
||||
ctx.type = 'text/html; charset=utf-8'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import config from '../base/config.mjs'
|
||||
import Parent from '../base/server.mjs'
|
||||
import StaticRoutes from '../base/static_routes.mjs'
|
||||
import ServeHandler from './serve.mjs'
|
||||
|
||||
export default class Server extends Parent {
|
||||
init() {
|
||||
super.init()
|
||||
let localUtil = new this.core.sc.Util(import.meta.url)
|
||||
|
||||
delete this.flaskaOptions.appendHeaders['Content-Security-Policy']
|
||||
this.flaskaOptions.nonce = []
|
||||
|
||||
this.routes = {
|
||||
static: new StaticRoutes(),
|
||||
}
|
||||
this.routes.serve = new ServeHandler({
|
||||
root: localUtil.getPathFromRoot('../public'),
|
||||
version: this.core.app.running,
|
||||
frontend: config.get('frontend:url'),
|
||||
})
|
||||
}
|
||||
|
||||
runCreateDatabase() {
|
||||
return {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
var m = require('mithril')
|
||||
var Helper = require('./helper')
|
||||
|
||||
var Footer = {
|
||||
oninit: function(vnode) {
|
||||
vnode.state.scrollListenerWaiting = []
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m('div.outerbox', [
|
||||
m('div.innerbox', [
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/contact.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
m('aside', [
|
||||
m('h3', 'Hafðu samband'),
|
||||
m('p', `
|
||||
Sendu okkur tölvupóst og lýstu þínum þörfum. Við munum setja okkur
|
||||
inn í málin og finna lausnir. Við bjóðum persónulega og
|
||||
metnaðarfulla þjónustu fyrir alla okkar viðskiptavini.
|
||||
`),
|
||||
m('h5', 'nfp@nfp.is'),
|
||||
]),
|
||||
]),
|
||||
m('div.meta', [
|
||||
m('span', 'Nordic Freelance Partners ehf.'),
|
||||
m('span', 'nfp@nfp.is'),
|
||||
m('span', 'kt. 420623-0110'),
|
||||
m('span', [
|
||||
'Bakgrunnsmyndir frá ',
|
||||
m('a', { href: 'https://www.freepik.com/pikisuperstar' }, 'pikisuperstar úr freepik.com'),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
<a href="https://www.freepik.com/free-photos-vectors/background">Background vector created by pikisuperstar - www.freepik.com</a>
|
||||
*/
|
||||
|
||||
module.exports = Footer
|
|
@ -0,0 +1,138 @@
|
|||
article {
|
||||
&.solution {
|
||||
box-shadow: 0 5px 50px #0005;
|
||||
background: #e9e9e9;
|
||||
margin: 0 0 5vh;
|
||||
height: 800px;
|
||||
|
||||
h3 {
|
||||
font-size: 5vh;
|
||||
padding: 5vh 20px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
align-self: center;
|
||||
|
||||
.left, .right {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: 0px;
|
||||
border-bottom: 2px solid #29688c;
|
||||
}
|
||||
|
||||
.left {
|
||||
right: 100%;
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
.right {
|
||||
left: 100%;
|
||||
right: -9999px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.hosting {
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
&.programming {
|
||||
background: #29688c;
|
||||
background: linear-gradient(150deg, #020024 15%, #29688c 100%);
|
||||
color: white;
|
||||
position: relative;
|
||||
min-height: auto;
|
||||
height: 70vw;
|
||||
max-height: 80vh;
|
||||
min-height: 600px;
|
||||
|
||||
section {
|
||||
}
|
||||
|
||||
aside {
|
||||
align-self: flex-start;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
margin-top: 5vh;
|
||||
flex: 1 1 auto;
|
||||
width: 90%;
|
||||
max-width: 1440px;
|
||||
|
||||
.item {
|
||||
width: 50%;
|
||||
min-width: 500px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
align-self: flex-start;
|
||||
padding: 10px 0 40px;
|
||||
}
|
||||
|
||||
a {
|
||||
border-color: white;
|
||||
color: white;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.image {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 87%;
|
||||
height: 90%;
|
||||
max-width: none;
|
||||
background-position: right bottom;
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
|
||||
&.video {
|
||||
min-height: 70vh;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 810px) {
|
||||
article.solution h3 {
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
article.programming {
|
||||
max-height: none;
|
||||
min-height: auto;
|
||||
padding: 0 40px 300px;
|
||||
|
||||
section {
|
||||
}
|
||||
|
||||
aside {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
max-width: none;
|
||||
background-position: right bottom;
|
||||
background-size: contain;
|
||||
transform: translate(50%, 0);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
var m = require('mithril')
|
||||
var Helper = require('../helper')
|
||||
|
||||
var Front = {
|
||||
oninit: function(vnode) {
|
||||
Helper.init(vnode, 'frontpage', {
|
||||
ratio: 0.91,
|
||||
})
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m('header', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domContainer = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
})
|
||||
},
|
||||
}, m('div.container', [
|
||||
!vnode.state.isMobile ? m('video', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domVideo = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
preload: 'none',
|
||||
loop: true,
|
||||
muted: true,
|
||||
playsinline: true,
|
||||
}, [
|
||||
m('source', {
|
||||
src: vnode.state.videoUrl,
|
||||
type: 'video/mp4',
|
||||
}),
|
||||
m('source', {
|
||||
src: '/assets/cover/frontpage.webm',
|
||||
type: 'video/webm',
|
||||
}),
|
||||
]
|
||||
) : m('div', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domVideo = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
}),
|
||||
m('div',
|
||||
m('img', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domImg = subnode.dom
|
||||
Helper.lazyLoadImage(vnode, 'domImg', '/assets/cover/frontpage.jpg')
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
src: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAQCAwMDAgQDAwMEBAQEBQkGBQUFBQsICAYJDQsNDQ0LDAwOEBQRDg8TDwwMEhgSExUWFxcXDhEZGxkWGhQWFxb/2wBDAQQEBAUFBQoGBgoWDwwPFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhb/wgARCABaAKADASIAAhEBAxEB/8QAGgAAAwEBAQEAAAAAAAAAAAAAAgMEAQAFB//EABkBAQEBAQEBAAAAAAAAAAAAAAECAAMEBf/aAAwDAQACEAMQAAAB+fctH2/iXeVbCXGuni5OaBWbztuNz9PnjWrPE2zT5h18PkJrjOnsFG6uRzkootzKNxwotobs1ezjvBwuphfgk1rSNdy5uN01MW4+RUEUZZ9QZaL5pZlDkIulMruzVrU6b0VTBh0uKnox8JcbYpeKfDwtfKy4vf5xdOd8yNRnGSJ49ypbUzSsII6Bhr8/TCAlPu657eYHFXLcBobWoYjail8FNS6SpOJV0jHTz9DfJ6TPrriILonHqhG1CDx25jnYvaHWSX1znHGUFndU/wD/xAAhEAADAAICAwADAQAAAAAAAAAAAQIQEQMgEiExEyJBQv/aAAgBAQABBQKnomi/nMhoazsQkeLGsSxo8B8Xq51jleyH7q/XI8NDRo0SiEPQ9D0cSR4IekO1rmYxVi2POjQlhMqjZsm9H5CmbGNCoVlMQxCHljyiUeA4ND0KiWJbNaGySPZaxJQ8MRLFfqqLY2SShVpclmxUcfIO9n9lFIrrsbHRs4hsqjeUyWQzzHRWNZYx4iiqN9EJio2eRskZo0Uhjwhvuuks2JiKkpDRrDwukSeH63mTYmcbGx9dCgc4Qq0fl9N7ymbJJ+VWIKWYJOT4+iH04/v+f6/kjP/EABwRAAIDAQEBAQAAAAAAAAAAAAABAhARIBIhQf/aAAgBAwEBPwFISNNN4TEZ2zRSJU4jTVK1EURx+nkwaHE82rfGGDQhOpPBPTDKwkYfp//EABwRAAMBAAIDAAAAAAAAAAAAAAABERAgIQISMP/aAAgBAgEBPwG5ONHl+ME8TLxpTxfR7FExMuseJ6mUpcaKJUfRRMpRMuf/xAAUEAEAAAAAAAAAAAAAAAAAAABw/9oACAEBAAY/AnD/xAAbEAEBAQEBAQEBAAAAAAAAAAABABEQISAxQf/aAAgBAQABPyF1bwG+ndzIb1wzwS9mkYTJN0kMx4G8e9hhxlt58hqUyJxeJCspGCwlsc9TfWDDpb4ZQ0nZUVl5+NuXq8XqPnOw63gn78Nr2cUnxQ8QOKsCzn9hyIhxmxT1vDmdnijSeXBgEGzxhztY4PO8wEYcWrb8SAI0lrBMzpdYcGb9I3wXLYnqDhh5wGIJi23LwZO/KXlevJumQ2/srbbNtGDPDbEvJ6DzmSWWwxqSUgyRlov4cyw3jM9Mc1JtrDYt4X7j5Cfvj1+oj4L2P1f/2gAMAwEAAgADAAAAEJfK1cHvJurU1ZyigO6xZhbQ1tS7V0hPtb08C1iCrD9IB0dS0jX6/HNHdekp0f/EABgRAQEBAQEAAAAAAAAAAAAAAAEAERAh/9oACAEDAQE/ENSYhkBkFvHCSazlg8ktnWCyxB4AIFZElJZRJt6WVtAHG/HxLjbyR6+wsZScWY7bGSccljCDJLDLOw9ywcf/xAAbEQEBAQADAQEAAAAAAAAAAAABABEQITFRMP/aAAgBAgEBPxBxbs6syBlS2+ODdre4bLznJJjSwka4SeHE/kVp0tQwvpaStlEIT20vUUOpeStq3NCh0v/EAB4QAQEBAQEBAQEBAQEAAAAAAAEAESExEEFRYbEg/9oACAEBAAE/EOyheLb6MeWjwlPy0pSYKt4hOHkL8jDZMJvpI+MreSMJZMI5B3YeNib2yuxZ5dvPgu3hyINJjOFrvkqGxy2PJL+WnxI8u1wVkzdkHvznOy7xv8LP4CrI2fc2dPWF/YvjY/bvbL/Znbd5AdGBxsftdrhPV6p4xiSHKM625YW3ELWAb38gf5J/ZWItFEubFsMFi0tcS+wUzJB7Dfhw2V8E3ht9bNvs6hAtln8ujGXfZj9mDv0dHexEFy3sjJhT9gY2Y7sqZto2TWxZIvtt+yMMlH2Vw2A6sZm3uYeDKy3ESwmS9TIM2RPZF9+NllYfs2Zs/wBreZts9n6RxyReQw8kyTYXMhqy5FDfiO2iFsY2W2LG8t71iU2Nhd2Knkfy8sCkT4uSNQoQ7B1zyI4T3IYCBvti8ZXOxc7IPYlkHs/7+GmT4RXlo7A9R6Fp6WxdmGeIm77NElszIzjGNYHE+Xu8Is8Ihc/Pu2F33/xniPbyn/i/7wM8nnnz/9k=',
|
||||
})
|
||||
),
|
||||
m('nav', [
|
||||
m(m.route.Link, { class: 'title', href: '/' }, m('span', 'NFP')),
|
||||
m('div.filler'),
|
||||
m(m.route.Link, { href: '/hosting-solutions' }, [ 'Hýsingarlausnir', m('div') ]),
|
||||
m(m.route.Link, { href: '/programming-solutions' }, [ 'Forritunarlausnir', m('div') ]),
|
||||
m(m.route.Link, { href: '/video-solutions' }, [ 'Videólausnir', m('div') ]),
|
||||
]),
|
||||
m('div.content', [
|
||||
m('h1', 'Nýjir tímar kalla á nýjar lausnir'),
|
||||
m('h2', '...og við erum með þær'),
|
||||
]),
|
||||
])
|
||||
),
|
||||
m('main', [
|
||||
// Solutions
|
||||
m('article.solution', [
|
||||
m('h3', [
|
||||
'Hvað getum við gert fyrir þig?',
|
||||
m('div.left'),
|
||||
m('div.right'),
|
||||
]),
|
||||
m('section', [
|
||||
m('aside', [
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h5', 'Þínar þarfir'),
|
||||
m('p', `
|
||||
Þarfir fólks og fyrirtækja eru margvíslegar og krefjast
|
||||
oft sérstæðrar nálgunar. Starfslið okkar hefur víðtæka
|
||||
reynslu í þarfagreiningu og útfærslum á ýmsum sviðum
|
||||
hins stafræna nútíma.
|
||||
`),
|
||||
]),
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h5', 'Okkar lausnir'),
|
||||
m('p', `
|
||||
Hvort sem þú ert að leitast eftir hýsingu, hugbúnaðargerð,
|
||||
vefhönnun, streymi- eða upptökulausnum, þá erum við með
|
||||
þekkinguna og reynsluna. Við erum alltaf tilbúnir til
|
||||
leiks og elskum að taka að okkur krefjandi verkefni.
|
||||
`),
|
||||
]),
|
||||
]),
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/solutions.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
// Solutions
|
||||
m('article.hosting', [
|
||||
m('section', [
|
||||
m('aside', [
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h3', 'Hýsingarlausnir'),
|
||||
m('p', `
|
||||
Hvort heldur sem þig vantar vefhýsingu, sýndarvélar eða
|
||||
geymslu og meðhöndlun gagna þá getum við fundið lausn sem
|
||||
hentar þér.
|
||||
`),
|
||||
m(m.route.Link, { class: 'next', href: '/hosting-solutions' }, 'Lesa meira'),
|
||||
]),
|
||||
]),
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/hosting.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
// Programming
|
||||
m('article.programming', [
|
||||
m('section', [
|
||||
m('aside', [
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h3', 'Forritunarlausnir'),
|
||||
m('p', `
|
||||
Ef þig vantar sérlausn eða aðstoð með þín verkefni að þá
|
||||
getum við hjálpað þér. Við höfum yfir 20 ára reynslu í
|
||||
forritun og hugbúnaðargerð.
|
||||
`),
|
||||
m(m.route.Link, { class: 'next', href: '/programming-solutions' }, 'Lesa meira'),
|
||||
]),
|
||||
]),
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/programming.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
// Programming
|
||||
m('article.video', [
|
||||
m('section', [
|
||||
m('aside', [
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h3', 'Streymi- og upptökulausnir'),
|
||||
m('p', `
|
||||
Hvort sem er að ræða fundi eða ráðstefnur þá tökum við að
|
||||
okkur upptökur og streymingu á þeim. Við höfum margra ára
|
||||
reynslu og getum unnið í hvaða sal sem er.
|
||||
`),
|
||||
m(m.route.Link, { class: 'next', href: '/video-solutions' }, 'Lesa meira'),
|
||||
]),
|
||||
]),
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/video.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
<a href="https://www.freepik.com/free-photos-vectors/background">Background vector created by macrovector - www.freepik.com</a>
|
||||
*/
|
||||
|
||||
module.exports = Front
|
|
@ -0,0 +1,249 @@
|
|||
var Helper = {
|
||||
init: function(vnode, filename, options) {
|
||||
vnode.state.domImg = null
|
||||
vnode.state.domVideo = null
|
||||
vnode.state.domContainer = null
|
||||
vnode.state.isMobile = Helper.isMobile()
|
||||
|
||||
vnode.state.videoShow = false
|
||||
vnode.state.videoFilename = filename
|
||||
vnode.state.resizePauseTimeout = null
|
||||
vnode.state.resizeTimeout = null
|
||||
vnode.state.videoResizeThrottler = Helper.resizeThrottler.bind(vnode.state, vnode)
|
||||
vnode.state.scrollListener = null
|
||||
vnode.state.scrollListenerWaiting = []
|
||||
vnode.state.videoVisibilityHidden = ''
|
||||
vnode.state.videoVisibilityEvent = null
|
||||
vnode.state.videoVisibilityChanged = null
|
||||
vnode.state.videoResizeRatio = options && options.ratio || 1
|
||||
vnode.state.videoUrl = screen.width > 1280
|
||||
? '/assets/cover/' + filename + '_1080.mp4'
|
||||
: '/assets/cover/' + filename + '_720.mp4'
|
||||
},
|
||||
|
||||
isMobile: function() {
|
||||
var check = false;
|
||||
(function(a) {
|
||||
if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true
|
||||
})(navigator.userAgent || navigator.vendor || window.opera);
|
||||
return check
|
||||
},
|
||||
|
||||
clear: function(vnode) {
|
||||
vnode.state.scrollListenerWaiting.splice(0, vnode.state.scrollListenerWaiting.length)
|
||||
window.removeEventListener('resize', vnode.state.videoResizeThrottler)
|
||||
window.removeEventListener('scroll', vnode.state.scrollListener, false)
|
||||
window.removeEventListener(vnode.state.videoVisibilityEvent, vnode.state.videoVisibilityChanged)
|
||||
},
|
||||
|
||||
scrollListener: function(vnode) {
|
||||
if (!vnode.state.scrollListenerWaiting.length) return
|
||||
|
||||
var scrollTop = window.pageYOffset
|
||||
var height = window.innerHeight
|
||||
|
||||
for (var i = 0; i < vnode.state.scrollListenerWaiting.length; i++) {
|
||||
Helper.scrollItemIsVisible(vnode, vnode.state.scrollListenerWaiting[i], scrollTop, height)
|
||||
}
|
||||
},
|
||||
|
||||
scrollCheckEmpty: function(vnode) {
|
||||
if (vnode.state.scrollListenerWaiting.length) return
|
||||
|
||||
vnode.state.scrollListener = null
|
||||
window.removeEventListener('scroll', vnode.state.scrollListener, false)
|
||||
},
|
||||
|
||||
scrollAddItem: function(vnode, options) {
|
||||
var bound = options.dom.getBoundingClientRect()
|
||||
var scrollTop = window.pageYOffset
|
||||
|
||||
var item = {
|
||||
dom: options.dom,
|
||||
threshold: options.threshold || 100,
|
||||
top: Math.round(bound.top) + Math.round(scrollTop),
|
||||
bottom: Math.round(bound.bottom) + Math.round(scrollTop),
|
||||
className: ' ' + options.className,
|
||||
img: options.img || null,
|
||||
ticking: false,
|
||||
}
|
||||
|
||||
vnode.state.scrollListenerWaiting.push(item)
|
||||
|
||||
if (!vnode.state.scrollListener) {
|
||||
vnode.state.scrollListener = Helper.scrollListener.bind(vnode.state, vnode)
|
||||
window.addEventListener('scroll', vnode.state.scrollListener, false)
|
||||
}
|
||||
Helper.scrollItemIsVisible(vnode, item, window.pageYOffset, window.innerHeight)
|
||||
},
|
||||
|
||||
scrollResetBound: function(vnode) {
|
||||
var scrollTop = window.pageYOffset
|
||||
|
||||
vnode.state.scrollListenerWaiting.forEach(function (item) {
|
||||
var bound = item.dom.getBoundingClientRect()
|
||||
item.top = Math.round(bound.top) + Math.round(scrollTop)
|
||||
})
|
||||
},
|
||||
|
||||
scrollItemIsVisible: function(vnode, item, scrollTop, height) {
|
||||
if (item.ticking) return
|
||||
|
||||
if (item.top - item.threshold < scrollTop + height) {
|
||||
item.ticking = true
|
||||
|
||||
if (item.img) {
|
||||
Helper.lazyLoadImage(vnode, null, item.img, function() {
|
||||
item.dom.style.backgroundImage = 'url("' + item.img + '")'
|
||||
|
||||
requestAnimationFrame(function() {
|
||||
item.dom.className += item.className
|
||||
var index = vnode.state.scrollListenerWaiting.indexOf(item)
|
||||
|
||||
if (index >= 0) {
|
||||
vnode.state.scrollListenerWaiting.splice(index, 1)
|
||||
}
|
||||
Helper.scrollCheckEmpty(vnode)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
requestAnimationFrame(function() {
|
||||
item.dom.className += item.className
|
||||
var index = vnode.state.scrollListenerWaiting.indexOf(item)
|
||||
|
||||
if (index >= 0) {
|
||||
vnode.state.scrollListenerWaiting.splice(index, 1)
|
||||
}
|
||||
Helper.scrollCheckEmpty(vnode)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
checkCreated: function(vnode) {
|
||||
if (!vnode.state.domImg || !vnode.state.domVideo || !vnode.state.domContainer) return
|
||||
|
||||
vnode.state.videoShow = Boolean(vnode.state.domVideo.play)
|
||||
|
||||
window.addEventListener('resize', vnode.state.videoResizeThrottler, false)
|
||||
|
||||
Helper.resizeHandler(vnode)
|
||||
if (vnode.state.videoShow) {
|
||||
vnode.state.domVideo.play()
|
||||
|
||||
if (typeof document.hidden !== 'undefined') { // Opera 12.10 and Firefox 18 and later support
|
||||
vnode.state.videoVisibilityHidden = 'hidden'
|
||||
vnode.state.videoVisibilityEvent = 'visibilitychange'
|
||||
} else if (typeof document.msHidden !== 'undefined') {
|
||||
vnode.state.videoVisibilityHidden = 'msHidden'
|
||||
vnode.state.videoVisibilityEvent = 'msvisibilitychange'
|
||||
} else if (typeof document.webkitHidden !== 'undefined') {
|
||||
vnode.state.videoVisibilityHidden = 'webkitHidden'
|
||||
vnode.state.videoVisibilityEvent = 'webkitvisibilitychange'
|
||||
}
|
||||
|
||||
if (vnode.state.videoVisibilityHidden && vnode.state.videoVisibilityEvent) {
|
||||
vnode.state.videoVisibilityChanged = Helper.visibilityChanged.bind(this, vnode)
|
||||
|
||||
document.addEventListener(vnode.state.videoVisibilityEvent, vnode.state.videoVisibilityChanged, false)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
visibilityChanged: function(vnode) {
|
||||
if (document[vnode.state.videoVisibilityHidden]) {
|
||||
vnode.state.domVideo.pause()
|
||||
} else {
|
||||
vnode.state.domVideo.play()
|
||||
}
|
||||
},
|
||||
|
||||
resizeThrottler: function(vnode) {
|
||||
if (vnode.state.videoShow) {
|
||||
if (!vnode.state.resizePauseTimeout) {
|
||||
vnode.state.domVideo.pause()
|
||||
}
|
||||
clearTimeout(vnode.state.resizePauseTimeout)
|
||||
vnode.state.resizePauseTimeout = setTimeout(function () {
|
||||
vnode.state.resizePauseTimeout = null
|
||||
|
||||
requestAnimationFrame(function() {
|
||||
Helper.scrollResetBound(vnode)
|
||||
vnode.state.domVideo.play()
|
||||
})
|
||||
}, 250)
|
||||
}
|
||||
|
||||
if (!vnode.state.resizeTimeout) {
|
||||
vnode.state.resizeTimeout = setTimeout(function () {
|
||||
vnode.state.resizeTimeout = null
|
||||
Helper.resizeHandler(vnode)
|
||||
}, 66)
|
||||
}
|
||||
},
|
||||
|
||||
resizeHandler: function(vnode) {
|
||||
var height = Helper.getHeight(vnode)
|
||||
var width = document.documentElement.clientWidth
|
||||
|
||||
Helper.scaleContainer(vnode, height)
|
||||
Helper.scaleContainerContent(vnode, 'domImg', true, width, height)
|
||||
Helper.scaleContainerContent(vnode, 'domVideo', false, width, height)
|
||||
},
|
||||
|
||||
lazyLoadImage: function(vnode, name, url, cb) {
|
||||
if (!vnode.state[name] && !cb) return
|
||||
|
||||
var img = document.createElement('img')
|
||||
img.onload = function() {
|
||||
if (cb) {
|
||||
cb()
|
||||
} else {
|
||||
vnode.state[name].src = url
|
||||
}
|
||||
}
|
||||
img.src = url
|
||||
},
|
||||
|
||||
scaleContainer: function(vnode, height) {
|
||||
if (!vnode.state.domContainer) return
|
||||
vnode.state.domContainer.setAttribute('style', 'height: ' + height + 'px;')
|
||||
},
|
||||
|
||||
scaleContainerContent: function(vnode, name, forceHeight, width, height) {
|
||||
if (!vnode.state[name]) return
|
||||
|
||||
var videoRatio = 0.5625
|
||||
var windowRatio = height / width
|
||||
|
||||
if (windowRatio > videoRatio) {
|
||||
var videoWidth = height / videoRatio
|
||||
var marginFromLeft = -(videoWidth - width) / 2 + 'px'
|
||||
vnode.state[name].setAttribute('style',
|
||||
'margin-top: 0; margin-left: ' + marginFromLeft + ';' +
|
||||
'width: ' + videoWidth + 'px; height: ' + height + 'px')
|
||||
} else {
|
||||
if (forceHeight) {
|
||||
vnode.state[name].setAttribute('style',
|
||||
'width: ' + width + 'px; height: ' + height + 'px')
|
||||
} else {
|
||||
vnode.state[name].setAttribute('style',
|
||||
'width: ' + width + 'px;')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getHeight: function(vnode) {
|
||||
var height = document.documentElement.clientHeight + 5
|
||||
if (height < 485) {
|
||||
height = 485
|
||||
} else {
|
||||
height = height * vnode.state.videoResizeRatio
|
||||
}
|
||||
return height
|
||||
},
|
||||
}
|
||||
|
||||
window.helper = Helper
|
||||
|
||||
module.exports = Helper
|
|
@ -0,0 +1,202 @@
|
|||
header.hosting {
|
||||
h1 {
|
||||
font-size: 4.3vw;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 2.5vw;
|
||||
}
|
||||
}
|
||||
|
||||
article {
|
||||
&.hostintro {
|
||||
.image {
|
||||
transform: translate(-50%, 0px);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
|
||||
&.hoststorage {
|
||||
background: #29688c;
|
||||
background: linear-gradient(150deg, #020024 15%, #29688c 100%);
|
||||
color: white;
|
||||
position: relative;
|
||||
height: 80vh;
|
||||
min-height: 600px;
|
||||
|
||||
section {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
aside {
|
||||
align-items: center;
|
||||
max-width: none;
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
align-self: flex-start;
|
||||
padding: 10px 0 40px;
|
||||
}
|
||||
|
||||
.image {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 50%;
|
||||
height: 90%;
|
||||
max-width: none;
|
||||
background-position: left center;
|
||||
background-size: contain;
|
||||
transform: translate(-50%, 0px);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
|
||||
&.hostmanaged {
|
||||
position: relative;
|
||||
height: 80vh;
|
||||
min-height: 600px;
|
||||
|
||||
section {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
aside {
|
||||
align-items: center;
|
||||
max-width: none;
|
||||
|
||||
.item {
|
||||
padding-left: 20px;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
align-self: flex-start;
|
||||
padding: 10px 0 40px;
|
||||
}
|
||||
|
||||
.image {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 50%;
|
||||
height: 90%;
|
||||
max-width: none;
|
||||
background-position: left center;
|
||||
background-size: contain;
|
||||
transform: translate(-50%, 0px);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
header.hosting {
|
||||
h1 {
|
||||
font-size: 2.3em;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
article {
|
||||
&.hostintro {
|
||||
h3 {
|
||||
padding: 30px 0 30px;
|
||||
}
|
||||
|
||||
.image {
|
||||
transform: translate(0, 50%);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
|
||||
&.hoststorage {
|
||||
min-height: auto;
|
||||
padding: 0;
|
||||
|
||||
section {
|
||||
justify-content: flex-start;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
aside {
|
||||
order: 1;
|
||||
padding: 0 50px;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background-position: center;
|
||||
padding-top: 66%;
|
||||
position: relative;
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
|
||||
&.hostmanaged {
|
||||
min-height: auto;
|
||||
padding: 0;
|
||||
|
||||
section {
|
||||
justify-content: flex-start;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
aside {
|
||||
order: 1;
|
||||
padding: 0 50px;
|
||||
}
|
||||
|
||||
aside .item {
|
||||
padding-left: 0;
|
||||
}
|
||||
.image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 68%;
|
||||
height: auto;
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
article {
|
||||
&.hoststorage {
|
||||
aside {
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&.hostmanaged {
|
||||
aside {
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
var m = require('mithril')
|
||||
var Helper = require('../helper')
|
||||
|
||||
var Hosting = {
|
||||
oninit: function(vnode) {
|
||||
Helper.init(vnode, 'hosting')
|
||||
},
|
||||
|
||||
onremove: function(vnode) {
|
||||
Helper.clear(vnode)
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m('header.hosting', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domContainer = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
})
|
||||
},
|
||||
}, m('div.container', [
|
||||
!vnode.state.isMobile ? m('video', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domVideo = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
preload: 'none',
|
||||
loop: true,
|
||||
muted: true,
|
||||
playsinline: true,
|
||||
}, [
|
||||
m('source', {
|
||||
src: vnode.state.videoUrl,
|
||||
type: 'video/mp4',
|
||||
}),
|
||||
m('source', {
|
||||
src: '/assets/cover/hosting.webm',
|
||||
type: 'video/webm',
|
||||
}),
|
||||
]
|
||||
) : m('div', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domVideo = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
}),
|
||||
m('div',
|
||||
m('img', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domImg = subnode.dom
|
||||
Helper.lazyLoadImage(vnode, 'domImg', '/assets/cover/hosting.jpg')
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
src: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wgARCABaAKADASIAAhEBAxEB/8QAGgAAAwEBAQEAAAAAAAAAAAAAAQIDAAQFBv/EABcBAQEBAQAAAAAAAAAAAAAAAAEAAgP/2gAMAwEAAhADEAAAAfisu652ITFMJkZ5vRZXzRl0L0OdOmc8y1nmutJtHFctzBsVtFkoYmgpWfSeLxpsnSIyaFhWOXqRxm5g0mLo2HEGARqGAr0GiylNNKqobSZB6TNtE+e8am2cUJKJmBDbZexpmih2gBhU0pOqmTZRNptWibplyoihkIrIIVzPc+l25h1x0rz5qxyCzyfDkKJQA406jIQ5WIZSttiy5NTBcRQoT0lUlV0D/8QAHRAAAgMBAQEBAQAAAAAAAAAAAAEQESACMRJBMP/aAAgBAQABBQKy82fvPh0VLEIYznbF7z4OKGhwhjFNlyzn3mHhn6joYi4ssuV7zDw45GP+PPqhllw45GMZe16oeuRsYxZss5FD0hwxFFFFFFCFtilizRQ82WNjFLiyyyz6PoubLLwpYoQ5YtWXCliwoYpelLP/xAAaEQACAwEBAAAAAAAAAAAAAAABEQAQIDBA/9oACAEDAQE/AbVv0vTyjAC6Ii7f/8QAHREAAwEBAQADAQAAAAAAAAAAAAEREBICEyAhMf/aAAgBAgEBPwH8JnWIhBeof3KzoueVRqDYspS6nBu4oMXg+M4H5mcnJzkLDo6G1MXo6LlyE+lLv//EABQQAQAAAAAAAAAAAAAAAAAAAHD/2gAIAQEABj8CcP/EABsQAQEBAQEBAQEAAAAAAAAAAAEAERAhIDFB/9oACAEBAAE/ISepLObM3qExJ4HsPPjb8bxfNMngMft+PibD02ez8d7bL2/PT9cb8BZeS8hnxnn+347jjHwSy+Sj8tnHBZX9l53/AFwNpbx7Py2dtsstsuC4UsMsMxi9l5bK22WeOU8ifMRhGMOMvLZ9sskk4cs8HOBZtlixDGGGG0kSODlb1vHq89memG2Zq1bLPG2ylLLrOP1HG23hiy+R+Dx+o+z83//aAAwDAQACAAMAAAAQQxMUBpQhhWsZvsfymwhtADDvcnRC6d2P9ahJ5zPOwQHzNBJeJ9ae+/zQ637l/8QAGhEAAwEBAQEAAAAAAAAAAAAAAAERECEgMf/aAAgBAwEBPxDpfBuCFIfBKeWLolrqKyl2Y8bxUJrKirVOtHYNglWNbylkyeYTf//EABoRAQEBAQEBAQAAAAAAAAAAAAEAESEQMUH/2gAIAQIBAT8Q24/J58lfPAJO5zJxMBiBXT43BI8bjl1JkcQMsWnn4Jfpvt27YXkqbfEqTwJtygg1tl2wbG5sCd4yPDuzHwdY6nEngypUtrf/xAAbEAEBAQEBAQEBAAAAAAAAAAABABEhEDFBIP/aAAgBAQABPxBCQOwrGJ5ckX7ATl8JLXxU8jw5ehWcjL8nZWy8uU9N8I+2ZDF44+79w0uGcfviqOLOyM8PmfMPZz150mZ46od8KDYyTuMEdXG0L53D6pjcSly2vnz8eDu3l3ZeS74JNJyx50J147ep0Q43EAwn2W+WM7CbIycy5nBF2j+AkbcbhC2d8E/JNllk2TLS8keI9Byu3K4Slbvg3bM8V2fJuvgIIO363Hjp66JdMsHwCM5bCEHG4XSH9uXqQQ+TxL4CzhjC6TF5kp+w74eEPJjfg5zxPl4XazOy1jUlQ8ly2VmTPo+19JIXx4Pfz7PiPD6+h6//2Q==',
|
||||
})
|
||||
),
|
||||
m('nav', [
|
||||
m(m.route.Link, { class: 'title', href: '/' }, m('span', 'NFP')),
|
||||
m('div.filler'),
|
||||
m(m.route.Link, { class: 'active', href: '/hosting-solutions' }, [ 'Hýsingarlausnir', m('div') ]),
|
||||
m(m.route.Link, { href: '/programming-solutions' }, [ 'Forritunarlausnir', m('div') ]),
|
||||
m(m.route.Link, { href: '/video-solutions' }, [ 'Videólausnir', m('div') ]),
|
||||
]),
|
||||
m('div.content', [
|
||||
m('h1', 'Hýsingarlausnir sem þú getur treyst'),
|
||||
m('h2', 'SÝNDARVÉLAR, WORDPRESS, GEYMSLUHÝSING, OG HVAÐ EINA!'),
|
||||
]),
|
||||
])
|
||||
),
|
||||
m('main', [
|
||||
// Solutions
|
||||
m('article.hostintro', [
|
||||
m('section', [
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/hostintro.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
m('aside', [
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h3', 'Vefhýsing'),
|
||||
m('p', `
|
||||
Við getum boðið okkar viðskiptavinum upp á margskonar
|
||||
lausnir þegar kemur að hýsa þeirra vefi. Meðal þess sem
|
||||
við höfum reynslu af er að setja upp sýndarvélar sem þú
|
||||
getur haft fulla umsjá með og gert hvað svo sem þig lystir
|
||||
með en einnig sérhæfum okkur í að bjóða upp á
|
||||
alsherjarlausnir þar sem við sjáum um alla uppsetningu og
|
||||
umsjón svo að þú getir haft athyglina á öðrum þáttum
|
||||
reksturs þíns.
|
||||
`),
|
||||
m('p', `
|
||||
Ef þig vantar Wordpress/PHP uppsetningu eða hefur sérhæfða
|
||||
síðu og vilt nota docker eða node þá munar okkur ekki um
|
||||
það. Okkar kerfi geta séð um að mæta þínum þörfum hverjar
|
||||
sem þær eru.
|
||||
`),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
m('article.hoststorage', [
|
||||
m('section', [
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/hostfile.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
m('aside', [
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h3', 'Gagnageymslur og skölun myndefnis'),
|
||||
m('p', `
|
||||
Örugg geymsla gagna er sífellt mikilvægari þáttur fyrir
|
||||
fyrirtæki. NFP hefur boðið upp á gagnageymslu fyrir
|
||||
fyrirtæki til að geyma sín afrit eða vinnslugögn sem
|
||||
starfsmenn geta haft aðgang að og unnið með hvar sem
|
||||
þeir eru staddir í heiminum.
|
||||
`),
|
||||
m('p', `
|
||||
Við getum einnig séð um hvers kyns meðhöndlun myndgagna
|
||||
svo sem endurkóðun upptökuefnis til að lágmarka rýmisþörf
|
||||
með bestu mögulegu gæðum eða skölun á myndum fyrir
|
||||
vefsíðubirtingar. Lausnir okkar eru skilvirkar og
|
||||
afkastamiklar.
|
||||
`),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
m('article.hostmanaged', [
|
||||
m('section', [
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/hostmanaged.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
m('aside', [
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h3', 'Láttu okkur sjá um viðhaldið'),
|
||||
m('p', `
|
||||
Það getur verið kúnst að viðhalda sýndarvélum og
|
||||
heimasíðum og Það er ekki á allra hendi að framkvæma
|
||||
Wordpress uppfærslur eða prófanir á öryggisafritum.
|
||||
`),
|
||||
m('p', `
|
||||
Þú átt að geta einbeitt þér að þínu sérsviði. Þess vegna
|
||||
bjóðum við upp á að sjá um allt sem kemur að viðhaldi og
|
||||
gæðaeftirliti. Við fylgjumst vel með þróuninni, gerum
|
||||
reglulegar uppfærslur og tryggjum að allt virki sem
|
||||
skildi.
|
||||
`),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
<a href="https://www.freepik.com/free-photos-vectors/background">Background vector created by macrovector - www.freepik.com</a>
|
||||
*/
|
||||
|
||||
module.exports = Hosting
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* @license
|
||||
* NFP <https://nfp.is>
|
||||
* Copyright 2018 Jonatan Nilsson <http://jonatan.nilsson.is/>
|
||||
*
|
||||
* Available under WTFPL License (http://www.wtfpl.net/txt/copying/)
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
//Add debug components to window. Allows us to play with controls
|
||||
//in the console.
|
||||
window.components = {}
|
||||
|
||||
var m = require('mithril')
|
||||
|
||||
m.route.setOrig = m.route.set
|
||||
m.route.set = function(path, data, options){
|
||||
m.route.setOrig(path, data, options)
|
||||
window.scrollTo(0, 0)
|
||||
}
|
||||
|
||||
m.route.linkOrig = m.route.link
|
||||
m.route.link = function(vnode){
|
||||
m.route.linkOrig(vnode)
|
||||
window.scrollTo(0, 0)
|
||||
}
|
||||
|
||||
var Front = require('./front')
|
||||
var Hosting = require('./hosting')
|
||||
var Programming = require('./programming')
|
||||
var Streaming = require('./streaming')
|
||||
var Footer = require('./footer')
|
||||
|
||||
m.route.prefix = ''
|
||||
m.route(document.getElementById('container'), '/', {
|
||||
'/': Front,
|
||||
'/hosting-solutions': Hosting,
|
||||
'/programming-solutions': Programming,
|
||||
'/video-solutions': Streaming,
|
||||
})
|
||||
m.mount(document.getElementById('footer'), Footer)
|
|
@ -0,0 +1,205 @@
|
|||
var m = require('mithril')
|
||||
var Helper = require('../helper')
|
||||
|
||||
var Programming = {
|
||||
oninit: function(vnode) {
|
||||
Helper.init(vnode, 'programming')
|
||||
},
|
||||
|
||||
onremove: function(vnode) {
|
||||
Helper.clear(vnode)
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m('header.programming', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domContainer = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
})
|
||||
},
|
||||
}, m('div.container', [
|
||||
!vnode.state.isMobile ? m('video', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domVideo = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
preload: 'none',
|
||||
loop: true,
|
||||
muted: true,
|
||||
playsinline: true,
|
||||
}, [
|
||||
m('source', {
|
||||
src: vnode.state.videoUrl,
|
||||
type: 'video/mp4',
|
||||
}),
|
||||
m('source', {
|
||||
src: '/assets/cover/programming.webm',
|
||||
type: 'video/webm',
|
||||
}),
|
||||
]
|
||||
) : m('div', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domVideo = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
}),
|
||||
m('div',
|
||||
m('img', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domImg = subnode.dom
|
||||
Helper.lazyLoadImage(vnode, 'domImg', '/assets/cover/programming.jpg')
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
src: ' data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAA0JCgsKCA0LCgsODg0PEyAVExISEyccHhcgLikxMC4pLSwzOko+MzZGNywtQFdBRkxOUlNSMj5aYVpQYEpRUk//2wBDAQ4ODhMREyYVFSZPNS01T09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0//wgARCABaAKADASIAAhEBAxEB/8QAGQAAAwEBAQAAAAAAAAAAAAAAAQIDAAQF/8QAFwEBAQEBAAAAAAAAAAAAAAAAAQACA//aAAwDAQACEAMQAAAB8adF6cJioGIqprZ2RWdHICE0FoBe/PXXNpOqQleOO3YCmuZyGXA0F50gqyICGNYOsEgpObzztZUnnp1HDfPBhSYqaLzeHDFyuc0qNKnE0NUmozsqcPVhPfOw58NxHTXSNdTSOudMi00gpoqQaG2HY6qCra5846lnnFgMxUUrYVRAKzK7bNnKFtShsVWiUrkKKApqtefocvLoVxzC6G5CoGaXgKhBno4XT//EABsQAAMBAQEBAQAAAAAAAAAAAAABERAgMBIC/9oACAEBAAEFAnkITqlHixof5GvN8TaUfcITiE4ePKPaUomXwePxXVPo+hsvknkyD8aUvCFtGy+DKUpcQtflCEIQmXqEIQhMhCE2HzyvC9JHyP8AJCEJtKUvaPzr5fP/xAAaEQADAAMBAAAAAAAAAAAAAAAAAREQEiAC/9oACAEDAQE/AUUpS4oxU88PlC4eKI1EsQ1NSEIJcU2KXLKXEz6EylEQh//EABkRAAIDAQAAAAAAAAAAAAAAAAARASAwEP/aAAgBAgEBPwG8cnKcGPrGO6EIWUUkYz//xAAUEAEAAAAAAAAAAAAAAAAAAABw/9oACAEBAAY/AnD/xAAcEAEBAQEBAQADAAAAAAAAAAABABEQICEwMUH/2gAIAQEAAT8hWyeFkFkFudvqD7OPAKT4SyILISQWcBDnLXLZPMsmEJk2RwyDrZTwrbeWnKxeDxifdnHgemz5CXxlknN8DODJZPneuLS+Xzwb0M2fLLyGyl8t4pss9Jw+cZkQbZeB6AwNjm1mWX3asT8LZY4GZ4ZFI4PN4JLnnDhx8H8np4/rl8f/2gAMAwEAAgADAAAAEBr/AJK106M1iZzHPUQsF7qTyBHmplEJPpqBT2lWE8Kfxs8s7ywQsqGwhiTaN5D/xAAbEQEBAQEBAAMAAAAAAAAAAAABABEQITFRYf/aAAgBAwEBPxBZCiMNWHB+rCSxtljajZ2Gb4djEHoDAYSI1yR8zDUJAW23mKX92jAStWoXjPYIZJDhT1g2L//EABkRAQEBAQEBAAAAAAAAAAAAAAEAERAhIP/aAAgBAgEBPxBsLIJL2zhyBNt5GT8PwY5krKlcXPibbLPfU9nssWFhIW2y8IWWXi1av//EAB0QAQEBAQEBAAMBAAAAAAAAAAEAERAhMSBBUWH/2gAIAQEAAT8QSas1/wATi1suQt4mEid8MoBey14Z2LIttJx4Pq+YmBl/OXeDfqQZxObQtb25ARjYgpBBycQkrwfEHYJAJk8S96BWyMdEGC0t9ivEvDOHkJyzMXjIkhlm3rBJ+Q+yeRoxk9jgE3eXK8YHbUqaVIZaQaRC4sfk0q2TtlllggFjktKpiNkZacOIYJq28sssmWPyVatWrcu8R7l4NbTKkbIGxsySG8XrPM6SE6z5tbdtJ4vqDln8FDrhhxvLS+SH6hk7YsD15YXgWCHJhYIWZZvLaVIPkH8hP1A35CRiTJ1aJq5bJl5edl+EFnvOQn2/i//Z',
|
||||
})
|
||||
),
|
||||
m('nav', [
|
||||
m(m.route.Link, { class: 'title', href: '/' }, m('span', 'NFP')),
|
||||
m('div.filler'),
|
||||
m(m.route.Link, { href: '/hosting-solutions' }, [ 'Hýsingarlausnir', m('div') ]),
|
||||
m(m.route.Link, { class: 'active', href: '/programming-solutions' }, [ 'Forritunarlausnir', m('div') ]),
|
||||
m(m.route.Link, { href: '/video-solutions' }, [ 'Videólausnir', m('div') ]),
|
||||
]),
|
||||
m('div.content', [
|
||||
m('h1', 'Sérfræðingar í forritun'),
|
||||
m('h2', 'Meira en 20 ára reynsla!'),
|
||||
]),
|
||||
])
|
||||
),
|
||||
m('main', [
|
||||
// Solutions
|
||||
m('article.programmingintro', [
|
||||
m('section', [
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/programmingintro.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
m('aside', [
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h3', 'Vefsíðuforritun'),
|
||||
m('p', `
|
||||
Með margra ára reynslu í forritun og yfirgripsmikla
|
||||
þekkingu þegar kemur að vefsíðugerð, er ekkert sem stoppar
|
||||
okkur í að leysa krefjandi verkefni. Hvort heldur sem um
|
||||
er að ræða útfærslu á hönnun og innleiðing hennar í
|
||||
HTML/CSS eða uppsetningu greiðslugátta og áskriftaleiða
|
||||
hjá mismunandi aðilum að þá erum við sérfræðingar á því
|
||||
sviði.
|
||||
`),
|
||||
m('p', `
|
||||
Við getum unnið með hvaða forritunartungumál sem er.
|
||||
Hvaða tækni sem þú notar, node, python, C# eða hvað
|
||||
annað tungumál, þá látum við ekkert stoppa okkur.
|
||||
`),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
m('article.programmingstore', [
|
||||
m('section', [
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/programmingstore.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
m('aside', [
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h3', 'Sölukerfi og vefverslun'),
|
||||
m('p', `
|
||||
Við höfum hannað og þróað sölukerfi frá grunni í síðuformi.
|
||||
Það er byggt til að vera einfalt í notkun og henta fyrir
|
||||
lítil og meðalstór fyrirtæki. Sölukerfið býður upp á
|
||||
pantanakerfi, verkbókhald og birgðakerfi sem og
|
||||
vefverslunarframenda og tengingu við posa.
|
||||
`),
|
||||
m('p', `
|
||||
Við getum aðlagað vefverslunarframendanum til að passa
|
||||
fyrir hvert fyrirtæki og þeirra vöruframboði. Við vinnum
|
||||
náið með hverjum og einum viðskiptavini og skilum af okkur
|
||||
lausn sem uppfyllir þarfir þeirra.
|
||||
`),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
m('article.programmingcontracting', [
|
||||
m('section', [
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/programmingcontractor.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
m('aside', [
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h3', 'Verktakavinna'),
|
||||
m('p', `
|
||||
Ef það er tímapressa hjá þér, að þá getum við komið beint
|
||||
til ykkar og unnið verktakavinnu á ykkar vinnustað. Við
|
||||
getum unnið náið með ykkar fólki og aðlagast hvaða
|
||||
vinnureglum sem er.
|
||||
`),
|
||||
m('p', `
|
||||
Okkar fólki hefur verið vel tekið hvert sem við förum og
|
||||
við höfum ætíð kappkostað að skila frá okkur góðu verki.
|
||||
Hverjar svo sem sem vandamálin kunna að liggja að þá
|
||||
geturðu treyst því að við munum finna lausn á þeim
|
||||
saman og redda málunum.
|
||||
`),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
<a href="https://www.freepik.com/free-photos-vectors/background">Background vector created by macrovector - www.freepik.com</a>
|
||||
*/
|
||||
|
||||
module.exports = Programming
|
|
@ -0,0 +1,185 @@
|
|||
header.programming {
|
||||
text-shadow: 0 0 30px black;
|
||||
|
||||
nav a {
|
||||
background-color: #0003;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 0 80px black;
|
||||
text-shadow: 0 0 80px black;
|
||||
opacity: 1;
|
||||
color: #999;
|
||||
transition: opacity 0.5 color 0.5s;
|
||||
|
||||
&.title {
|
||||
color: white;
|
||||
opacity: 0.6;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 5.8vw;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 3.5vw;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
}
|
||||
}
|
||||
|
||||
article {
|
||||
&.programmingintro {
|
||||
.image {
|
||||
transform: translate(-50%, 0px);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
|
||||
&.programmingstore {
|
||||
background: #29688c;
|
||||
background: linear-gradient(30deg, #020024 15%, #29688c 100%);
|
||||
color: white;
|
||||
position: relative;
|
||||
height: 80vh;
|
||||
min-height: 600px;
|
||||
|
||||
section {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
aside {
|
||||
align-items: center;
|
||||
max-width: none;
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
align-self: flex-start;
|
||||
padding: 10px 0 40px;
|
||||
}
|
||||
|
||||
.image {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 5%;
|
||||
width: 50%;
|
||||
height: 90%;
|
||||
max-width: none;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
transform: translate(50%, 0px);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
|
||||
&.programmingcontracting {
|
||||
.image {
|
||||
transform: translate(-50%, 0px);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
header.programming {
|
||||
nav a {
|
||||
&.title {
|
||||
background-position: calc(50% - 60px) center;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 5.8vw;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 3.5vw;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
}
|
||||
}
|
||||
|
||||
article {
|
||||
&.programmingintro {
|
||||
.image {
|
||||
transform: translate(0, 50%);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
|
||||
h3 {
|
||||
padding: 30px 0 30px;
|
||||
}
|
||||
}
|
||||
|
||||
&.programmingstore {
|
||||
aside {
|
||||
order: 1;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
h3 {
|
||||
align-self: flex-start;
|
||||
padding: 10px 0 40px;
|
||||
}
|
||||
|
||||
.image {
|
||||
position: relative;
|
||||
order: 2;
|
||||
width: 100%;
|
||||
padding-top: 430px;
|
||||
height: auto;
|
||||
background-size: auto 100%;
|
||||
background-position: center -17px;
|
||||
transform: translate(0, 50%);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
|
||||
&.programmingcontracting {
|
||||
h3 {
|
||||
padding: 30px 0 30px;
|
||||
}
|
||||
.image {
|
||||
transform: translate(0, 50%);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
var m = require('mithril')
|
||||
var Helper = require('../helper')
|
||||
|
||||
var Streaming = {
|
||||
oninit: function(vnode) {
|
||||
Helper.init(vnode, 'streaming')
|
||||
},
|
||||
|
||||
onremove: function(vnode) {
|
||||
Helper.clear(vnode)
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m('header.streaming', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domContainer = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
})
|
||||
},
|
||||
}, m('div.container', [
|
||||
!vnode.state.isMobile ? m('video', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domVideo = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
preload: 'none',
|
||||
loop: true,
|
||||
muted: true,
|
||||
playsinline: true,
|
||||
}, [
|
||||
m('source', {
|
||||
src: vnode.state.videoUrl,
|
||||
type: 'video/mp4',
|
||||
}),
|
||||
m('source', {
|
||||
src: '/assets/cover/streaming.webm',
|
||||
type: 'video/webm',
|
||||
}),
|
||||
]
|
||||
) : m('div', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domVideo = subnode.dom
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
}),
|
||||
m('div',
|
||||
m('img', {
|
||||
oncreate: function(subnode) {
|
||||
vnode.state.domImg = subnode.dom
|
||||
Helper.lazyLoadImage(vnode, 'domImg', '/assets/cover/streaming.jpg')
|
||||
Helper.checkCreated(vnode)
|
||||
},
|
||||
src: ' data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCABaAKADASIAAhEBAxEB/8QAGQAAAwEBAQAAAAAAAAAAAAAAAgMEAQAF/8QAGAEBAQEBAQAAAAAAAAAAAAAAAQACAwT/2gAMAwEAAhADEAAAAfE3s3jSzmvr8mjfH2dBmVesLXdZnuE0twpgpDVKNAbAW5W/P4nMPzehWVC09IPS1khWbemw6WHJRm0pCyvWlO6kJw2PUpesRkneGqXwv0+hRAzT6Gw9Ztf5TC9cvIbh9DIOm9ciS9DoD0ULUOseBudjWsWdU9jHYl3NwcI61W0zFrinklRCC0o6fRRxgY0wZLnJe9Ec0XKgYstaltclqY0l6J5g0fLKuHejmrfW85GtFyNJoBwm9F6SKomHc3K7NysEhD//xAAfEAACAgIDAQEBAAAAAAAAAAAAARESAhATICEDMDH/2gAIAQEAAQUC2j55Fj5mWoIFpoggggga6pmOR834yCCNvcdYI3gjHKC5c5DkLnIXHmXLlx5lujxKmKPRJkFGLBlPHixJmSZVlWVZVkMkkWQmYoSQkjzSg8PDwhEIaRCKoeCKLpixZFzkOQuchynKcpynKP6i+pynIcnRC6SNkiyLFi27Fi3RC/FiZJPdC0ySdSIe5/BCENEDRGkZfmiCSxYnaxMl+eOsuqMf5n+H/8QAGxEAAgIDAQAAAAAAAAAAAAAAAREAEAIgITD/2gAIAQMBAT8Bo4di7Brm6UUGPdiPEwu1Si2Hg6GhMFmf/8QAHBEAAwEBAQADAAAAAAAAAAAAAAERECASAjAx/9oACAECAQE/Acov0eLEsXFGyi+R7Pe3GQhBdUv1IfKJqHx//8QAFBABAAAAAAAAAAAAAAAAAAAAcP/aAAgBAQAGPwJw/8QAHBABAQEBAQEBAQEAAAAAAAAAAQARECEgMWFB/9oACAEBAAE/Ie/qynV+Ie2dBH5yYx6fSfjG2eT2xDhkcJJMS2X2SOGc9rJGm8WYHGIDASZMiCfd5GQHAiliiSn4Mbo0qbbZZeU5NoOgJNN5E2J8CG08g9h6RSKtRmP7iMP6n+vjb86vxMb8AUs8TU2ziPoPgk2yx1cmMW2X6c8vDyzYnq8yw8bbbxOnheWnxWTyWz9HQjx46M2PbcsLLLLLJPg4/wA+n75fv7b/2gAMAwEAAgADAAAAEPwPCFT3BsQCB450JYuJIbsSOBWsH5KrM+9uoLxxCtTeMHdfes2QH7mWonlVfv/EABkRAQEBAQEBAAAAAAAAAAAAAAEAERAhMf/aAAgBAwEBPxBPZNl9QIPOZx+Q9X3oJrpJJa8GyRgYLGzLXQviCxy8dsky+rdny1lS9Yct4feBy220leWHP//EABoRAQEBAQADAAAAAAAAAAAAAAEAERAgITH/2gAIAQIBAT8QGLGSmJyXuzeIZ0S58nwoIQYeNgM64YI9d3jCMWkvQskgcbPRs4LZYX14f//EAB4QAQEBAAIDAQEBAAAAAAAAAAEAESExEEFRYSBx/9oACAEBAAE/EMxvVlxgZ5hIbDfGFIKyZbNmSfxdo4RzvDEcM3vKOsWZEbQQIJcIbDHFYzg8ZGWH8lkHNwG4PYOyCeWPpK9wc2Qe73N7y+u+2+6wdx+0EMVA9Sr1erknAzmu3Vwn23G7DOl1uxHOy3G37L9F+qJ9tfuFOe7n7h7y+781+OEcZAxo5yz+XB6lvq/ylfkP5HzS/RfGXyHgbJsZQ7mHuU9yPcbdzjuXYydzHuRiBE3uf7Gvdud3+vI3NNZrfAOdzFOrd3AO5X3KHub3uZ3GPci7lTuUe/JPm4pzPCwk4YmzJLhclhccsiwyV8keb0jd3Qsm2fOzOaxuUiRYtLOyDwcI+ETs+JWwx8xB2xjZ3J21lLslyWtoITiYhZNhZchlrsWbMmzDPEfx4Tv423x1wvdPcfw+H//Z',
|
||||
})
|
||||
),
|
||||
m('nav', [
|
||||
m(m.route.Link, { class: 'title', href: '/' }, m('span', 'NFP')),
|
||||
m('div.filler'),
|
||||
m(m.route.Link, { href: '/hosting-solutions' }, [ 'Hýsingarlausnir', m('div') ]),
|
||||
m(m.route.Link, { href: '/programming-solutions' }, [ 'Forritunarlausnir', m('div') ]),
|
||||
m(m.route.Link, { class: 'active', href: '/video-solutions' }, [ 'Videólausnir', m('div') ]),
|
||||
]),
|
||||
m('div.content', [
|
||||
m('h1', 'Streymi- og upptökulausnir'),
|
||||
m('h2', 'Fyrir ráðstefnur og fundi á tækniöld'),
|
||||
]),
|
||||
])
|
||||
),
|
||||
m('main', [
|
||||
// Solutions
|
||||
m('article.streamintro', [
|
||||
m('section', [
|
||||
m('div.image', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
img: '/assets/images/videoconference.svg',
|
||||
})
|
||||
},
|
||||
}),
|
||||
m('aside', [
|
||||
m('div.item', {
|
||||
oncreate: function(subnode) {
|
||||
Helper.scrollAddItem(vnode, {
|
||||
dom: subnode.dom,
|
||||
className: 'visible',
|
||||
threshold: -100,
|
||||
})
|
||||
},
|
||||
}, [
|
||||
m('h3', 'Ekki missa af neinu!'),
|
||||
m('p', `
|
||||
Aðstæður fólks eru æði ólíkar og ekki geta allir komist á
|
||||
alla viðburði, hvort sem er vegna fjarlægðar, fötlunar eða
|
||||
tímaleysis. Við bjóðum upp á upptöku og streymiþjónustu
|
||||
fyrir hvers kyns viðburði til að færa þá nær fólki í tíma
|
||||
og rúmi.
|
||||
`),
|
||||
m('p', `
|
||||
Innifalið í þjónustunni er streymigluggi, upptaka og
|
||||
úrvinnsla myndefnis með sundurgreiningu eftir ræðumanni
|
||||
eða umræðuefni, tilbúið til birtingar á netinu og/eða
|
||||
í varðveislu á stafrænu formi.
|
||||
`),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
<a href="https://www.freepik.com/free-photos-vectors/background">Background vector created by macrovector - www.freepik.com</a>
|
||||
*/
|
||||
|
||||
module.exports = Streaming
|
|
@ -0,0 +1,39 @@
|
|||
header.streaming {
|
||||
text-shadow: 0 0 40px black;
|
||||
|
||||
h1 {
|
||||
font-size: 5.7vw;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 3vw;
|
||||
}
|
||||
}
|
||||
|
||||
article.streamintro {
|
||||
.image {
|
||||
transform: translate(-50%, 0px);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
article {
|
||||
&.streamintro {
|
||||
h3 {
|
||||
padding: 30px 0 30px;
|
||||
}
|
||||
|
||||
.image {
|
||||
transform: translate(0, 50%);
|
||||
}
|
||||
|
||||
.visible.image {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
../base
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"scripts": {
|
||||
"build": "asbundle app/index.js public/assets/app.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"asbundle": "^2.6.1",
|
||||
"mithril": "^2.2.2",
|
||||
"service-core": "^3.0.0-beta.17"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import fs from 'fs'
|
||||
import { pathToFileURL } from 'url'
|
||||
import config from './base/config.mjs'
|
||||
|
||||
export function start(http, port, ctx) {
|
||||
config.sources[1].store = ctx.config
|
||||
|
||||
return import('./api/server.mjs')
|
||||
.then(function(module) {
|
||||
let server = new module.default(http, port, ctx)
|
||||
return server.run()
|
||||
})
|
||||
}
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
import('service-core').then(core => {
|
||||
const port = 4210
|
||||
|
||||
var core = new core.ServiceCore('nfp_is', import.meta.url, port, '')
|
||||
|
||||
let config = {
|
||||
frontend: {
|
||||
url: 'http://localhost:' + port
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
config = JSON.parse(fs.readFileSync('./config.json'))
|
||||
} catch {}
|
||||
|
||||
config.port = port
|
||||
|
||||
core.setConfig(config)
|
||||
core.init({ start }).then(function() {
|
||||
return core.run()
|
||||
})
|
||||
})
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"name": "nfp_is",
|
||||
"version": "1.0.3",
|
||||
"port": 4220,
|
||||
"description": "nfp.is website",
|
||||
"main": "index.mjs",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node index.mjs",
|
||||
"build": "asbundle app/index.js public/assets/app.js",
|
||||
"dev:build": "npm-watch build",
|
||||
"dev:server": "node index.mjs | bunyan",
|
||||
"dev": "npm-watch dev:server"
|
||||
},
|
||||
"watch": {
|
||||
"dev:server": {
|
||||
"patterns": [
|
||||
"api/*",
|
||||
"base/*",
|
||||
"../base/*"
|
||||
],
|
||||
"extensions": "js,mjs",
|
||||
"quiet": true,
|
||||
"inherit": true
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.nfp.is/nfp/nfp_sites.git"
|
||||
},
|
||||
"author": "Jonatan Nilsson",
|
||||
"license": "WTFPL",
|
||||
"bugs": {
|
||||
"url": "https://git.nfp.is/nfp/nfp_sites/issues"
|
||||
},
|
||||
"homepage": "https://git.nfp.is/nfp/nfp_sites",
|
||||
"dependencies": {
|
||||
"dot": "^2.0.0-beta.1",
|
||||
"flaska": "^1.3.4",
|
||||
"formidable": "^1.2.6",
|
||||
"nconf-lite": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"asbundle": "^2.6.1",
|
||||
"mithril": "^2.2.3",
|
||||
"service-core": "^3.0.0-beta.17"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 4.3 KiB |
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 524.9 117.7" fill="#3c435f"><path d="M90.7 44.4C90.2 19.8 70.1.1 45.4.1 20.3.1 0 20.4 0 45.5s20.3 45.4 45.4 45.4c6.7 0 13-1.4 18.7-4V65.8c-4.9 4.5-11.5 7.3-18.7 7.3-15.2 0-27.6-12.4-27.6-27.6s12.4-27.6 27.6-27.6c14.8 0 27 11.8 27.6 26.5v46.5h17.8l-.1-46.5z"></path><path d="M108.6 44.4v46.5h17.8V45.5c0-15.2 12.4-27.6 27.6-27.6V.1c-24.8 0-44.8 19.7-45.4 44.3"></path><path d="M253.6 44.4C253 19.8 232.9.1 208.2.1c-25.1 0-45.4 20.3-45.4 45.4s20.3 45.4 45.4 45.4c6.7 0 13-1.4 18.7-4V65.8c-4.9 4.5-11.5 7.3-18.7 7.3-15.2 0-27.6-12.4-27.6-27.6s12.4-27.6 27.6-27.6c14.8 0 27 11.8 27.6 26.5v46.5h17.8V44.4z"></path><path d="M316.7 0c-24.7 0-44.8 19.7-45.4 44.3v46.5h17.8V45.4c0-15.2 12.4-27.6 27.6-27.6 15.2 0 27.6 12.4 27.6 27.6v45.4h17.8V45.4c0-25.1-20.3-45.4-45.4-45.4"></path><path d="M407 0h-26.7v17.9h18.2v45.5c0 20.1-16.4 36.5-36.4 36.5h-17.8v17.8h17.8c29.9 0 54.3-24.4 54.3-54.3V0H407z"></path><path d="M524.9 44.3C524.3 19.7 504.2 0 479.5 0c-25.1 0-45.4 20.3-45.4 45.4s20.3 45.4 45.4 45.4c6.7 0 13-1.4 18.7-4V65.7c-4.9 4.5-11.5 7.3-18.7 7.3-15.2 0-27.6-12.4-27.6-27.6 0-15.2 12.4-27.6 27.6-27.6 14.8 0 27 11.8 27.6 26.5v46.5h17.8V44.3z"></path></svg>
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 135 KiB |
After Width: | Height: | Size: 137 KiB |
After Width: | Height: | Size: 2.8 MiB |
After Width: | Height: | Size: 1.4 KiB |