Compare commits
No commits in common. "a5c7e538021dafe34804a0bacdba70f575292759" and "5baf1823f4245bf6b165460b831d9b0970d3dfe7" have entirely different histories.
a5c7e53802
...
5baf1823f4
32 changed files with 33 additions and 132760 deletions
|
@ -8,14 +8,11 @@ export default class ArticleRoutes {
|
||||||
uploadMedia: uploadMedia,
|
uploadMedia: uploadMedia,
|
||||||
uploadFile: uploadFile,
|
uploadFile: uploadFile,
|
||||||
deleteFile: deleteFile,
|
deleteFile: deleteFile,
|
||||||
requireAuth: opts.requireAuth,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
register(server) {
|
register(server) {
|
||||||
if (!this.requireAuth) {
|
server.flaska.get('/api/articles/:path', this.getArticle.bind(this))
|
||||||
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', server.authenticate(), this.auth_getAllArticles.bind(this))
|
||||||
server.flaska.get('/api/auth/articles/:id', server.authenticate(), this.auth_getSingleArticle.bind(this))
|
server.flaska.get('/api/auth/articles/:id', server.authenticate(), this.auth_getSingleArticle.bind(this))
|
||||||
server.flaska.put('/api/auth/articles/:id', [
|
server.flaska.put('/api/auth/articles/:id', [
|
||||||
|
@ -114,10 +111,6 @@ 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)
|
await Promise.all(promises)
|
||||||
|
|
||||||
return this.private_getUpdateArticle(ctx, ctx.req.body, newBanner, newMedia)
|
return this.private_getUpdateArticle(ctx, ctx.req.body, newBanner, newMedia)
|
||||||
|
|
|
@ -98,10 +98,6 @@ export default class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
createJwt(body, secret) {
|
createJwt(body, secret) {
|
||||||
return Client.createJwt(body, secret)
|
|
||||||
}
|
|
||||||
|
|
||||||
static createJwt(body, secret) {
|
|
||||||
let header = {
|
let header = {
|
||||||
typ: 'JWT',
|
typ: 'JWT',
|
||||||
alg: 'HS256',
|
alg: 'HS256',
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
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'
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
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 VideoRoutes from './video/routes.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(),
|
|
||||||
auth: new AuthenticationRoutes(),
|
|
||||||
videos: new VideoRoutes(),
|
|
||||||
}
|
|
||||||
this.routes.serve = new ServeHandler({
|
|
||||||
root: localUtil.getPathFromRoot('../public'),
|
|
||||||
version: this.core.app.running,
|
|
||||||
frontend: config.get('frontend:url'),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
import config from '../../base/config.mjs'
|
|
||||||
import Client from '../../base/media/client.mjs'
|
|
||||||
import { deleteFile, uploadFile } from '../../base/media/upload.mjs'
|
|
||||||
import { parseVideos, parseVideo } from './util.mjs'
|
|
||||||
|
|
||||||
export default class VideoRoutes {
|
|
||||||
constructor(opts = {}) {
|
|
||||||
Object.assign(this, {
|
|
||||||
uploadFile: uploadFile,
|
|
||||||
deleteFile: deleteFile,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
register(server) {
|
|
||||||
server.flaska.get('/api/videos', server.authenticate(), this.getVideos.bind(this))
|
|
||||||
server.flaska.get('/api/videos/uploadToken', server.authenticate(), this.getUploadToken.bind(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** GET: /api/videos */
|
|
||||||
async getVideos(ctx) {
|
|
||||||
console.log(ctx.state.auth_token)
|
|
||||||
let res = await ctx.db.safeCallProc('filadelfia_archive.videos_auth_get_all', [ctx.state.auth_token])
|
|
||||||
|
|
||||||
ctx.body = {
|
|
||||||
videos: parseVideos(res.results[0]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getUploadToken(ctx) {
|
|
||||||
const media = config.get('media')
|
|
||||||
|
|
||||||
return {
|
|
||||||
token: Client.createJwt({ iss: media.iss }, media.secret),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
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)
|
|
||||||
return video
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
const Authentication = require('./authentication')
|
|
||||||
|
|
||||||
exports.sendRequest = function(options, isPagination) {
|
|
||||||
let token = Authentication.getToken()
|
|
||||||
let pagination = isPagination
|
|
||||||
|
|
||||||
if (token) {
|
|
||||||
options.headers = options.headers || {}
|
|
||||||
options.headers['Authorization'] = 'Bearer ' + token
|
|
||||||
}
|
|
||||||
|
|
||||||
options.extract = function(xhr) {
|
|
||||||
if (xhr.responseText && xhr.responseText.slice(0, 9) === '<!doctype') {
|
|
||||||
throw new Error('Expected JSON but got HTML (' + xhr.status + ': ' + this.url.split('?')[0] + ')')
|
|
||||||
}
|
|
||||||
let out = null
|
|
||||||
if (pagination && xhr.status < 300) {
|
|
||||||
let headers = {}
|
|
||||||
|
|
||||||
xhr.getAllResponseHeaders().split('\r\n').forEach(function(item) {
|
|
||||||
var splitted = item.split(': ')
|
|
||||||
headers[splitted[0]] = splitted[1]
|
|
||||||
})
|
|
||||||
|
|
||||||
out = {
|
|
||||||
headers: headers || {},
|
|
||||||
data: JSON.parse(xhr.responseText),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (xhr.responseText) {
|
|
||||||
out = JSON.parse(xhr.responseText)
|
|
||||||
} else {
|
|
||||||
out = {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (xhr.status >= 300) {
|
|
||||||
throw out
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.request(options)
|
|
||||||
.catch(function (error) {
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
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)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
Authentication.updateToken(localStorage.getItem(storageName))
|
|
||||||
|
|
||||||
window.Authentication = Authentication
|
|
||||||
|
|
||||||
module.exports = Authentication
|
|
|
@ -1,41 +0,0 @@
|
||||||
const m = require('mithril')
|
|
||||||
const videos = require('./videos')
|
|
||||||
const Authentication = require('./authentication')
|
|
||||||
|
|
||||||
const Menu = {
|
|
||||||
oninit: function(vnode) {
|
|
||||||
this.currentActive = 'home'
|
|
||||||
this.loading = false
|
|
||||||
this.onbeforeupdate()
|
|
||||||
},
|
|
||||||
|
|
||||||
onbeforeupdate: function() {
|
|
||||||
let currentPath = m.route.get()
|
|
||||||
|
|
||||||
/*if (currentPath === '/') this.currentActive = 'home'
|
|
||||||
else if (currentPath === '/login') this.currentActive = 'login'
|
|
||||||
else if (currentPath && currentPath.startsWith('/page')) this.currentActive = currentPath.slice(currentPath.lastIndexOf('/') + 1)*/
|
|
||||||
},
|
|
||||||
|
|
||||||
logOut: function() {
|
|
||||||
Authentication.clearToken()
|
|
||||||
m.route.set('/')
|
|
||||||
},
|
|
||||||
|
|
||||||
view: function() {
|
|
||||||
console.log(Authentication.currentUser)
|
|
||||||
return Authentication.currentUser
|
|
||||||
? [
|
|
||||||
m('nav', [
|
|
||||||
m('h4', m(m.route.Link, { href: '/browse' }, 'Filadelfia archival center')),
|
|
||||||
Authentication.currentUser.rank > 10
|
|
||||||
? m(m.route.Link, { class: 'upload', href: '/upload' }, 'Upload')
|
|
||||||
: null,
|
|
||||||
m('button.logout', { onclick: this.logOut }, 'Log out'),
|
|
||||||
])
|
|
||||||
]
|
|
||||||
: null
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Menu
|
|
|
@ -1,46 +0,0 @@
|
||||||
const m = require('mithril')
|
|
||||||
const Authentication = require('./authentication')
|
|
||||||
const Header = require('./header')
|
|
||||||
const Login = require('./page_login')
|
|
||||||
const Browse = require('./page_browse')
|
|
||||||
window.m = m
|
|
||||||
|
|
||||||
var fileref = document.createElement("link");
|
|
||||||
fileref.setAttribute("rel", "stylesheet");
|
|
||||||
fileref.setAttribute("type", "text/css");
|
|
||||||
fileref.setAttribute("href", '/assets/app.css?v=2');
|
|
||||||
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 = {
|
|
||||||
'/': Login,
|
|
||||||
'/browse': Browse,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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=';
|
|
|
@ -1,31 +0,0 @@
|
||||||
const m = require('mithril')
|
|
||||||
const Authentication = require('./authentication')
|
|
||||||
const videos = require('./videos')
|
|
||||||
|
|
||||||
const Browse = {
|
|
||||||
oninit: function(vnode) {
|
|
||||||
if (!videos.Tree.length) {
|
|
||||||
this.refreshTree()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
oncreate: function() {
|
|
||||||
if (Authentication.currentUser) return
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshTree: function(vnode) {
|
|
||||||
videos.refreshTree()
|
|
||||||
},
|
|
||||||
|
|
||||||
view: function(vnode) {
|
|
||||||
return [
|
|
||||||
videos.error
|
|
||||||
? m('div.full-error', { onclick: this.refreshTree.bind(this) }, [
|
|
||||||
videos.error, m('br'), 'Click here to try again'
|
|
||||||
])
|
|
||||||
: null,
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Browse
|
|
|
@ -1,91 +0,0 @@
|
||||||
const m = require('mithril')
|
|
||||||
const Authentication = require('./authentication')
|
|
||||||
const api = require('./api')
|
|
||||||
|
|
||||||
const Login = {
|
|
||||||
oninit: function(vnode) {
|
|
||||||
this.redirect = vnode.attrs.redirect || ''
|
|
||||||
if (Authentication.currentUser) return m.route.set('/browse')
|
|
||||||
|
|
||||||
this.error = ''
|
|
||||||
this.loading = false
|
|
||||||
this.username = ''
|
|
||||||
this.password = ''
|
|
||||||
},
|
|
||||||
|
|
||||||
oncreate: function() {
|
|
||||||
if (Authentication.currentUser) return
|
|
||||||
},
|
|
||||||
|
|
||||||
loginuser: function(vnode, e) {
|
|
||||||
e.preventDefault()
|
|
||||||
this.error = ''
|
|
||||||
|
|
||||||
if (!this.password) this.error = 'Password is missing'
|
|
||||||
if (!this.username) this.error = 'Email is missing'
|
|
||||||
|
|
||||||
if (this.error) return false
|
|
||||||
|
|
||||||
this.loading = true
|
|
||||||
|
|
||||||
api.sendRequest({
|
|
||||||
method: 'POST',
|
|
||||||
url: '/api/authentication/login',
|
|
||||||
body: {
|
|
||||||
email: this.username,
|
|
||||||
password: this.password,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.token) {
|
|
||||||
return Promise.reject(new Error('Server authentication down.'))
|
|
||||||
}
|
|
||||||
Authentication.updateToken(result.token)
|
|
||||||
m.route.set(this.redirect || '/browse')
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.error = 'Error while logging in! ' + error.message
|
|
||||||
vnode.state.password = ''
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.loading = false
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
|
|
||||||
view: function(vnode) {
|
|
||||||
return [
|
|
||||||
m('div.modal', [
|
|
||||||
m('form', {
|
|
||||||
onsubmit: this.loginuser.bind(this, vnode),
|
|
||||||
}, [
|
|
||||||
m('h3', 'Filadelfia archival center'),
|
|
||||||
this.error ? m('p.error', this.error) : null,
|
|
||||||
m('label', 'Email or name'),
|
|
||||||
m('input', {
|
|
||||||
type: 'text',
|
|
||||||
value: this.username,
|
|
||||||
oninput: (e) => { this.username = e.currentTarget.value },
|
|
||||||
}),
|
|
||||||
m('label', 'Password'),
|
|
||||||
m('input', {
|
|
||||||
type: 'password',
|
|
||||||
value: this.password,
|
|
||||||
oninput: (e) => { this.password = e.currentTarget.value },
|
|
||||||
}),
|
|
||||||
m('input.spinner', {
|
|
||||||
hidden: this.loading,
|
|
||||||
type: 'submit',
|
|
||||||
value: 'Log in',
|
|
||||||
}),
|
|
||||||
this.loading ? m('div.loading-spinner') : null,
|
|
||||||
]),
|
|
||||||
m('div.login--asuna.spritesheet'),
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Login
|
|
|
@ -1,91 +0,0 @@
|
||||||
const m = require('mithril')
|
|
||||||
const Authentication = require('./authentication')
|
|
||||||
const api = require('./api')
|
|
||||||
|
|
||||||
const Login = {
|
|
||||||
oninit: function(vnode) {
|
|
||||||
this.redirect = vnode.attrs.redirect || ''
|
|
||||||
if (!Authentication.currentUser) return m.route.set('/')
|
|
||||||
|
|
||||||
this.error = ''
|
|
||||||
this.loading = false
|
|
||||||
this.body = {
|
|
||||||
year: new Date().getFullYear(),
|
|
||||||
month: new Date().getMonth() + 1,
|
|
||||||
title: '',
|
|
||||||
file: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
uploadfile: function(vnode, e) {
|
|
||||||
e.preventDefault()
|
|
||||||
this.error = ''
|
|
||||||
|
|
||||||
if (!this.password) this.error = 'Password is missing'
|
|
||||||
if (!this.username) this.error = 'Email is missing'
|
|
||||||
|
|
||||||
if (this.error) return false
|
|
||||||
|
|
||||||
this.loading = true
|
|
||||||
|
|
||||||
api.sendRequest({
|
|
||||||
method: 'POST',
|
|
||||||
url: '/api/authentication/login',
|
|
||||||
body: {
|
|
||||||
email: this.username,
|
|
||||||
password: this.password,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.token) {
|
|
||||||
return Promise.reject(new Error('Server authentication down.'))
|
|
||||||
}
|
|
||||||
Authentication.updateToken(result.token)
|
|
||||||
m.route.set(this.redirect || '/browse')
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.error = 'Error while logging in! ' + error.message
|
|
||||||
vnode.state.password = ''
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.loading = false
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
|
|
||||||
view: function(vnode) {
|
|
||||||
return [
|
|
||||||
m('div.modal', [
|
|
||||||
m('form', {
|
|
||||||
onsubmit: this.uploadfile.bind(this, vnode),
|
|
||||||
}, [
|
|
||||||
m('h3', 'Filadelfia archival center'),
|
|
||||||
this.error ? m('p.error', this.error) : null,
|
|
||||||
m('label', 'Email or name'),
|
|
||||||
m('input', {
|
|
||||||
type: 'text',
|
|
||||||
value: this.username,
|
|
||||||
oninput: (e) => { this.username = e.currentTarget.value },
|
|
||||||
}),
|
|
||||||
m('label', 'Password'),
|
|
||||||
m('input', {
|
|
||||||
type: 'password',
|
|
||||||
value: this.password,
|
|
||||||
oninput: (e) => { this.password = e.currentTarget.value },
|
|
||||||
}),
|
|
||||||
m('input.spinner', {
|
|
||||||
hidden: this.loading,
|
|
||||||
type: 'submit',
|
|
||||||
value: 'Log in',
|
|
||||||
}),
|
|
||||||
this.loading ? m('div.loading-spinner') : null,
|
|
||||||
]),
|
|
||||||
m('div.login--asuna.spritesheet'),
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Login
|
|
|
@ -1,35 +0,0 @@
|
||||||
const m = require('mithril')
|
|
||||||
const api = require('./api')
|
|
||||||
|
|
||||||
const Tree = []
|
|
||||||
|
|
||||||
exports.Tree = Tree
|
|
||||||
|
|
||||||
exports.loading = false
|
|
||||||
exports.error = ''
|
|
||||||
|
|
||||||
exports.refreshTree = function() {
|
|
||||||
exports.error = ''
|
|
||||||
|
|
||||||
if (exports.loading) return Promise.resolve()
|
|
||||||
|
|
||||||
exports.loading = true
|
|
||||||
|
|
||||||
m.redraw()
|
|
||||||
|
|
||||||
return api.sendRequest({
|
|
||||||
method: 'GET',
|
|
||||||
url: '/api/videos',
|
|
||||||
})
|
|
||||||
.then(pages => {
|
|
||||||
console.log(pages)
|
|
||||||
Tree.splice(0, Tree.length)
|
|
||||||
Tree.push.apply(Tree, pages.videos)
|
|
||||||
exports.loading = false
|
|
||||||
m.redraw()
|
|
||||||
}, err => {
|
|
||||||
exports.loading = false
|
|
||||||
m.redraw()
|
|
||||||
exports.error = 'Error fetching videos: ' + err.message
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
../base
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"build": "echo done;"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"service-core": "^3.0.0-beta.17"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
import fs from 'fs'
|
|
||||||
import { ServiceCore } from 'service-core'
|
|
||||||
import * as index from './index.mjs'
|
|
||||||
|
|
||||||
const port = 4130
|
|
||||||
|
|
||||||
var core = new 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(index).then(function() {
|
|
||||||
return core.run()
|
|
||||||
})
|
|
|
@ -1,11 +0,0 @@
|
||||||
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()
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
{
|
|
||||||
"name": "filadelfia_web",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"port": 4130,
|
|
||||||
"description": "Filadelfia web portal",
|
|
||||||
"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": "asbundle app/index.js public/assets/app.js",
|
|
||||||
"dev:build": "eltro --watch build --npm build",
|
|
||||||
"dev:server": "eltro --watch server --npm server",
|
|
||||||
"server": "node dev.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",
|
|
||||||
"msnodesqlv8": "^2.4.7",
|
|
||||||
"nconf-lite": "^2.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"asbundle": "^2.6.1",
|
|
||||||
"eltro": "^1.4.4",
|
|
||||||
"flaska": "^1.3.2",
|
|
||||||
"mithril": "^2.2.2",
|
|
||||||
"service-core": "^3.0.0-beta.17"
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -1,246 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Filadelfia web portal</title>
|
|
||||||
<base href="/">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="icon" type="image/png" href="/favicon.png">
|
|
||||||
<style>
|
|
||||||
|
|
||||||
[hidden] { display: none !important; }
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--bg: #fff;
|
|
||||||
--bg-component: #f3f7ff;
|
|
||||||
--bg-component-alt: #ffd99c;
|
|
||||||
--color: #031131;
|
|
||||||
--main: #1066ff;
|
|
||||||
--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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
input[type=text],
|
|
||||||
input[type=password] {
|
|
||||||
border: 1px solid var(--main);
|
|
||||||
background: #fff;
|
|
||||||
color: var(--color);
|
|
||||||
border-radius: 0;
|
|
||||||
padding: 0.25rem;
|
|
||||||
line-height: 1rem;
|
|
||||||
outline: none;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button.spinner, input[type=submit].spinner {
|
|
||||||
height: 2rem;
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text]:focus {
|
|
||||||
outline: 1px solid var(--link);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
form p, label {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 500;
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Nav */
|
|
||||||
|
|
||||||
#header nav {
|
|
||||||
display: flex;
|
|
||||||
background: var(--bg-component);
|
|
||||||
padding: 0.5rem 1rem 0.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header nav a,
|
|
||||||
#header nav button {
|
|
||||||
margin-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header h4 {
|
|
||||||
flex: 2 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header h4 a,
|
|
||||||
#header h4 a:visited {
|
|
||||||
color: var(--color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="header"></div>
|
|
||||||
<main id="main"></main>
|
|
||||||
<script type="text/javascript" src="/assets/app.js?v=2"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,144 +0,0 @@
|
||||||
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
|
|
File diff suppressed because one or more lines are too long
|
@ -1,18 +1,15 @@
|
||||||
const m = require('mithril')
|
const m = require('mithril')
|
||||||
|
|
||||||
const Combobox = require('./combobox')
|
|
||||||
const Constants = require('./consts')
|
|
||||||
|
|
||||||
const Frontpage = {
|
const Frontpage = {
|
||||||
oninit: function(vnode) {
|
oninit: function(vnode) {
|
||||||
this.error = ''
|
this.error = ''
|
||||||
this.showAddLocation = true
|
this.showAddLocation = false
|
||||||
this.form = {
|
this.form = {
|
||||||
city: '',
|
city: '',
|
||||||
zip: '',
|
zip: '',
|
||||||
street_name: '',
|
street_name: '',
|
||||||
locations: [
|
locations: [
|
||||||
// 'Hverfisgata, 101 - Reykjavík',
|
'Hverfisgata, 101 - Reykjavík',
|
||||||
],
|
],
|
||||||
type: [ true, false, false, false, false ],
|
type: [ true, false, false, false, false ],
|
||||||
size: [ true, false, false, false, false ],
|
size: [ true, false, false, false, false ],
|
||||||
|
@ -23,23 +20,11 @@ const Frontpage = {
|
||||||
size: [ 'Alveg sama', '0 - 50fm', '50 - 80fm', '80 - 120fm', '120fm +'],
|
size: [ 'Alveg sama', '0 - 50fm', '50 - 80fm', '80 - 120fm', '120fm +'],
|
||||||
rooms: [ 'Alveg sama', 'Stúdíó', '2 - 3 herb.', '3 - 4 herb.', '5 + herb.' ],
|
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) {
|
onFormUpdate: function(vnode, key, index, event) {
|
||||||
if (['city', 'zip', 'street_name'].includes(key)) {
|
if (['city', 'zip', 'street_name'].includes(key)) {
|
||||||
this.form[key] = event.target.value
|
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') {
|
} else if (key === 'type' || key === 'size' || key === 'rooms') {
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
this.form[key][0] = false
|
this.form[key][0] = false
|
||||||
|
@ -73,9 +58,6 @@ const Frontpage = {
|
||||||
this.form.zip = ''
|
this.form.zip = ''
|
||||||
this.form.street_name = ''
|
this.form.street_name = ''
|
||||||
this.form.locations.push(entry)
|
this.form.locations.push(entry)
|
||||||
this.cities = Object.keys(Constants.Locations)
|
|
||||||
this.zips = Object.keys(Constants.Streets)
|
|
||||||
this.streets = []
|
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -117,33 +99,30 @@ const Frontpage = {
|
||||||
hidden: !this.showAddLocation,
|
hidden: !this.showAddLocation,
|
||||||
onsubmit: (e) => this.onLocationAdd(e),
|
onsubmit: (e) => this.onLocationAdd(e),
|
||||||
}, [
|
}, [
|
||||||
m(Combobox, {
|
m('div.form-item', [
|
||||||
label: 'City*',
|
m('label', 'City*'),
|
||||||
items: this.cities,
|
m('input', {
|
||||||
value: this.form.city,
|
type: 'text',
|
||||||
placeholder: 'Reykjavík',
|
placeholder: 'Reykjavík',
|
||||||
oninput: (e) => this.onFormUpdate(vnode, 'city', null, e),
|
oninput: (e) => this.onFormUpdate(vnode, 'city', null, e),
|
||||||
ondone: () => { this.inputs.zip.state.focus() },
|
}),
|
||||||
}),
|
]),
|
||||||
m(Combobox, {
|
m('div.form-item', [
|
||||||
label: 'Postal code (optional)',
|
m('label', 'Postal code (optional)'),
|
||||||
items: this.zips,
|
m('input', {
|
||||||
value: this.form.zip,
|
type: 'text',
|
||||||
placeholder: '000',
|
placeholder: '000',
|
||||||
oninput: (e) => this.onFormUpdate(vnode, 'zip', null, e),
|
oninput: (e) => this.onFormUpdate(vnode, 'zip', null, e),
|
||||||
oncreate: (e) => { this.inputs.zip = e },
|
}),
|
||||||
ondone: () => { this.inputs.street.state.focus() },
|
]),
|
||||||
}),
|
m('div.form-item.form-fill', [
|
||||||
m(Combobox, {
|
m('label', 'Street name (optional)'),
|
||||||
class: 'form-fill',
|
m('input', {
|
||||||
label: 'Street name (optional)',
|
type: 'text',
|
||||||
items: this.streets,
|
placeholder: 'Enter your dream street adress',
|
||||||
value: this.form.street_name,
|
oninput: (e) => this.onFormUpdate(vnode, 'street_name', null, e),
|
||||||
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('div.form-item.form-small.form-no-label', [
|
||||||
m('input', {
|
m('input', {
|
||||||
class: this.form.city ? 'button-active' : 'button-outline',
|
class: this.form.city ? 'button-active' : 'button-outline',
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
export function cancelPropagation(event) {
|
|
||||||
event.stopPropagation()
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,195 +0,0 @@
|
||||||
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
|
|
|
@ -50,7 +50,7 @@
|
||||||
"dot": "^2.0.0-beta.1",
|
"dot": "^2.0.0-beta.1",
|
||||||
"flaska": "^1.3.0",
|
"flaska": "^1.3.0",
|
||||||
"formidable": "^1.2.6",
|
"formidable": "^1.2.6",
|
||||||
"msnodesqlv8": "^2.7.0",
|
"msnodesqlv8": "^2.4.7",
|
||||||
"nconf-lite": "^2.0.0",
|
"nconf-lite": "^2.0.0",
|
||||||
"striptags": "^3.2.0"
|
"striptags": "^3.2.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -571,38 +571,6 @@ i.ic-plus {
|
||||||
background-size: contain;
|
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>
|
</style>
|
||||||
<link id="headstyle" rel="Stylesheet" href="/assets/app.css?v=2" type="text/css" media="none" />
|
<link id="headstyle" rel="Stylesheet" href="/assets/app.css?v=2" type="text/css" media="none" />
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -8,13 +8,17 @@
|
||||||
"test": "test"
|
"test": "test"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index.mjs",
|
"start": "node --experimental-modules index.mjs",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build:prod": "asbundle app/index.js public/assets/app.js && asbundle app/admin/admin.js public/assets/admin.js",
|
"build:prod": "asbundle app/index.js public/assets/app.js && asbundle app/admin/admin.js public/assets/admin.js",
|
||||||
"build": "asbundle app/index.js public/assets/app.js && asbundle app/admin/admin.js public/assets/admin.js",
|
"build": "asbundle app/index.js public/assets/app.js && asbundle app/admin/admin.js public/assets/admin.js",
|
||||||
"dev:build": "npm-watch build",
|
"dev:build": "npm-watch build",
|
||||||
"dev:server": "node index.mjs | bunyan",
|
"dev:server": "node index.mjs | bunyan",
|
||||||
"dev": "npm-watch dev:server",
|
"dev": "npm-watch dev:server",
|
||||||
|
"watch:sass:public": "sass --watch app/app.scss public/assets/app.css",
|
||||||
|
"watch:sass:admin": "sass --watch app/admin.scss public/assets/admin.css",
|
||||||
|
"prod": "npm run build && npm start",
|
||||||
|
"temp": "asbundle"
|
||||||
},
|
},
|
||||||
"watch": {
|
"watch": {
|
||||||
"dev:server": {
|
"dev:server": {
|
||||||
|
|
Loading…
Reference in a new issue