refactored everything to use lowdb instead of sqlite
This commit is contained in:
parent
c1f87628d4
commit
27871c9ed4
53 changed files with 5339 additions and 1163 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -33,12 +33,6 @@ node_modules
|
||||||
.node_repl_history
|
.node_repl_history
|
||||||
|
|
||||||
db.sqlite
|
db.sqlite
|
||||||
public/client.css
|
|
||||||
public/client.css.map
|
|
||||||
public/client.js
|
public/client.js
|
||||||
public/main.css
|
|
||||||
public/main.css.map
|
|
||||||
public/main.js
|
public/main.js
|
||||||
public/status.css
|
|
||||||
public/status.css.map
|
|
||||||
public/status.js
|
public/status.js
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import _ from 'lodash'
|
|
||||||
import knex from 'knex'
|
import knex from 'knex'
|
||||||
import bookshelf from 'bookshelf'
|
import bookshelf from 'bookshelf'
|
||||||
|
|
||||||
import config from '../config'
|
import defaults from './defaults.mjs'
|
||||||
import log from '../log'
|
import config from './config.mjs'
|
||||||
|
import log from './log.mjs'
|
||||||
|
|
||||||
let host = config.get('knex:connection')
|
let host = config.get('knex:connection')
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ let shelf = bookshelf(client)
|
||||||
// Helper method to create models
|
// Helper method to create models
|
||||||
shelf.createModel = (attr, opts) => {
|
shelf.createModel = (attr, opts) => {
|
||||||
// Create default attributes to all models
|
// Create default attributes to all models
|
||||||
let attributes = _.defaults(attr, {
|
let attributes = defaults(attr, {
|
||||||
/**
|
/**
|
||||||
* Initialize a new instance of model. This does not get called when
|
* Initialize a new instance of model. This does not get called when
|
||||||
* relations to this model is being fetched though.
|
* relations to this model is being fetched though.
|
||||||
|
@ -55,7 +55,7 @@ shelf.createModel = (attr, opts) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create default options for all models
|
// Create default options for all models
|
||||||
let options = _.defaults(opts, {
|
let options = defaults(opts, {
|
||||||
/**
|
/**
|
||||||
* Create new model object in database.
|
* Create new model object in database.
|
||||||
*
|
*
|
|
@ -1,5 +1,7 @@
|
||||||
import Settings from '../settings/model'
|
import CasparConnection from 'casparcg-connection'
|
||||||
import { CasparCG, AMCP } from 'casparcg-connection'
|
|
||||||
|
const CasparCG = CasparConnection.CasparCG
|
||||||
|
const AMCP = CasparConnection.AMCP
|
||||||
|
|
||||||
const timeoutDuration = 5000
|
const timeoutDuration = 5000
|
||||||
|
|
||||||
|
@ -11,23 +13,20 @@ let casparIsPlaying
|
||||||
let casparIsConnected
|
let casparIsConnected
|
||||||
let currentHost
|
let currentHost
|
||||||
|
|
||||||
export async function initialise(log, socket) {
|
export function initialise(log, db, socket) {
|
||||||
io = socket.socket
|
io = socket.socket
|
||||||
logger = log
|
logger = log
|
||||||
|
db = db
|
||||||
|
|
||||||
return connect()
|
connect(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function connect() {
|
export function connect(db) {
|
||||||
currentHost = await Settings.getValue('casparcg')
|
currentHost = db.get('settings').value().casparhost
|
||||||
casparIsPlaying = false
|
casparIsPlaying = false
|
||||||
casparIsConnected = false
|
casparIsConnected = false
|
||||||
logger.info('CasparCG: Connectiong to', currentHost + ':' + 5250)
|
logger.info('CasparCG: Connectiong to', currentHost + ':' + 5250)
|
||||||
|
|
||||||
if (connection && connection.close) {
|
|
||||||
await connection.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
connection = new CasparCG({
|
connection = new CasparCG({
|
||||||
host: currentHost,
|
host: currentHost,
|
||||||
port: 5250,
|
port: 5250,
|
||||||
|
@ -37,6 +36,7 @@ export async function connect() {
|
||||||
logger.error(err, 'CasparCG: Error')
|
logger.error(err, 'CasparCG: Error')
|
||||||
},
|
},
|
||||||
onConnectionStatus: data => {
|
onConnectionStatus: data => {
|
||||||
|
if (casparIsPlaying) return
|
||||||
casparIsConnected = data.connected
|
casparIsConnected = data.connected
|
||||||
|
|
||||||
if (!casparIsConnected) {
|
if (!casparIsConnected) {
|
||||||
|
@ -45,9 +45,10 @@ export async function connect() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onConnected: async connected => {
|
onConnected: async connected => {
|
||||||
|
if (casparIsPlaying) return
|
||||||
logger.info('CasparCG: connected', connected)
|
logger.info('CasparCG: connected', connected)
|
||||||
if (!casparIsPlaying) {
|
if (!casparIsPlaying) {
|
||||||
startPlaying().then()
|
startPlaying(db).then()
|
||||||
} else {
|
} else {
|
||||||
logger.warn('CasparCG: Stopped from starting play again.')
|
logger.warn('CasparCG: Stopped from starting play again.')
|
||||||
}
|
}
|
||||||
|
@ -63,8 +64,8 @@ export function currentStatus(e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startPlaying() {
|
export async function startPlaying(db) {
|
||||||
let ip = 'localhost'
|
let ip = db.get('settings').value().casparplayhost
|
||||||
|
|
||||||
// Check if we lost connection while attempting to start playing
|
// Check if we lost connection while attempting to start playing
|
||||||
if (!connection.connected) {
|
if (!connection.connected) {
|
||||||
|
@ -75,7 +76,7 @@ export async function startPlaying() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Send a play command
|
// Send a play command
|
||||||
let command = `PLAY 1-100 [HTML] "http://${ip}:3000/client.html" CUT 1 LINEAR RIGHT`
|
let command = `PLAY 1-100 [HTML] "http://${ip}/client.html" CUT 1 LINEAR RIGHT`
|
||||||
logger.info(`CasparCG Command: ${command}`)
|
logger.info(`CasparCG Command: ${command}`)
|
||||||
await connection.do(new AMCP.CustomCommand(command))
|
await connection.do(new AMCP.CustomCommand(command))
|
||||||
success = true
|
success = true
|
||||||
|
@ -94,6 +95,12 @@ export async function startPlaying() {
|
||||||
// We are playing, notify all clients
|
// We are playing, notify all clients
|
||||||
io.emit('casparcg.status', currentStatus())
|
io.emit('casparcg.status', currentStatus())
|
||||||
logger.info('CasparCG: client is up and playing')
|
logger.info('CasparCG: client is up and playing')
|
||||||
|
/* console.log(connection)
|
||||||
|
for (var key in connection) {
|
||||||
|
console.log(key, '=', typeof(connection[key]))
|
||||||
|
} */
|
||||||
|
connection.autoConnect = false
|
||||||
|
// connection.close()
|
||||||
} else {
|
} else {
|
||||||
// Unknown error occured
|
// Unknown error occured
|
||||||
casparIsPlaying = false
|
casparIsPlaying = false
|
|
@ -1,4 +1,4 @@
|
||||||
import { currentStatus } from './client'
|
import { currentStatus } from './client.mjs'
|
||||||
|
|
||||||
export async function casparConnection(ctx) {
|
export async function casparConnection(ctx) {
|
||||||
ctx.socket.emit('casparcg.status', currentStatus())
|
ctx.socket.emit('casparcg.status', currentStatus())
|
|
@ -1,6 +1,5 @@
|
||||||
import _ from 'lodash'
|
|
||||||
import nconf from 'nconf'
|
import nconf from 'nconf'
|
||||||
const pckg = require('./package.json')
|
import { readFileSync } from 'fs'
|
||||||
|
|
||||||
// Helper method for global usage.
|
// Helper method for global usage.
|
||||||
nconf.inTest = () => nconf.get('NODE_ENV') === 'test'
|
nconf.inTest = () => nconf.get('NODE_ENV') === 'test'
|
||||||
|
@ -18,8 +17,15 @@ nconf.argv()
|
||||||
|
|
||||||
|
|
||||||
// Load package.json for name and such
|
// Load package.json for name and such
|
||||||
let project = _.pick(pckg, ['name', 'version', 'description', 'author', 'license', 'homepage'])
|
let pckg = JSON.parse(readFileSync('./package.json'))
|
||||||
|
let project = {
|
||||||
|
name: pckg.name,
|
||||||
|
version: pckg.version,
|
||||||
|
description: pckg.description,
|
||||||
|
author: pckg.author,
|
||||||
|
license: pckg.license,
|
||||||
|
homepage: pckg.homepage,
|
||||||
|
}
|
||||||
|
|
||||||
// If we have global.it, there's a huge chance
|
// If we have global.it, there's a huge chance
|
||||||
// we're in test mode so we force node_env to be test.
|
// we're in test mode so we force node_env to be test.
|
||||||
|
@ -57,4 +63,5 @@ if (typeof global.it === 'function' & !nconf.inTest()) {
|
||||||
console.log('Critical: potentially running test on production enviroment. Shutting down.')
|
console.log('Critical: potentially running test on production enviroment. Shutting down.')
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
module.exports = nconf
|
|
||||||
|
export default nconf
|
|
@ -1,4 +1,4 @@
|
||||||
import { reset, list } from './routes'
|
import { reset, list } from './routes.mjs'
|
||||||
|
|
||||||
export async function contentConnection(ctx) {
|
export async function contentConnection(ctx) {
|
||||||
ctx.log.info('Got new socket connection')
|
ctx.log.info('Got new socket connection')
|
|
@ -1,4 +1,4 @@
|
||||||
import bookshelf from '../bookshelf'
|
import bookshelf from '../bookshelf.mjs'
|
||||||
|
|
||||||
/* Content model:
|
/* Content model:
|
||||||
{
|
{
|
|
@ -1,5 +1,4 @@
|
||||||
import _ from 'lodash'
|
import template from 'lodash.template'
|
||||||
import Content from './model'
|
|
||||||
|
|
||||||
export const active = { }
|
export const active = { }
|
||||||
|
|
||||||
|
@ -14,13 +13,17 @@ function getSocket(ctx, all) {
|
||||||
* Display a specific graphic content
|
* Display a specific graphic content
|
||||||
*/
|
*/
|
||||||
export async function display(ctx, data) {
|
export async function display(ctx, data) {
|
||||||
let compiled = _.template(data.graphic.settings.html)
|
let compiled = template(data.graphic.settings.html)
|
||||||
let html = compiled(data.data)
|
let html = compiled(data.data)
|
||||||
|
|
||||||
let old = await Content.getSingle(data.graphic.name)
|
// let old = await Content.getSingle(data.graphic.name)
|
||||||
|
|
||||||
|
let playing = ctx.db.get('playing')
|
||||||
|
|
||||||
|
let old = playing.find({ name: data.graphic.name }).value()
|
||||||
|
|
||||||
if (old) {
|
if (old) {
|
||||||
await old.destroy()
|
await playing.removeById(old.id).write()
|
||||||
}
|
}
|
||||||
|
|
||||||
let payload = {
|
let payload = {
|
||||||
|
@ -32,9 +35,9 @@ export async function display(ctx, data) {
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = await Content.create(payload)
|
await playing.insert(payload).write()
|
||||||
|
|
||||||
ctx.io.emit('client.display', content.toJSON())
|
ctx.io.emit('client.display', playing.find({ name: data.graphic.name }).value())
|
||||||
|
|
||||||
list(ctx, true)
|
list(ctx, true)
|
||||||
}
|
}
|
||||||
|
@ -45,11 +48,13 @@ export async function display(ctx, data) {
|
||||||
* Hide a specific graphic content
|
* Hide a specific graphic content
|
||||||
*/
|
*/
|
||||||
export async function hide(ctx, data) {
|
export async function hide(ctx, data) {
|
||||||
let content = await Content.getSingle(data.name)
|
let playing = ctx.db.get('playing')
|
||||||
|
|
||||||
if (!content) return
|
let old = playing.find({ name: data.name }).value()
|
||||||
|
|
||||||
await content.destroy()
|
if (!old) return
|
||||||
|
|
||||||
|
await playing.removeById(old.id).write()
|
||||||
|
|
||||||
ctx.io.emit('client.hide', {
|
ctx.io.emit('client.hide', {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
|
@ -63,7 +68,7 @@ function generateDisplayText(item) {
|
||||||
// return `${item.data.text} - ${item.data.finished}`
|
// return `${item.data.text} - ${item.data.finished}`
|
||||||
// }
|
// }
|
||||||
try {
|
try {
|
||||||
return _.template(item.graphic.settings.main)(item.data)
|
return template(item.graphic.settings.main)(item.data)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return `Error creating display: ${e.message}`
|
return `Error creating display: ${e.message}`
|
||||||
}
|
}
|
||||||
|
@ -76,12 +81,14 @@ function generateDisplayText(item) {
|
||||||
* Send a name list of all active graphics
|
* Send a name list of all active graphics
|
||||||
*/
|
*/
|
||||||
export async function list(ctx, all) {
|
export async function list(ctx, all) {
|
||||||
let allContent = await Content.getAll()
|
let allContent = ctx.db.get('playing').value()
|
||||||
|
|
||||||
let payload = await Promise.all(allContent.map(item => ({
|
let payload = await Promise.all(allContent.map(function(item) {
|
||||||
name: item.get('name'),
|
return {
|
||||||
display: generateDisplayText(item.toJSON()),
|
name: item.name,
|
||||||
})))
|
display: generateDisplayText(item),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
getSocket(ctx, all).emit('content.list', payload)
|
getSocket(ctx, all).emit('content.list', payload)
|
||||||
}
|
}
|
||||||
|
@ -93,7 +100,7 @@ export async function list(ctx, all) {
|
||||||
* Send actual graphics of all active graphics
|
* Send actual graphics of all active graphics
|
||||||
*/
|
*/
|
||||||
export async function reset(ctx) {
|
export async function reset(ctx) {
|
||||||
let allContent = await Content.getAll()
|
let allContent = ctx.db.get('playing').value()
|
||||||
|
|
||||||
ctx.socket.emit('client.reset', allContent.toJSON())
|
ctx.socket.emit('client.reset', allContent)
|
||||||
}
|
}
|
162
api/db.mjs
Normal file
162
api/db.mjs
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
import lowdb from 'lowdb'
|
||||||
|
import FileAsync from 'lowdb/adapters/FileAsync.js'
|
||||||
|
import log from './log.mjs'
|
||||||
|
|
||||||
|
let lastId = -1
|
||||||
|
|
||||||
|
// Take from https://github.com/typicode/lodash-id/blob/master/src/index.js
|
||||||
|
// from package lodash-id
|
||||||
|
const lodashId = {
|
||||||
|
// Empties properties
|
||||||
|
__empty: function (doc) {
|
||||||
|
this.forEach(doc, function (value, key) {
|
||||||
|
delete doc[key]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// Copies properties from an object to another
|
||||||
|
__update: function (dest, src) {
|
||||||
|
this.forEach(src, function (value, key) {
|
||||||
|
dest[key] = value
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// Removes an item from an array
|
||||||
|
__remove: function (array, item) {
|
||||||
|
var index = this.indexOf(array, item)
|
||||||
|
if (index !== -1) array.splice(index, 1)
|
||||||
|
},
|
||||||
|
|
||||||
|
__id: function () {
|
||||||
|
var id = this.id || 'id'
|
||||||
|
return id
|
||||||
|
},
|
||||||
|
|
||||||
|
getById: function (collection, id) {
|
||||||
|
var self = this
|
||||||
|
return this.find(collection, function (doc) {
|
||||||
|
if (self.has(doc, self.__id())) {
|
||||||
|
return doc[self.__id()] === id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
createId: function (collection, doc) {
|
||||||
|
let next = new Date().getTime()
|
||||||
|
if (next <= lastId) {
|
||||||
|
next = lastId + 1
|
||||||
|
}
|
||||||
|
lastId = next
|
||||||
|
return next
|
||||||
|
},
|
||||||
|
|
||||||
|
insert: function (collection, doc) {
|
||||||
|
doc[this.__id()] = doc[this.__id()] || this.createId(collection, doc)
|
||||||
|
var d = this.getById(collection, doc[this.__id()])
|
||||||
|
if (d) throw new Error('Insert failed, duplicate id')
|
||||||
|
collection.push(doc)
|
||||||
|
return doc
|
||||||
|
},
|
||||||
|
|
||||||
|
upsert: function (collection, doc) {
|
||||||
|
if (doc[this.__id()]) {
|
||||||
|
// id is set
|
||||||
|
var d = this.getById(collection, doc[this.__id()])
|
||||||
|
if (d) {
|
||||||
|
// replace properties of existing object
|
||||||
|
this.__empty(d)
|
||||||
|
this.assign(d, doc)
|
||||||
|
} else {
|
||||||
|
// push new object
|
||||||
|
collection.push(doc)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// create id and push new object
|
||||||
|
doc[this.__id()] = this.createId(collection, doc)
|
||||||
|
collection.push(doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc
|
||||||
|
},
|
||||||
|
|
||||||
|
updateById: function (collection, id, attrs) {
|
||||||
|
var doc = this.getById(collection, id)
|
||||||
|
|
||||||
|
if (doc) {
|
||||||
|
this.assign(doc, attrs, {id: doc.id})
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc
|
||||||
|
},
|
||||||
|
|
||||||
|
updateWhere: function (collection, predicate, attrs) {
|
||||||
|
var self = this
|
||||||
|
var docs = this.filter(collection, predicate)
|
||||||
|
|
||||||
|
docs.forEach(function (doc) {
|
||||||
|
self.assign(doc, attrs, {id: doc.id})
|
||||||
|
})
|
||||||
|
|
||||||
|
return docs
|
||||||
|
},
|
||||||
|
|
||||||
|
replaceById: function (collection, id, attrs) {
|
||||||
|
var doc = this.getById(collection, id)
|
||||||
|
|
||||||
|
if (doc) {
|
||||||
|
var docId = doc.id
|
||||||
|
this.__empty(doc)
|
||||||
|
this.assign(doc, attrs, {id: docId})
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc
|
||||||
|
},
|
||||||
|
|
||||||
|
removeById: function (collection, id) {
|
||||||
|
var doc = this.getById(collection, id)
|
||||||
|
|
||||||
|
this.__remove(collection, doc)
|
||||||
|
|
||||||
|
return doc
|
||||||
|
},
|
||||||
|
|
||||||
|
removeWhere: function (collection, predicate) {
|
||||||
|
var self = this
|
||||||
|
var docs = this.filter(collection, predicate)
|
||||||
|
|
||||||
|
docs.forEach(function (doc) {
|
||||||
|
self.__remove(collection, doc)
|
||||||
|
})
|
||||||
|
|
||||||
|
return docs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const adapter = new FileAsync('db.json')
|
||||||
|
|
||||||
|
export default function GetDB() {
|
||||||
|
return lowdb(adapter)
|
||||||
|
.then(function(db) {
|
||||||
|
db._.mixin(lodashId)
|
||||||
|
|
||||||
|
db.defaults({
|
||||||
|
graphics: [],
|
||||||
|
presets: [],
|
||||||
|
playing: [],
|
||||||
|
schedule: [],
|
||||||
|
settings: {
|
||||||
|
casparplayhost: 'localhost:3000',
|
||||||
|
casparhost: 'host.docker.internal',
|
||||||
|
},
|
||||||
|
version: 1,
|
||||||
|
trash: [],
|
||||||
|
})
|
||||||
|
.write()
|
||||||
|
.then(
|
||||||
|
function() { },
|
||||||
|
function(e) { log.error(e, 'Error writing defaults to lowdb') }
|
||||||
|
)
|
||||||
|
|
||||||
|
return db
|
||||||
|
})
|
||||||
|
}
|
34
api/defaults.mjs
Normal file
34
api/defaults.mjs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
// taken from isobject npm library
|
||||||
|
function isObject(val) {
|
||||||
|
return val != null && typeof val === 'object' && Array.isArray(val) === false
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function defaults(options, def) {
|
||||||
|
let out = { }
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
Object.keys(options || {}).forEach(key => {
|
||||||
|
out[key] = options[key]
|
||||||
|
|
||||||
|
if (Array.isArray(out[key])) {
|
||||||
|
out[key] = out[key].map(item => {
|
||||||
|
if (isObject(item)) return defaults(item)
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
} else if (out[key] && typeof out[key] === 'object') {
|
||||||
|
out[key] = defaults(options[key], def && def[key])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (def) {
|
||||||
|
Object.keys(def).forEach(function(key) {
|
||||||
|
if (typeof out[key] === 'undefined') {
|
||||||
|
out[key] = def[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import bookshelf from '../bookshelf'
|
import bookshelf from '../bookshelf.mjs'
|
||||||
|
|
||||||
/* Graphic model:
|
/* Graphic model:
|
||||||
{
|
{
|
|
@ -1,14 +1,12 @@
|
||||||
import Graphic from './model'
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Event: 'graphic.all'
|
* Event: 'graphic.all'
|
||||||
*
|
*
|
||||||
* Request all graphics in store
|
* Request all graphics in store
|
||||||
*/
|
*/
|
||||||
export async function all(ctx) {
|
export async function all(ctx) {
|
||||||
let data = await Graphic.getAll()
|
let data = ctx.db.get('graphics').value()
|
||||||
|
|
||||||
ctx.socket.emit('graphic.all', data.toJSON())
|
ctx.socket.emit('graphic.all', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -22,9 +20,9 @@ export async function single(ctx, data) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let graphic = await Graphic.getSingle(data.id)
|
let graphic = ctx.db.get('graphics').getById(Number(data.id)).value()
|
||||||
|
|
||||||
ctx.socket.emit('graphic.single', graphic.toJSON())
|
ctx.socket.emit('graphic.single', graphic)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -37,16 +35,17 @@ export async function single(ctx, data) {
|
||||||
*/
|
*/
|
||||||
export async function create(ctx, data) {
|
export async function create(ctx, data) {
|
||||||
data.settings = {}
|
data.settings = {}
|
||||||
data.is_deleted = false
|
|
||||||
|
|
||||||
if (data.engine === 'countdown') {
|
if (data.engine === 'countdown') {
|
||||||
data.settings.html = `<span id="${data.name}-countdown-timer">countdown appears here</span>`
|
data.settings.html = `<span id="${data.name}-countdown-timer">countdown appears here</span>`
|
||||||
data.settings.main = '<%- text %> - <%- finished %>'
|
data.settings.main = '<%- text %> - <%- finished %>'
|
||||||
}
|
}
|
||||||
|
|
||||||
let graphic = await Graphic.create(data)
|
let graphics = ctx.db.get('graphics').insert(data)
|
||||||
|
let graphic = graphics.last().value()
|
||||||
|
await graphics.write()
|
||||||
|
|
||||||
ctx.io.emit('graphic.single', graphic.toJSON())
|
ctx.io.emit('graphic.single', graphic)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -62,12 +61,16 @@ export async function remove(ctx, data) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let graphic = await Graphic.getSingle(data.id)
|
let graphics = ctx.db.get('graphics')
|
||||||
graphic.set({ is_deleted: true })
|
let graphic = graphics.removeById(Number(data.id)).value()
|
||||||
await graphic.save()
|
await graphics.write()
|
||||||
|
|
||||||
let output = await Graphic.getAll()
|
graphic.deleted_at = new Date().getTime()
|
||||||
ctx.io.emit('graphic.all', output.toJSON())
|
graphic.type = 'graphic'
|
||||||
|
|
||||||
|
await ctx.db.get('trash').insert(graphic).write()
|
||||||
|
|
||||||
|
ctx.io.emit('graphic.all', graphics)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -86,11 +89,9 @@ export async function update(ctx, data) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let graphic = await Graphic.getSingle(data.id)
|
await ctx.db.get('graphics').updateById(Number(data.id), data).write()
|
||||||
|
|
||||||
graphic.set(data)
|
let graphic = ctx.db.get('graphics').getById(Number(data.id)).value()
|
||||||
|
|
||||||
await graphic.save()
|
ctx.io.emit('graphic.single', graphic)
|
||||||
|
|
||||||
ctx.io.emit('graphic.single', graphic.toJSON())
|
|
||||||
}
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
import _ from 'lodash'
|
|
||||||
|
|
||||||
export function register(ctx, name, method) {
|
export function register(ctx, name, method) {
|
||||||
if (_.isPlainObject(method)) {
|
if (typeof(method) === 'object') {
|
||||||
Object.keys(method).forEach(key => {
|
Object.keys(method).forEach(key => {
|
||||||
register(ctx, [name, key].join('.'), method[key])
|
register(ctx, [name, key].join('.'), method[key])
|
||||||
})
|
})
|
|
@ -1,10 +1,10 @@
|
||||||
import _ from 'lodash'
|
|
||||||
import bunyan from 'bunyan'
|
import bunyan from 'bunyan'
|
||||||
import config from './config'
|
import defaults from './defaults.mjs'
|
||||||
|
import config from './config.mjs'
|
||||||
|
|
||||||
// Clone the settings as we will be touching
|
// Clone the settings as we will be touching
|
||||||
// on them slightly.
|
// on them slightly.
|
||||||
let settings = _.cloneDeep(config.get('bunyan'))
|
let settings = defaults(config.get('bunyan'), null)
|
||||||
|
|
||||||
// Replace any instance of 'process.stdout' with the
|
// Replace any instance of 'process.stdout' with the
|
||||||
// actual reference to the process.stdout.
|
// actual reference to the process.stdout.
|
|
@ -1,4 +1,4 @@
|
||||||
import bookshelf from '../bookshelf'
|
import bookshelf from '../bookshelf.mjs'
|
||||||
|
|
||||||
/* Preset model:
|
/* Preset model:
|
||||||
{
|
{
|
|
@ -1,50 +0,0 @@
|
||||||
import Preset from './model'
|
|
||||||
|
|
||||||
export async function all(ctx, payload) {
|
|
||||||
let id = Number(payload.graphic_id || payload.id)
|
|
||||||
|
|
||||||
let data = await Preset.getAll({ graphic_id: id }, [], 'sort')
|
|
||||||
|
|
||||||
ctx.io.emit(`preset.all:${id}`, data.toJSON())
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function add(ctx, payload) {
|
|
||||||
payload.is_deleted = false
|
|
||||||
payload.sort = 1
|
|
||||||
|
|
||||||
let last = await Preset.query(q => {
|
|
||||||
q.where({ graphic_id: payload.graphic_id })
|
|
||||||
q.orderBy('sort', 'desc')
|
|
||||||
q.limit(1)
|
|
||||||
}).fetch({ require: false })
|
|
||||||
|
|
||||||
if (last) {
|
|
||||||
payload.sort = last.get('sort') + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
await Preset.create(payload)
|
|
||||||
|
|
||||||
await all(ctx, payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function patch(ctx, payload) {
|
|
||||||
await Promise.all(payload.map(async item => {
|
|
||||||
let preset = await Preset.getSingle(item.id)
|
|
||||||
|
|
||||||
preset.set({ sort: item.sort })
|
|
||||||
|
|
||||||
await preset.save()
|
|
||||||
}))
|
|
||||||
|
|
||||||
await all(ctx, payload[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function remove(ctx, payload) {
|
|
||||||
let preset = await Preset.getSingle(payload.id)
|
|
||||||
|
|
||||||
preset.set('is_deleted', true)
|
|
||||||
|
|
||||||
await preset.save()
|
|
||||||
|
|
||||||
await all(ctx, payload)
|
|
||||||
}
|
|
50
api/preset/routes.mjs
Normal file
50
api/preset/routes.mjs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
export async function all(ctx, payload) {
|
||||||
|
let id = Number(payload.graphic_id || payload.id)
|
||||||
|
|
||||||
|
let data = ctx.db.get('presets').filter({ graphic_id: id }).value()
|
||||||
|
|
||||||
|
ctx.io.emit(`preset.all:${id}`, data || [])
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function add(ctx, payload) {
|
||||||
|
payload.sort = 1
|
||||||
|
|
||||||
|
let presets = ctx.db.get('presets')
|
||||||
|
|
||||||
|
let last = presets.sortBy('sort').last().value()
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
payload.sort = last.sort + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.graphic_id = Number(payload.graphic_id)
|
||||||
|
|
||||||
|
await presets.insert(payload).write()
|
||||||
|
|
||||||
|
await all(ctx, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function patch(ctx, payload) {
|
||||||
|
let presets = ctx.db.get('presets')
|
||||||
|
|
||||||
|
payload.forEach(function(item) {
|
||||||
|
presets.updateById(Number(item.id), { sort: item.sort })
|
||||||
|
})
|
||||||
|
|
||||||
|
await presets.write()
|
||||||
|
|
||||||
|
await all(ctx, payload[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(ctx, payload) {
|
||||||
|
let presets = ctx.db.get('presets')
|
||||||
|
let preset = presets.removeById(Number(payload.id)).value()
|
||||||
|
await presets.write()
|
||||||
|
|
||||||
|
preset.deleted_at = new Date().getTime()
|
||||||
|
preset.type = 'preset'
|
||||||
|
|
||||||
|
await ctx.db.get('trash').insert(preset).write()
|
||||||
|
|
||||||
|
await all(ctx, payload)
|
||||||
|
}
|
|
@ -1,33 +0,0 @@
|
||||||
import logger from '../log'
|
|
||||||
import { register } from './io/helper'
|
|
||||||
import { contentConnection } from './content/connection'
|
|
||||||
import { casparConnection } from './casparcg/connection'
|
|
||||||
|
|
||||||
import * as content from './content/routes'
|
|
||||||
import * as engine from './engine/routes'
|
|
||||||
import * as graphic from './graphic/routes'
|
|
||||||
import * as preset from './preset/routes'
|
|
||||||
import * as settings from './settings/routes'
|
|
||||||
import * as schedule from './schedule/routes'
|
|
||||||
|
|
||||||
function onConnection(server, data) {
|
|
||||||
const io = server.socket
|
|
||||||
const socket = data.socket
|
|
||||||
const log = logger.child({
|
|
||||||
id: socket.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
let ctx = { io, socket, log }
|
|
||||||
|
|
||||||
contentConnection(ctx)
|
|
||||||
casparConnection(ctx)
|
|
||||||
|
|
||||||
register(ctx, 'content', content)
|
|
||||||
register(ctx, 'engine', engine)
|
|
||||||
register(ctx, 'graphic', graphic)
|
|
||||||
register(ctx, 'preset', preset)
|
|
||||||
register(ctx, 'settings', settings)
|
|
||||||
register(ctx, 'schedule', schedule)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default onConnection
|
|
33
api/routerio.mjs
Normal file
33
api/routerio.mjs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import logger from './log.mjs'
|
||||||
|
import { register } from './io/helper.mjs'
|
||||||
|
import { contentConnection } from './content/connection.mjs'
|
||||||
|
import { casparConnection } from './casparcg/connection.mjs'
|
||||||
|
|
||||||
|
import * as content from './content/routes.mjs'
|
||||||
|
import * as engine from './engine/routes.mjs'
|
||||||
|
import * as graphic from './graphic/routes.mjs'
|
||||||
|
import * as preset from './preset/routes.mjs'
|
||||||
|
import * as settings from './settings/routes.mjs'
|
||||||
|
import * as schedule from './schedule/routes.mjs'
|
||||||
|
|
||||||
|
function onConnection(server, db, data) {
|
||||||
|
const io = server.socket
|
||||||
|
const socket = data.socket
|
||||||
|
const log = logger.child({
|
||||||
|
id: socket.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
let ctx = { io, socket, log, db }
|
||||||
|
|
||||||
|
contentConnection(ctx)
|
||||||
|
casparConnection(ctx)
|
||||||
|
|
||||||
|
register(ctx, 'content', content)
|
||||||
|
register(ctx, 'engine', engine)
|
||||||
|
register(ctx, 'graphic', graphic)
|
||||||
|
register(ctx, 'preset', preset)
|
||||||
|
register(ctx, 'settings', settings)
|
||||||
|
register(ctx, 'schedule', schedule)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default onConnection
|
|
@ -1,5 +1,5 @@
|
||||||
import bookshelf from '../bookshelf'
|
import bookshelf from '../bookshelf.mjs'
|
||||||
import Graphic from '../graphic/model'
|
import Graphic from '../graphic/model.mjs'
|
||||||
|
|
||||||
/* Schedule model:
|
/* Schedule model:
|
||||||
{
|
{
|
|
@ -1,32 +1,34 @@
|
||||||
import Schedule from './model'
|
import Schedule from './model.mjs'
|
||||||
|
|
||||||
export async function all(ctx) {
|
export async function all(ctx) {
|
||||||
let data = await Schedule.getAll({ }, ['graphic'], 'sort')
|
let graphics = ctx.db.get('graphics')
|
||||||
|
let data = ctx.db.get('schedule').forEach(function(s) {
|
||||||
|
s.graphic = graphics.getById(s.graphic_id).value()
|
||||||
|
}).sortBy('sort').value()
|
||||||
|
// let data = await Schedule.getAll({ }, ['graphic'], 'sort')
|
||||||
|
|
||||||
ctx.io.emit('schedule.all', data.toJSON())
|
ctx.io.emit('schedule.all', data)
|
||||||
total(ctx)
|
total(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function total(ctx) {
|
export async function total(ctx) {
|
||||||
let data = await Schedule.getAll({ }, ['graphic'], 'sort')
|
let data = ctx.db.get('schedule').size()
|
||||||
|
|
||||||
ctx.io.emit('schedule.total', { total: data.length })
|
ctx.io.emit('schedule.total', { total: data })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function add(ctx, payload) {
|
export async function add(ctx, payload) {
|
||||||
payload.is_deleted = false
|
|
||||||
payload.sort = 1
|
payload.sort = 1
|
||||||
|
|
||||||
let last = await Schedule.query(q => {
|
let schedule = ctx.db.get('schedule')
|
||||||
q.orderBy('sort', 'desc')
|
|
||||||
q.limit(1)
|
let last = schedule.sortBy('sort').last().value()
|
||||||
}).fetch({ require: false })
|
|
||||||
|
|
||||||
if (last) {
|
if (last) {
|
||||||
payload.sort = last.get('sort') + 1
|
payload.sort = last.sort + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
await Schedule.create(payload)
|
await schedule.insert(payload).write()
|
||||||
|
|
||||||
await all(ctx)
|
await all(ctx)
|
||||||
}
|
}
|
|
@ -1,35 +0,0 @@
|
||||||
import Koa from 'koa'
|
|
||||||
import serve from 'koa-better-serve'
|
|
||||||
import socket from 'koa-socket'
|
|
||||||
import * as casparcg from './casparcg/client'
|
|
||||||
|
|
||||||
import config from '../config'
|
|
||||||
import log from '../log'
|
|
||||||
import onConnection from './routerio'
|
|
||||||
import { bunyanLogger, errorHandler } from './middlewares'
|
|
||||||
|
|
||||||
const app = new Koa()
|
|
||||||
const io = new socket()
|
|
||||||
|
|
||||||
io.attach(app)
|
|
||||||
|
|
||||||
io.on('connection', onConnection.bind(this, io))
|
|
||||||
|
|
||||||
casparcg.initialise(log, io).catch(e => {
|
|
||||||
log.error(e, 'Critical error initialising casparcg')
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use(bunyanLogger(log))
|
|
||||||
app.use(errorHandler())
|
|
||||||
app.use(async (ctx, next) => {
|
|
||||||
if (ctx.url === '/') {
|
|
||||||
return ctx.redirect('/index.html')
|
|
||||||
}
|
|
||||||
await next()
|
|
||||||
})
|
|
||||||
app.use(serve('./public', ''))
|
|
||||||
|
|
||||||
app.listen(config.get('server:port'), err => {
|
|
||||||
if (err) return log.critical(err)
|
|
||||||
log.info(`Server is listening on ${config.get('server:port')}`)
|
|
||||||
})
|
|
44
api/server.mjs
Normal file
44
api/server.mjs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import Koa from 'koa'
|
||||||
|
import serve from 'koa-better-serve'
|
||||||
|
import socket from 'koa-socket'
|
||||||
|
import * as casparcg from './casparcg/client.mjs'
|
||||||
|
|
||||||
|
import lowdb from './db.mjs'
|
||||||
|
import config from './config.mjs'
|
||||||
|
import log from './log.mjs'
|
||||||
|
import onConnection from './routerio.mjs'
|
||||||
|
import { bunyanLogger, errorHandler } from './middlewares.mjs'
|
||||||
|
|
||||||
|
log.info('Server: Opening database db.json')
|
||||||
|
|
||||||
|
lowdb().then(function(db) {
|
||||||
|
const app = new Koa()
|
||||||
|
const io = new socket()
|
||||||
|
|
||||||
|
io.attach(app)
|
||||||
|
|
||||||
|
io.on('connection', onConnection.bind(this, io, db))
|
||||||
|
|
||||||
|
casparcg.initialise(log, db, io)
|
||||||
|
|
||||||
|
app.use(bunyanLogger(log))
|
||||||
|
app.use(errorHandler())
|
||||||
|
app.use(async (ctx, next) => {
|
||||||
|
if (ctx.url === '/') {
|
||||||
|
return ctx.redirect('/index.html')
|
||||||
|
}
|
||||||
|
await next()
|
||||||
|
})
|
||||||
|
app.use(serve('./public', ''))
|
||||||
|
|
||||||
|
app.listen(config.get('server:port'), err => {
|
||||||
|
if (err) return log.fatal(err)
|
||||||
|
log.info(`Server is listening on ${config.get('server:port')}`)
|
||||||
|
})
|
||||||
|
}, function(e) {
|
||||||
|
log.fatal(e, 'Critical error loading database')
|
||||||
|
process.exit(1)
|
||||||
|
}).catch(function(e) {
|
||||||
|
log.fatal(e, 'Critical error starting server')
|
||||||
|
process.exit(1)
|
||||||
|
})
|
|
@ -1,51 +0,0 @@
|
||||||
import bookshelf from '../bookshelf'
|
|
||||||
|
|
||||||
/* Settings model:
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Settings = bookshelf.createModel({
|
|
||||||
tableName: 'settings',
|
|
||||||
}, {
|
|
||||||
getValue(name) {
|
|
||||||
return this.query({ where: { name: name } })
|
|
||||||
.fetch({ require: false })
|
|
||||||
.then(item => item && item.get('value') || item)
|
|
||||||
},
|
|
||||||
|
|
||||||
setValue(name, value) {
|
|
||||||
return this.query({ where: { name } })
|
|
||||||
.fetch({ require: false })
|
|
||||||
.then(item => {
|
|
||||||
if (item) {
|
|
||||||
item.set({ value })
|
|
||||||
return item.save()
|
|
||||||
}
|
|
||||||
return this.create({
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
is_deleted: false,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
getSettings() {
|
|
||||||
return this.query({ where: { }})
|
|
||||||
.fetchAll({ })
|
|
||||||
.then(data => {
|
|
||||||
let out = { }
|
|
||||||
|
|
||||||
data.forEach(item => {
|
|
||||||
out[item.get('name')] = item.get('value')
|
|
||||||
})
|
|
||||||
|
|
||||||
return out
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export default Settings
|
|
|
@ -1,5 +1,4 @@
|
||||||
import Settings from './model'
|
import { connect } from '../casparcg/client.mjs'
|
||||||
import { connect } from '../casparcg/client'
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Event: 'settings.all'
|
* Event: 'settings.all'
|
||||||
|
@ -7,7 +6,7 @@ import { connect } from '../casparcg/client'
|
||||||
* Request all settings in store
|
* Request all settings in store
|
||||||
*/
|
*/
|
||||||
export async function all(ctx) {
|
export async function all(ctx) {
|
||||||
let data = await Settings.getSettings()
|
let data = ctx.db.get('settings').value()
|
||||||
|
|
||||||
ctx.socket.emit('settings.all', data)
|
ctx.socket.emit('settings.all', data)
|
||||||
}
|
}
|
27
api/setup.mjs
Normal file
27
api/setup.mjs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import knex from 'knex'
|
||||||
|
import defaults from './defaults.mjs'
|
||||||
|
import config from './config.mjs'
|
||||||
|
import log from './log.mjs'
|
||||||
|
|
||||||
|
// This is important for setup to run cleanly.
|
||||||
|
let knexConfig = defaults(config.get('knex'), null) // Clone
|
||||||
|
knexConfig.pool = { min: 1, max: 1 }
|
||||||
|
|
||||||
|
let knexSetup = knex(knexConfig)
|
||||||
|
|
||||||
|
export default function setup() {
|
||||||
|
log.info(knexConfig, 'Running database integrity scan.')
|
||||||
|
|
||||||
|
return knexSetup.migrate.latest({
|
||||||
|
directory: './migrations',
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
if (result[1].length === 0) {
|
||||||
|
return log.info('Database is up to date')
|
||||||
|
}
|
||||||
|
for (let i = 0; i < result[1].length; i++) {
|
||||||
|
log.info('Applied migration from', result[1][i].substr(result[1][i].lastIndexOf('\\') + 1))
|
||||||
|
}
|
||||||
|
return knexSetup.destroy()
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
var socket = require('../socket')
|
var socket = require('../shared/socket')
|
||||||
|
|
||||||
var engines = {
|
var engines = {
|
||||||
text: require('./text'),
|
text: require('./text'),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const m = require('mithril')
|
const m = require('mithril')
|
||||||
const createModule = require('../common/module')
|
const createModule = require('../common/module')
|
||||||
const components = require('../common/components')
|
const components = require('../common/components')
|
||||||
const socket = require('../../socket')
|
const socket = require('../../shared/socket')
|
||||||
const store = require('../store')
|
const store = require('../store')
|
||||||
|
|
||||||
const Add = createModule({
|
const Add = createModule({
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const m = require('mithril')
|
const m = require('mithril')
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const store = require('../store')
|
const store = require('../store')
|
||||||
const socket = require('../../socket')
|
const socket = require('../../shared/socket')
|
||||||
const dragula = require('dragula')
|
const dragula = require('dragula')
|
||||||
|
|
||||||
function createModule(component, view) {
|
function createModule(component, view) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const m = require('mithril')
|
const m = require('mithril')
|
||||||
const createModule = require('../common/module')
|
const createModule = require('../common/module')
|
||||||
const socket = require('../../socket')
|
const socket = require('../../shared/socket')
|
||||||
|
|
||||||
const Dagskra = createModule({
|
const Dagskra = createModule({
|
||||||
init: function() {
|
init: function() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const m = require('mithril')
|
const m = require('mithril')
|
||||||
const createModule = require('../common/module')
|
const createModule = require('../common/module')
|
||||||
const socket = require('../../socket')
|
const socket = require('../../shared/socket')
|
||||||
const view = require('./view')
|
const view = require('./view')
|
||||||
const dragula = require('dragula')
|
const dragula = require('dragula')
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const m = require('mithril')
|
const m = require('mithril')
|
||||||
const createModule = require('./common/module')
|
const createModule = require('./common/module')
|
||||||
const socket = require('../socket')
|
const socket = require('../shared/socket')
|
||||||
|
|
||||||
const Header = createModule({
|
const Header = createModule({
|
||||||
init: function() {
|
init: function() {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
//in the console.
|
//in the console.
|
||||||
window.components = {}
|
window.components = {}
|
||||||
|
|
||||||
require('../socket')
|
require('../shared/socket')
|
||||||
require('./store')
|
require('./store')
|
||||||
|
|
||||||
const m = require('mithril')
|
const m = require('mithril')
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const m = require('mithril')
|
const m = require('mithril')
|
||||||
const createModule = require('./common/module')
|
const createModule = require('./common/module')
|
||||||
const socket = require('../socket')
|
const socket = require('../shared/socket')
|
||||||
|
|
||||||
const Menu = createModule({
|
const Menu = createModule({
|
||||||
init: function() {
|
init: function() {
|
||||||
|
@ -22,7 +22,7 @@ const Menu = createModule({
|
||||||
|
|
||||||
saveNewHost() {
|
saveNewHost() {
|
||||||
socket.emit('settings.update', {
|
socket.emit('settings.update', {
|
||||||
name: 'casparcg',
|
name: 'casparhost',
|
||||||
value: this.newHost,
|
value: this.newHost,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ const Menu = createModule({
|
||||||
m('h5.header.header--space', 'CasparCG Status'),
|
m('h5.header.header--space', 'CasparCG Status'),
|
||||||
m('input[type=text]', {
|
m('input[type=text]', {
|
||||||
placeholder: 'Host IP',
|
placeholder: 'Host IP',
|
||||||
value: this.newHost || this.settings.casparcg || '',
|
value: this.newHost || this.settings.casparhost || '',
|
||||||
oninput: control => this.setHost(control.target.value),
|
oninput: control => this.setHost(control.target.value),
|
||||||
}),
|
}),
|
||||||
this.enableEdit && m('button', {
|
this.enableEdit && m('button', {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const socket = require('../socket')
|
const socket = require('../shared/socket')
|
||||||
const storage = {}
|
const storage = {}
|
||||||
const events = {}
|
const events = {}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const socket = require('../socket')
|
const socket = require('../shared/socket')
|
||||||
const m = require('mithril')
|
const m = require('mithril')
|
||||||
|
|
||||||
const Status = {
|
const Status = {
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
* {
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
box-sizing:border-box; /* This sets all elements to be the actual set dimensions, disregarding padding and borders */
|
|
||||||
/* -webkit-backface-visibility: hidden; */ /* Hide the backface of elements - useful for 3d effects */
|
|
||||||
-webkit-transition: translate3d(0,0,0); /* Turns on hardware acceleration - not known to be of benefit in CCG, but won't hurt */
|
|
||||||
}
|
|
||||||
html, body {
|
|
||||||
width:1920px; /* Set to your channel's resolution */
|
|
||||||
height:1080px; /* Set to your channel's resolution */
|
|
||||||
margin:0; /* Use all available space */
|
|
||||||
padding:0; /* Use all available space */
|
|
||||||
background:transparent; /* The HTML consumer actually makes your background transparent by default, unless a color or image is specified - but this might be usefull when debugging in browsers */
|
|
||||||
overflow:hidden; /* Hide any overflowing elements - to disable scollbars */
|
|
||||||
-webkit-font-smoothing:antialiased !important; /* Set aliasing of fonts - possible options: none, antialiased, subpixel-antialiased */
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: Calibri,Arial;
|
|
||||||
font-size: 40px;
|
|
||||||
color: #FFFFFF;
|
|
||||||
/* -webkit-text-stroke-width: 0.5px;
|
|
||||||
-webkit-text-stroke-color: #888888;
|
|
||||||
text-shadow: 2px 2px 1px #000000; */
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: Arial;
|
|
||||||
font-weight: normal;
|
|
||||||
/* text-shadow: 0px 0px 0px #000000; */
|
|
||||||
font-size: 22pt;
|
|
||||||
}
|
|
||||||
html {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
body > div
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.root-element {
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.root-element-display {
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity 1s;
|
|
||||||
}
|
|
|
@ -1,566 +0,0 @@
|
||||||
/* http://meyerweb.com/eric/tools/css/reset/
|
|
||||||
v2.0 | 20110126
|
|
||||||
License: none (public domain)
|
|
||||||
*/
|
|
||||||
|
|
||||||
html, body, div, span, applet, object, iframe,
|
|
||||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
|
||||||
a, abbr, acronym, address, big, cite, code,
|
|
||||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
|
||||||
small, strike, strong, sub, sup, tt, var,
|
|
||||||
b, u, i, center,
|
|
||||||
dl, dt, dd, ol, ul, li,
|
|
||||||
fieldset, form, label, legend,
|
|
||||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
|
||||||
article, aside, canvas, details, embed,
|
|
||||||
figure, figcaption, footer, header, hgroup,
|
|
||||||
menu, nav, output, ruby, section, summary,
|
|
||||||
time, mark, audio, video {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
font: inherit;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
/* HTML5 display-role reset for older browsers */
|
|
||||||
article, aside, details, figcaption, figure,
|
|
||||||
footer, header, hgroup, menu, nav, section {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
ol, ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
blockquote, q {
|
|
||||||
quotes: none;
|
|
||||||
}
|
|
||||||
blockquote:before, blockquote:after,
|
|
||||||
q:before, q:after {
|
|
||||||
content: '';
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
font-size: 16px;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: #3f3f41;
|
|
||||||
color: #f1f1f1;
|
|
||||||
display: flex;
|
|
||||||
min-height: 100vh;
|
|
||||||
flex-direction: column;
|
|
||||||
font-family: Helvetica, sans-serif, Arial;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Components */
|
|
||||||
|
|
||||||
button {
|
|
||||||
border: none;
|
|
||||||
color: #f1f1f1;
|
|
||||||
background: #2199e8;
|
|
||||||
font-size: 0.6em;
|
|
||||||
height: 3em;
|
|
||||||
|
|
||||||
&.green {
|
|
||||||
background: #3adb78;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.red {
|
|
||||||
background: #ec5840;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-box {
|
|
||||||
margin: 1rem 0rem 2rem 0;
|
|
||||||
padding: 1rem;
|
|
||||||
background: #FF0000;
|
|
||||||
color: white;
|
|
||||||
font-size: 0.7em;
|
|
||||||
line-height: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
$header-size = 0.8em;
|
|
||||||
$header-color = #777777;
|
|
||||||
|
|
||||||
/* Container */
|
|
||||||
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
flex-grow: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Header */
|
|
||||||
section.current {
|
|
||||||
padding: 0 13px;
|
|
||||||
background: black;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
z-index: 10;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
color: $header-color;
|
|
||||||
font-size: 0.7em;
|
|
||||||
padding: 0.2em;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1em;
|
|
||||||
line-height: 2em;
|
|
||||||
color: #eb6e00;
|
|
||||||
flex-grow: 2;
|
|
||||||
height: 2em;
|
|
||||||
padding-right: 0.5em;
|
|
||||||
overflow: hidden;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 80px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.disconnected {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: rgba(0,0,0,0.8);
|
|
||||||
color: white;
|
|
||||||
font-size: 1em;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Menu */
|
|
||||||
|
|
||||||
nav {
|
|
||||||
width: 200px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
padding: 10px;
|
|
||||||
background: #2d2d30;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
color: $header-color;
|
|
||||||
font-size: $header-size;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
&--space {
|
|
||||||
margin-top: 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-size: $header-size;
|
|
||||||
line-height: 2.6em;
|
|
||||||
display: block;
|
|
||||||
border: 4px solid #2d2d30;
|
|
||||||
background: #007acc;
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background: transparent;
|
|
||||||
border: 4px solid #007acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border: 4px solid #007acc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text] {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status {
|
|
||||||
padding: 5px 20px;
|
|
||||||
font-size: 0.8em;
|
|
||||||
color: $header-color;
|
|
||||||
text-align: left;
|
|
||||||
position: relative;
|
|
||||||
margin-left: 1.8em;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: calc(50% - 5px);
|
|
||||||
content: '';
|
|
||||||
border: 6px solid #ec5840;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.green::after {
|
|
||||||
border-color: #008000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Main */
|
|
||||||
|
|
||||||
main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
padding: 10px 1em;
|
|
||||||
flex-grow: 2;
|
|
||||||
width: 300px;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
color: $header-color;
|
|
||||||
font-size: $header-size;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inputs */
|
|
||||||
|
|
||||||
label {
|
|
||||||
margin-top: 0.6em;
|
|
||||||
color: #f1f1f1;
|
|
||||||
font-size: 0.7em;
|
|
||||||
|
|
||||||
& a,
|
|
||||||
& a:hover,
|
|
||||||
& a:visited {
|
|
||||||
color: #aaa;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type='text'],
|
|
||||||
textarea,
|
|
||||||
select {
|
|
||||||
font-size: 0.6em;
|
|
||||||
padding: 0.5em;
|
|
||||||
margin: 0.5em 0;
|
|
||||||
background: #333337;
|
|
||||||
border: 1px solid #2d2d30;
|
|
||||||
color: #999999;
|
|
||||||
transition-property: none !important;
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #f1f1f1;
|
|
||||||
border-color: #007acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
background: #333337;
|
|
||||||
color: #f1f1f1;
|
|
||||||
border-color: #007acc;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea#graphic-html {
|
|
||||||
min-height: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea#graphic-css {
|
|
||||||
min-height: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=submit] {
|
|
||||||
margin-top: 0.6em;
|
|
||||||
border: none;
|
|
||||||
color: #f1f1f1;
|
|
||||||
background: #2199e8;
|
|
||||||
font-size: 0.6em;
|
|
||||||
line-height: 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[readonly],
|
|
||||||
input[readonly]:hover {
|
|
||||||
background: #2d2d30 !important;
|
|
||||||
border-color: #3f3f3f;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
height: 2.5em;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
border-radius: 0;
|
|
||||||
background-position: right center;
|
|
||||||
background-size: 9px 6px;
|
|
||||||
background-origin: content-box;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='32' height='24' viewBox='0 0 32 24'><polygon points='0,0 32,0 16,24' style='fill: rgb%28138, 138, 138%29'></polygon></svg>")
|
|
||||||
}
|
|
||||||
|
|
||||||
select:hover {
|
|
||||||
color: #f1f1f1;
|
|
||||||
border-color: #007acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
select:focus {
|
|
||||||
background: #333337;
|
|
||||||
color: #f1f1f1;
|
|
||||||
border-color: #007acc;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.button {
|
|
||||||
margin: 0 1rem 0 0;
|
|
||||||
width: 7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Graphic */
|
|
||||||
|
|
||||||
header {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1em;
|
|
||||||
flex-grow: 2;
|
|
||||||
border-bottom: 1px solid #2d2d30;
|
|
||||||
padding-top: 10px;
|
|
||||||
margin-right: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
border: 0;
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.graphic {
|
|
||||||
&-presetlist {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-presetadd {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
border: 1px solid #2d2d30;
|
|
||||||
margin: 30px 0 10px;
|
|
||||||
padding: 20px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&-header {
|
|
||||||
background: #3f3f41;
|
|
||||||
position: absolute;
|
|
||||||
top: -1.3em;
|
|
||||||
left: 10px;
|
|
||||||
font-size: 0.8em;
|
|
||||||
padding: 0.8em 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-buttons {
|
|
||||||
display: flex;
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
& button {
|
|
||||||
margin-right: 10px;
|
|
||||||
width: 150px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-presetremove {
|
|
||||||
align-self: center;
|
|
||||||
margin-top: 50px;
|
|
||||||
width: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-empty {
|
|
||||||
font-size: 0.7em;
|
|
||||||
color: #999;
|
|
||||||
text-align: center;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-delete {
|
|
||||||
align-self: center;
|
|
||||||
margin-top: 30px;
|
|
||||||
width: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-label {
|
|
||||||
margin-top: 30px;
|
|
||||||
padding-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-helper {
|
|
||||||
font-size: 0.7em;
|
|
||||||
color: #999;
|
|
||||||
margin: 5px 0 0;
|
|
||||||
|
|
||||||
&.bottom {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-property,
|
|
||||||
&-preset {
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
|
|
||||||
&-reorder {
|
|
||||||
width: 62px;
|
|
||||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODQ5NDcyMzY3MTU5MTFFOEEwQjVFQUFCMEYzRDE2QjciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODQ5NDcyMzc3MTU5MTFFOEEwQjVFQUFCMEYzRDE2QjciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4NDk0NzIzNDcxNTkxMUU4QTBCNUVBQUIwRjNEMTZCNyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4NDk0NzIzNTcxNTkxMUU4QTBCNUVBQUIwRjNEMTZCNyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pla8e2wAAABFUExURXd3d////3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d0brzhMAAAAWdFJOUwAAN0NEVXJzdHV2d3h/iImKmaq73e5w9SzPAAAAlElEQVRIx+XT2wqDMBRE0Witt2piW53//9SCoETMwJkXkWY/CgtNBp0jDa/086JgwgNeIwEghpFVAMFOJoCaNNkFMNlIJBImRcrPodJ8ybQ/I285B7mLSCWX+/qL3EVTznK5T/mUu++P7OVyX/9U9T30sLyliUeobR8WmcZ6lnYTrf34HRX8xlbTaZfcA726yziyXX5Joj0NgTfsUQAAAABJRU5ErkJggg==') no-repeat transparent;
|
|
||||||
background-size: 25px;
|
|
||||||
background-position: center;
|
|
||||||
touch-action: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
& input {
|
|
||||||
flex-grow: 2;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
& button {
|
|
||||||
width: 100px;
|
|
||||||
border: 1px solid #3f3f41;
|
|
||||||
border-left: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-preset {
|
|
||||||
& input {
|
|
||||||
padding-top: 1.5em;
|
|
||||||
padding-bottom: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
& button {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.schedule {
|
|
||||||
&-empty {
|
|
||||||
margin-top: 2em;
|
|
||||||
font-size: 1em;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings {
|
|
||||||
&-empty {
|
|
||||||
text-align: center;
|
|
||||||
margin: 50px 0 30px;
|
|
||||||
font-size: 0.8em;
|
|
||||||
color: #999;
|
|
||||||
|
|
||||||
&-button {
|
|
||||||
align-self: center;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dragula */
|
|
||||||
@css {
|
|
||||||
#dragcontainer {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gu-mirror {
|
|
||||||
position: absolute !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
z-index: 9999 !important;
|
|
||||||
opacity: 0.8;
|
|
||||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
|
|
||||||
filter: alpha(opacity=80);
|
|
||||||
}
|
|
||||||
.gu-hide {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
.gu-unselectable {
|
|
||||||
-webkit-user-select: none !important;
|
|
||||||
-moz-user-select: none !important;
|
|
||||||
-ms-user-select: none !important;
|
|
||||||
user-select: none !important;
|
|
||||||
}
|
|
||||||
.gu-transit {
|
|
||||||
opacity: 0.2;
|
|
||||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
|
|
||||||
filter: alpha(opacity=20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Media queries */
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-device-width: 600px) {
|
|
||||||
#container {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
width: auto;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
width: calc(50% - 8px);
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text] {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status {
|
|
||||||
align-self: center;
|
|
||||||
width: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,168 +0,0 @@
|
||||||
/* http://meyerweb.com/eric/tools/css/reset/
|
|
||||||
v2.0 | 20110126
|
|
||||||
License: none (public domain)
|
|
||||||
*/
|
|
||||||
|
|
||||||
html, body, div, span, applet, object, iframe,
|
|
||||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
|
||||||
a, abbr, acronym, address, big, cite, code,
|
|
||||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
|
||||||
small, strike, strong, sub, sup, tt, var,
|
|
||||||
b, u, i, center,
|
|
||||||
dl, dt, dd, ol, ul, li,
|
|
||||||
fieldset, form, label, legend,
|
|
||||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
|
||||||
article, aside, canvas, details, embed,
|
|
||||||
figure, figcaption, footer, header, hgroup,
|
|
||||||
menu, nav, output, ruby, section, summary,
|
|
||||||
time, mark, audio, video {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
font: inherit;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
/* HTML5 display-role reset for older browsers */
|
|
||||||
article, aside, details, figcaption, figure,
|
|
||||||
footer, header, hgroup, menu, nav, section {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
ol, ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
blockquote, q {
|
|
||||||
quotes: none;
|
|
||||||
}
|
|
||||||
blockquote:before, blockquote:after,
|
|
||||||
q:before, q:after {
|
|
||||||
content: '';
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
font-size: 16px;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: #3f3f41;
|
|
||||||
color: #eb6e00;
|
|
||||||
display: flex;
|
|
||||||
min-height: 100vh;
|
|
||||||
flex-direction: column;
|
|
||||||
font-family: Helvetica, sans-serif, Arial;
|
|
||||||
}
|
|
||||||
|
|
||||||
$header-size = 2.3em;
|
|
||||||
|
|
||||||
#container {
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-grow: 2;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
background: black;
|
|
||||||
font-size: $header-size;
|
|
||||||
line-height: ($header-size);
|
|
||||||
color: #eb6e00;
|
|
||||||
height: ($header-size);
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 0 0.3em;
|
|
||||||
flex-grow: 2;
|
|
||||||
border-radius: 5px 0 0 5px;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
border: none;
|
|
||||||
color: black;
|
|
||||||
background: #eb6e00;
|
|
||||||
font-size: 2em;
|
|
||||||
width: 80px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
border-radius: 0 5px 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
|
||||||
display: flex;
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
|
||||||
flex-grow: 2;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: #ccc;
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
color: #ccc;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 3em;
|
|
||||||
line-height: 64px;
|
|
||||||
padding: 0 0.3em;
|
|
||||||
flex-grow: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status {
|
|
||||||
padding: 0 20px 0 30px;
|
|
||||||
line-height: 64px;
|
|
||||||
font-size: 2em;
|
|
||||||
color: $header-color;
|
|
||||||
text-align: left;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: calc(50% - 7px);
|
|
||||||
content: '';
|
|
||||||
border: 10px solid #ec5840;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.green::after {
|
|
||||||
border-color: #00FF00;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.disconnected {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: rgba(0,0,0,0.8);
|
|
||||||
color: white;
|
|
||||||
font-size: 3em;
|
|
||||||
display: flex;
|
|
||||||
text-align: center;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
187
db.json
Normal file
187
db.json
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
{
|
||||||
|
"graphics": [
|
||||||
|
{
|
||||||
|
"name": "Nidurteljari",
|
||||||
|
"engine": "countdown",
|
||||||
|
"settings": {
|
||||||
|
"html": "<%- text %><br><span id=\"Nidurteljari-countdown-timer\">countdown appears here</span>",
|
||||||
|
"main": "text",
|
||||||
|
"text": "Sunnudagssamkoman hefst klukkan 11:00",
|
||||||
|
"countdown": "2018-07-01 11:00",
|
||||||
|
"finished": "Skamma stund",
|
||||||
|
"css": "#Nidurteljari {\n position: absolute;\n font: 2em \"Berthold Akzidenz Grotesk BE\";\n top: 830px;\n left: 230px;\n width: 1450px;\n padding-top: 20px;\n border-top: 1px solid #095376;\n text-align: center;\n color: #095376;\n font-size: 60pt;\n line-height: 70pt;\n font-weight: bold;\n}\n#Nidurteljari-countdown-timer {\n font-size: 80pt;\n}"
|
||||||
|
},
|
||||||
|
"id": 1586112780891
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nafn",
|
||||||
|
"engine": "text",
|
||||||
|
"settings": {
|
||||||
|
"properties": [
|
||||||
|
"nafn",
|
||||||
|
"titill"
|
||||||
|
],
|
||||||
|
"main": "<%- nafn %>",
|
||||||
|
"html": "<div class=\"outer\">\n<div class=\"inside\">\n<h2><%- nafn %></h2>\n<h4><%- titill %></h4>\n</div>\n</div>",
|
||||||
|
"css": "#Nafn {\nposition: absolute;\nbottom: 50px;\nleft: 0px;\nwidth: 100%;\nright: 0;\nbackground: transparent;\ncolor: black;\nfont-family: Raleway;\ndisplay: flex;\njustify-content: center;\n}\n\n#Nafn .outer {\nborder: 4px solid white;\npadding: 6px;\n}\n\n#Nafn .inside {\npadding: 8px 24px;\nbackground: white;\ndisplay: flex;\nflex-direction: column;\n}\n\n#Nafn .inside h2 {\nfont-weight: bold;\nfont-size: 40px;\ntext-align: center;\ntext-transform: uppercase;\npadding: 0;\nmargin: 0;\nline-height: 120%;\n}\n\n#Nafn .inside h4 {\nfont-weight: normal;\nfont-size: 18px;\npadding: 0;\nmargin: 0;\ntext-align: center;\nline-height: 125%;\ntext-transform: uppercase;\n}"
|
||||||
|
},
|
||||||
|
"id": 1586112780892
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Minnisvers",
|
||||||
|
"engine": "text",
|
||||||
|
"settings": {
|
||||||
|
"properties": [
|
||||||
|
"bok",
|
||||||
|
"vers"
|
||||||
|
],
|
||||||
|
"main": "bok",
|
||||||
|
"css": "#Minnisvers {\n position: absolute;\n font: 2em \"Berthold Akzidenz Grotesk BE\";\n top: 850px;\n right: 0px;\n}\n.bok-holder {\n background: rgba(0,68,105,0.6);\n line-height: 60px;\n font-size: 60px;\n color: white;\n padding: 10px 60px 0px 30px;\n}\n.vers-holder {\n background: rgba(33,29,29,0.6);\n font-size: 30px;\n line-height: 30px;\n color: white;\n padding: 10px 60px 0px 40px;\n}",
|
||||||
|
"html": "<div class=\"bok-holder\"><%- bok %></div>\n<div class=\"vers-holder\"><%- vers %></div>"
|
||||||
|
},
|
||||||
|
"id": 1586112780893
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Forn",
|
||||||
|
"engine": "text",
|
||||||
|
"settings": {
|
||||||
|
"properties": [
|
||||||
|
"Titill",
|
||||||
|
"Text"
|
||||||
|
],
|
||||||
|
"main": "<%- Titill %> - <%- Text %>",
|
||||||
|
"css": "#Forn {\nposition: absolute;\ntop: 50px;\nleft: 95px;\nborder: 4px solid white;\npadding: 6px;\nbackground: transparent;\ncolor: black;\nfont-family: Raleway;\n}\n\n#Forn .inside {\npadding: 6px 24px;\nbackground: white;\ndisplay: flex;\nflex-direction: column;\n}\n\n#Forn .inside h2 {\nfont-weight: bold;\nfont-size: 24px;\ntext-align: center;\ntext-transform: uppercase;\npadding: 0;\nmargin: 0;\n}\n\n#Forn .inside h4 {\nfont-weight: normal;\nfont-size: 18px;\npadding: 0;\nmargin: 0;\ntext-align: center;\nline-height: 125%;\n}",
|
||||||
|
"html": "<div class=\"inside\">\n<h2><%- Titill %></h2>\n<h4><%= Text.replace('\\n', '<br>') %></h4>\n</div>"
|
||||||
|
},
|
||||||
|
"id": 1586112780894
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Lög",
|
||||||
|
"engine": "text",
|
||||||
|
"settings": {
|
||||||
|
"properties": [
|
||||||
|
"nafn"
|
||||||
|
],
|
||||||
|
"html": "<div class=\"box-before\"></div>\n<div class=\"box-logo\"></div>\n<div class=\"box-after\"></div>\n<div class=\"box-name\">\n <div class=\"holder\">\n <div class=\"name-holder\"><%- nafn %>\n <div class=\"name-triangle\"></div><div class=\"titill-triangle\"></div>\n </div>\n </div>\n</div>",
|
||||||
|
"css": ".box-before {\n margin-bottom: 15px;\n width: 160px;\n height: 112px;\n margin-right: -37px;\n /* ---------- THEME COLOR ---------- */\n background-image: -webkit-radial-gradient(200px 50%, circle, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 0) 75px, rgba(0,68,105,1) 77px);\n /* ---------- END THEME COLOR ---------- */\n}\n.box-after {\n /* ---------- THEME COLOR ---------- */\n background-image: -webkit-radial-gradient(-40px 50%, circle, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 0) 75px, rgba(0,68,105,1) 77px);\n /* ---------- END THEME COLOR ---------- */\n margin-bottom: 15px;\n width: 100px;\n height: 112px;\n margin-left: -37px;\n}\n\n#Lög {\n position: absolute;\n color: white;\n font: 2em \"Berthold Akzidenz Grotesk BE\";\n top: 100px;\n left: 0px;\n}\n\n#Lög > div { display: inline-block; }\n\n.box-logo {\n width: 141px;\n height: 141px;\n background: url('uploads/logo.png') 0px 0px no-repeat;\n background-size: contain;\n}\n\n.box-name {\n margin-bottom: 15px;\n height: 112px;\n position: relative;\n margin-left: -6px;\n width: 700px;\n}\n.holder { position: absolute; }\n.name-holder {\n position: relative;\n background: linear-gradient(to right, rgba(207,209,210,0.6) 0%,rgba(235,235,237,0.6) 50%,rgba(183,185,185,0.6) 100%);\n line-height: 102px;\n font-size: 60px;\n color: rgba(0,68,105,1);\n padding: 10px 30px 0px 20px;\n}\n.name-triangle {\n position: absolute;\n right: -112px;\n border-width: 0 112px 112px 0px;\n top: 0;\n border-style: solid;\n border-color: transparent transparent rgba(183,185,185,0.6) transparent;\n}",
|
||||||
|
"main": "nafn"
|
||||||
|
},
|
||||||
|
"id": 1586112780895
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Dagskra",
|
||||||
|
"engine": "schedule",
|
||||||
|
"settings": {
|
||||||
|
"properties": [
|
||||||
|
"temp"
|
||||||
|
],
|
||||||
|
"textfields": [
|
||||||
|
"item1"
|
||||||
|
],
|
||||||
|
"main": "temp"
|
||||||
|
},
|
||||||
|
"id": 1586112780896
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"presets": [
|
||||||
|
{
|
||||||
|
"graphic_id": 1586112780892,
|
||||||
|
"values": {
|
||||||
|
"nafn": "Bla",
|
||||||
|
"titill": "test"
|
||||||
|
},
|
||||||
|
"sort": 1,
|
||||||
|
"id": 1586205588587
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"graphic_id": 1586112780892,
|
||||||
|
"values": {
|
||||||
|
"nafn": "Bla 2",
|
||||||
|
"titill": "test 2"
|
||||||
|
},
|
||||||
|
"sort": 2,
|
||||||
|
"id": 1586205644890
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"playing": [
|
||||||
|
{
|
||||||
|
"graphic": {
|
||||||
|
"name": "Nafn",
|
||||||
|
"engine": "text",
|
||||||
|
"settings": {
|
||||||
|
"properties": [
|
||||||
|
"nafn",
|
||||||
|
"titill"
|
||||||
|
],
|
||||||
|
"main": "<%- nafn %>",
|
||||||
|
"html": "<div class=\"outer\">\n<div class=\"inside\">\n<h2><%- nafn %></h2>\n<h4><%- titill %></h4>\n</div>\n</div>",
|
||||||
|
"css": "#Nafn {\nposition: absolute;\nbottom: 50px;\nleft: 0px;\nwidth: 100%;\nright: 0;\nbackground: transparent;\ncolor: black;\nfont-family: Raleway;\ndisplay: flex;\njustify-content: center;\n}\n\n#Nafn .outer {\nborder: 4px solid white;\npadding: 6px;\n}\n\n#Nafn .inside {\npadding: 8px 24px;\nbackground: white;\ndisplay: flex;\nflex-direction: column;\n}\n\n#Nafn .inside h2 {\nfont-weight: bold;\nfont-size: 40px;\ntext-align: center;\ntext-transform: uppercase;\npadding: 0;\nmargin: 0;\nline-height: 120%;\n}\n\n#Nafn .inside h4 {\nfont-weight: normal;\nfont-size: 18px;\npadding: 0;\nmargin: 0;\ntext-align: center;\nline-height: 125%;\ntext-transform: uppercase;\n}"
|
||||||
|
},
|
||||||
|
"id": 1586112780892
|
||||||
|
},
|
||||||
|
"name": "Nafn",
|
||||||
|
"html": "<div class=\"outer\">\n<div class=\"inside\">\n<h2>Bla 2</h2>\n<h4>test 2</h4>\n</div>\n</div>",
|
||||||
|
"css": "#Nafn {\nposition: absolute;\nbottom: 50px;\nleft: 0px;\nwidth: 100%;\nright: 0;\nbackground: transparent;\ncolor: black;\nfont-family: Raleway;\ndisplay: flex;\njustify-content: center;\n}\n\n#Nafn .outer {\nborder: 4px solid white;\npadding: 6px;\n}\n\n#Nafn .inside {\npadding: 8px 24px;\nbackground: white;\ndisplay: flex;\nflex-direction: column;\n}\n\n#Nafn .inside h2 {\nfont-weight: bold;\nfont-size: 40px;\ntext-align: center;\ntext-transform: uppercase;\npadding: 0;\nmargin: 0;\nline-height: 120%;\n}\n\n#Nafn .inside h4 {\nfont-weight: normal;\nfont-size: 18px;\npadding: 0;\nmargin: 0;\ntext-align: center;\nline-height: 125%;\ntext-transform: uppercase;\n}",
|
||||||
|
"data": {
|
||||||
|
"nafn": "Bla 2",
|
||||||
|
"titill": "test 2"
|
||||||
|
},
|
||||||
|
"is_deleted": false,
|
||||||
|
"id": 1586213076185
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"schedule": [
|
||||||
|
{
|
||||||
|
"graphic_id": 1586112780892,
|
||||||
|
"values": {
|
||||||
|
"nafn": "Bla 2",
|
||||||
|
"titill": "test 2"
|
||||||
|
},
|
||||||
|
"sort": 1,
|
||||||
|
"id": 1586213073361,
|
||||||
|
"graphic": {
|
||||||
|
"name": "Nafn",
|
||||||
|
"engine": "text",
|
||||||
|
"settings": {
|
||||||
|
"properties": [
|
||||||
|
"nafn",
|
||||||
|
"titill"
|
||||||
|
],
|
||||||
|
"main": "<%- nafn %>",
|
||||||
|
"html": "<div class=\"outer\">\n<div class=\"inside\">\n<h2><%- nafn %></h2>\n<h4><%- titill %></h4>\n</div>\n</div>",
|
||||||
|
"css": "#Nafn {\nposition: absolute;\nbottom: 50px;\nleft: 0px;\nwidth: 100%;\nright: 0;\nbackground: transparent;\ncolor: black;\nfont-family: Raleway;\ndisplay: flex;\njustify-content: center;\n}\n\n#Nafn .outer {\nborder: 4px solid white;\npadding: 6px;\n}\n\n#Nafn .inside {\npadding: 8px 24px;\nbackground: white;\ndisplay: flex;\nflex-direction: column;\n}\n\n#Nafn .inside h2 {\nfont-weight: bold;\nfont-size: 40px;\ntext-align: center;\ntext-transform: uppercase;\npadding: 0;\nmargin: 0;\nline-height: 120%;\n}\n\n#Nafn .inside h4 {\nfont-weight: normal;\nfont-size: 18px;\npadding: 0;\nmargin: 0;\ntext-align: center;\nline-height: 125%;\ntext-transform: uppercase;\n}"
|
||||||
|
},
|
||||||
|
"id": 1586112780892
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"graphic_id": 1586112780892,
|
||||||
|
"values": {
|
||||||
|
"nafn": "Bla",
|
||||||
|
"titill": "test"
|
||||||
|
},
|
||||||
|
"sort": 2,
|
||||||
|
"id": 1586213074043,
|
||||||
|
"graphic": {
|
||||||
|
"name": "Nafn",
|
||||||
|
"engine": "text",
|
||||||
|
"settings": {
|
||||||
|
"properties": [
|
||||||
|
"nafn",
|
||||||
|
"titill"
|
||||||
|
],
|
||||||
|
"main": "<%- nafn %>",
|
||||||
|
"html": "<div class=\"outer\">\n<div class=\"inside\">\n<h2><%- nafn %></h2>\n<h4><%- titill %></h4>\n</div>\n</div>",
|
||||||
|
"css": "#Nafn {\nposition: absolute;\nbottom: 50px;\nleft: 0px;\nwidth: 100%;\nright: 0;\nbackground: transparent;\ncolor: black;\nfont-family: Raleway;\ndisplay: flex;\njustify-content: center;\n}\n\n#Nafn .outer {\nborder: 4px solid white;\npadding: 6px;\n}\n\n#Nafn .inside {\npadding: 8px 24px;\nbackground: white;\ndisplay: flex;\nflex-direction: column;\n}\n\n#Nafn .inside h2 {\nfont-weight: bold;\nfont-size: 40px;\ntext-align: center;\ntext-transform: uppercase;\npadding: 0;\nmargin: 0;\nline-height: 120%;\n}\n\n#Nafn .inside h4 {\nfont-weight: normal;\nfont-size: 18px;\npadding: 0;\nmargin: 0;\ntext-align: center;\nline-height: 125%;\ntext-transform: uppercase;\n}"
|
||||||
|
},
|
||||||
|
"id": 1586112780892
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"casparplayhost": "localhost:3000",
|
||||||
|
"casparhost": "host.docker.internal"
|
||||||
|
},
|
||||||
|
"version": 1,
|
||||||
|
"trash": []
|
||||||
|
}
|
|
@ -1,14 +1,11 @@
|
||||||
'use strict'
|
import log from './api/log.mjs'
|
||||||
require('babel-register')
|
import setup from './api/setup.mjs'
|
||||||
|
|
||||||
let log = require('./log').default
|
|
||||||
|
|
||||||
// Run the database script automatically.
|
// Run the database script automatically.
|
||||||
log.info('Running database integrity scan.')
|
log.info('Running database integrity scan.')
|
||||||
let setup = require('./script/setup')
|
|
||||||
|
|
||||||
setup().then(() => {
|
setup().then(() => {
|
||||||
require('./api/server')
|
import('./api/server.mjs')
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
log.error(error, 'Error while preparing database')
|
log.error(error, 'Error while preparing database')
|
||||||
process.exit(1)
|
process.exit(1)
|
|
@ -1,6 +1,3 @@
|
||||||
'use strict'
|
|
||||||
require('babel-register')
|
|
||||||
|
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const config = require('./config')
|
const config = require('./config')
|
||||||
|
|
||||||
|
|
4082
package-lock.json
generated
Normal file
4082
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
29
package.json
29
package.json
|
@ -3,17 +3,17 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "CasparCG superimposed graphics project",
|
"description": "CasparCG superimposed graphics project",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:styl": "stylus -m app/styl/main.styl --out public && stylus -m app/styl/client.styl --out public && stylus -m app/styl/status.styl --out public",
|
"js:build:main": "asbundle app/main/index.js public/main.js",
|
||||||
"watch:styl": "stylus -w -m app/styl/main.styl app/styl/client.styl app/styl/status.styl --out public",
|
"js:build:client": "asbundle app/client/index.js public/client.js",
|
||||||
"build:js": "asbundle app/main/index.js ../../../public/main.js && asbundle app/client/index.js public/client.js && asbundle app/status/index.js ../../public/status.js",
|
"js:build:status": "asbundle app/status/index.js public/status.js",
|
||||||
"watch:js": "nodemon --watch app --exec \"npm run build:js\"",
|
"js:watch": "nodemon --watch app --exec \"npm run build\"",
|
||||||
"watch:server": "nodemon --watch api index.js | bunyan -o short",
|
"start:watch": "nodemon --experimental-modules --watch api index.mjs | bunyan -o short",
|
||||||
"start": "node index.js | bunyan -o short",
|
"start": "node --experimental-modules index.mjs | bunyan -o short",
|
||||||
"start:win": "node index.js | bunyan -o short",
|
"start:win": "node --experimental-modules index.mjs | bunyan -o short",
|
||||||
"dev": "run-p watch:styl watch:js watch:server",
|
"dev": "run-p js:watch start:watch",
|
||||||
"prod-run": "npm run build:js && npm run build-client:js && npm run build-status:js && npm run build:styl && npm run build-client:styl && npm run build-status:styl && npm start",
|
"build": "run-p js:build:main js:build:client js:build:status",
|
||||||
"build": "npm run build:js && npm run build:styl",
|
|
||||||
"docker": "docker run -it --rm --name my-running-script -p 3000:3000 -v \"%cd%\":/usr/src/app -w /usr/src/app node",
|
"docker": "docker run -it --rm --name my-running-script -p 3000:3000 -v \"%cd%\":/usr/src/app -w /usr/src/app node",
|
||||||
"docker:install": "npm run docker -- npm install",
|
"docker:install": "npm run docker -- npm install",
|
||||||
"docker:dev": "npm run docker -- npm run dev"
|
"docker:dev": "npm run docker -- npm run dev"
|
||||||
|
@ -34,8 +34,6 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/nfp-projects/caspar-sup#readme",
|
"homepage": "https://github.com/nfp-projects/caspar-sup#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
|
|
||||||
"babel-register": "^6.26.0",
|
|
||||||
"bookshelf": "^0.11.1",
|
"bookshelf": "^0.11.1",
|
||||||
"bunyan": "^1.8.12",
|
"bunyan": "^1.8.12",
|
||||||
"casparcg-connection": "4.9.0",
|
"casparcg-connection": "4.9.0",
|
||||||
|
@ -44,18 +42,19 @@
|
||||||
"koa": "^2.4.1",
|
"koa": "^2.4.1",
|
||||||
"koa-better-serve": "^2.0.7",
|
"koa-better-serve": "^2.0.7",
|
||||||
"koa-socket": "^4.4.0",
|
"koa-socket": "^4.4.0",
|
||||||
|
"lodash.template": "^4.5.0",
|
||||||
|
"lowdb": "^1.0.0",
|
||||||
"nconf": "^0.9.1",
|
"nconf": "^0.9.1",
|
||||||
"socket.io": "^2.3.0",
|
"socket.io": "^2.3.0",
|
||||||
"sqlite3": "^4.1.1",
|
"sqlite3": "^4.1.1",
|
||||||
"tslib": "^1.11.1"
|
"tslib": "^1.11.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"asbundle": "^2.6.0",
|
"asbundle": "TheThing/asbundle",
|
||||||
"dragula": "^3.7.2",
|
"dragula": "^3.7.2",
|
||||||
"mithril": "^1.1.5",
|
"mithril": "^1.1.5",
|
||||||
"nodemon": "^2.0.2",
|
"nodemon": "^2.0.2",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
"run-p": "0.0.0",
|
"run-p": "0.0.0"
|
||||||
"stylus": "^0.54.7"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +1,45 @@
|
||||||
* {
|
html, body {
|
||||||
|
width:1920px; /* Set to your channel's resolution */
|
||||||
|
height:1080px; /* Set to your channel's resolution */
|
||||||
|
margin:0; /* Use all available space */
|
||||||
|
padding:0; /* Use all available space */
|
||||||
|
background:transparent; /* The HTML consumer actually makes your background transparent by default, unless a color or image is specified - but this might be usefull when debugging in browsers */
|
||||||
|
overflow:hidden; /* Hide any overflowing elements - to disable scollbars */
|
||||||
|
-webkit-font-smoothing:antialiased !important; /* Set aliasing of fonts - possible options: none, antialiased, subpixel-antialiased */
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
overflow: auto;
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
box-sizing:border-box;
|
box-sizing:border-box;
|
||||||
/* This sets all elements to be the actual set dimensions, disregarding padding and borders */
|
|
||||||
/* -webkit-backface-visibility: hidden; */
|
|
||||||
/* Hide the backface of elements - useful for 3d effects */
|
|
||||||
-webkit-transition: translate3d(0, 0, 0); /* Turns on hardware acceleration - not known to be of benefit in CCG, but won't hurt */
|
|
||||||
}
|
}
|
||||||
html,
|
|
||||||
body {
|
*, *:before, *:after {
|
||||||
width: 1920px;
|
box-sizing: inherit;
|
||||||
/* Set to your channel's resolution */
|
|
||||||
height: 1080px;
|
|
||||||
/* Set to your channel's resolution */
|
|
||||||
margin: 0;
|
|
||||||
/* Use all available space */
|
|
||||||
padding: 0;
|
|
||||||
/* Use all available space */
|
|
||||||
background: transparent;
|
|
||||||
/* The HTML consumer actually makes your background transparent by default, unless a color or image is specified - but this might be usefull when debugging in browsers */
|
|
||||||
overflow: hidden;
|
|
||||||
/* Hide any overflowing elements - to disable scollbars */
|
|
||||||
-webkit-font-smoothing: antialiased !important;
|
|
||||||
/* Set aliasing of fonts - possible options: none, antialiased, subpixel-antialiased */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: Calibri, Arial;
|
font-family: Arial;
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
color: #fff;
|
color: #FFFFFF;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 22pt;
|
||||||
/* -webkit-text-stroke-width: 0.5px;
|
/* -webkit-text-stroke-width: 0.5px;
|
||||||
-webkit-text-stroke-color: #888888;
|
-webkit-text-stroke-color: #888888;
|
||||||
text-shadow: 2px 2px 1px #000000; */
|
text-shadow: 2px 2px 1px #000000; */
|
||||||
}
|
}
|
||||||
body {
|
|
||||||
font-family: Arial;
|
body > div
|
||||||
font-weight: normal;
|
{
|
||||||
/* text-shadow: 0px 0px 0px #000000; */
|
|
||||||
font-size: 22pt;
|
|
||||||
}
|
|
||||||
html {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
body > div {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
.root-element {
|
.root-element {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 1s;
|
transition: opacity 1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.root-element-display {
|
.root-element-display {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 1s;
|
transition: opacity 1s;
|
||||||
}
|
}
|
||||||
/*# sourceMappingURL=client.css.map */
|
|
|
@ -1 +0,0 @@
|
||||||
{"version":3,"sources":["../app/styl/client.styl"],"names":[],"mappings":"AAAA;EACE,oBAAoB,WAApB;EACA,YAAW,WAAX;AAA4C;AAC7C;AAAiD;EAChD,oBAAoB,qBAApB;AAA4C;;AAE9C;AAAM;EACJ,OAAM,OAAN;AAAyD;EACzD,QAAO,OAAP;AAAyD;EACzD,QAAO,EAAP;AAAyD;EACzD,SAAQ,EAAR;AAAyD;EACzD,YAAW,YAAX;AAAyD;EACzD,UAAS,OAAT;AAAyD;EACzD,wBAAuB,uBAAvB;AAAyD;;AAE3D;EACE,aAAoB,eAApB;EACA,WAAW,KAAX;EACA,OAAO,KAAP;AACA;;;;AAKF;EACE,aAAa,MAAb;EACA,aAAa,OAAb;AACA;EACA,WAAW,KAAX;;AAEF;EACE,UAAU,KAAV;;AAEF;EAEE,UAAU,SAAV;;AAGF;EACE,SAAS,EAAT;EACA,YAAY,WAAZ;;AAGF;EACE,SAAS,EAAT;EACA,YAAY,WAAZ","file":"client.css"}
|
|
437
public/main.css
Normal file
437
public/main.css
Normal file
|
@ -0,0 +1,437 @@
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, h1, h2, h3, h4, h5, h6, p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #3f3f41;
|
||||||
|
color: #f1f1f1;
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: Helvetica, sans-serif, Arial;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
/* Components */
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
color: #f1f1f1;
|
||||||
|
background: #2199e8;
|
||||||
|
font-size: 0.6em;
|
||||||
|
height: 3em;
|
||||||
|
}
|
||||||
|
button.green {
|
||||||
|
background: #3adb78;
|
||||||
|
}
|
||||||
|
button.red {
|
||||||
|
background: #ec5840;
|
||||||
|
}
|
||||||
|
.error-box {
|
||||||
|
margin: 1rem 0rem 2rem 0;
|
||||||
|
padding: 1rem;
|
||||||
|
background: #f00;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.7em;
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
/* Container */
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
flex-grow: 2;
|
||||||
|
}
|
||||||
|
/* Header */
|
||||||
|
section.current {
|
||||||
|
padding: 0 13px;
|
||||||
|
background: #000;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
section.current h4 {
|
||||||
|
color: #777;
|
||||||
|
font-size: 0.7em;
|
||||||
|
padding: 0.2em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
section.current h3 {
|
||||||
|
font-size: 1em;
|
||||||
|
line-height: 2em;
|
||||||
|
color: #eb6e00;
|
||||||
|
flex-grow: 2;
|
||||||
|
height: 2em;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
overflow: hidden;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
section.current button {
|
||||||
|
width: 80px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
section.current .item {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.disconnected {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0,0,0,0.8);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1em;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
/* Menu */
|
||||||
|
nav {
|
||||||
|
width: 200px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
padding: 10px;
|
||||||
|
background: #2d2d30;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
nav .header {
|
||||||
|
color: #777;
|
||||||
|
font-size: 0.8em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
nav .header--space {
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
|
nav a {
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: 2.6em;
|
||||||
|
display: block;
|
||||||
|
border: 4px solid #2d2d30;
|
||||||
|
background: #007acc;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
nav a.active {
|
||||||
|
background: transparent;
|
||||||
|
border: 4px solid #007acc;
|
||||||
|
}
|
||||||
|
nav a:hover {
|
||||||
|
border: 4px solid #007acc;
|
||||||
|
}
|
||||||
|
nav input[type=text] {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
nav .status {
|
||||||
|
padding: 5px 20px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #777;
|
||||||
|
text-align: left;
|
||||||
|
position: relative;
|
||||||
|
margin-left: 1.8em;
|
||||||
|
}
|
||||||
|
nav .status::after {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: calc(50% - 5px);
|
||||||
|
content: '';
|
||||||
|
border: 6px solid #ec5840;
|
||||||
|
}
|
||||||
|
nav .status.green::after {
|
||||||
|
border-color: #008000;
|
||||||
|
}
|
||||||
|
/* Main */
|
||||||
|
main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
padding: 10px 1em;
|
||||||
|
flex-grow: 2;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
main .header {
|
||||||
|
color: #777;
|
||||||
|
font-size: 0.8em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
/* Inputs */
|
||||||
|
label {
|
||||||
|
margin-top: 0.6em;
|
||||||
|
color: #f1f1f1;
|
||||||
|
font-size: 0.7em;
|
||||||
|
}
|
||||||
|
label a,
|
||||||
|
label a:hover,
|
||||||
|
label a:visited {
|
||||||
|
color: #aaa;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
input[type='text'],
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
font-size: 0.6em;
|
||||||
|
padding: 0.5em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
background: #333337;
|
||||||
|
border: 1px solid #2d2d30;
|
||||||
|
color: #999;
|
||||||
|
transition-property: none !important;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
input[type='text']:hover,
|
||||||
|
textarea:hover,
|
||||||
|
select:hover {
|
||||||
|
color: #f1f1f1;
|
||||||
|
border-color: #007acc;
|
||||||
|
}
|
||||||
|
input[type='text']:focus,
|
||||||
|
textarea:focus,
|
||||||
|
select:focus {
|
||||||
|
background: #333337;
|
||||||
|
color: #f1f1f1;
|
||||||
|
border-color: #007acc;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
textarea#graphic-html {
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
textarea#graphic-css {
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
input[type=submit] {
|
||||||
|
margin-top: 0.6em;
|
||||||
|
border: none;
|
||||||
|
color: #f1f1f1;
|
||||||
|
background: #2199e8;
|
||||||
|
font-size: 0.6em;
|
||||||
|
line-height: 3em;
|
||||||
|
}
|
||||||
|
input[readonly],
|
||||||
|
input[readonly]:hover {
|
||||||
|
background: #2d2d30 !important;
|
||||||
|
border-color: #3f3f3f;
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
height: 2.5em;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
border-radius: 0;
|
||||||
|
background-position: right center;
|
||||||
|
background-size: 9px 6px;
|
||||||
|
background-origin: content-box;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='32' height='24' viewBox='0 0 32 24'><polygon points='0,0 32,0 16,24' style='fill: rgb%28138, 138, 138%29'></polygon></svg>");
|
||||||
|
}
|
||||||
|
select:hover {
|
||||||
|
color: #f1f1f1;
|
||||||
|
border-color: #007acc;
|
||||||
|
}
|
||||||
|
select:focus {
|
||||||
|
background: #333337;
|
||||||
|
color: #f1f1f1;
|
||||||
|
border-color: #007acc;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
a.button {
|
||||||
|
margin: 0 1rem 0 0;
|
||||||
|
width: 7rem;
|
||||||
|
}
|
||||||
|
/* Graphic */
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
header h3 {
|
||||||
|
font-size: 1em;
|
||||||
|
flex-grow: 2;
|
||||||
|
border-bottom: 1px solid #2d2d30;
|
||||||
|
padding-top: 10px;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
header button {
|
||||||
|
border: 0;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
.graphic-presetlist {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.graphic-presetadd {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
border: 1px solid #2d2d30;
|
||||||
|
margin: 30px 0 10px;
|
||||||
|
padding: 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.graphic-presetadd-header {
|
||||||
|
background: #3f3f41;
|
||||||
|
position: absolute;
|
||||||
|
top: -1.3em;
|
||||||
|
left: 10px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
padding: 0.8em 10px;
|
||||||
|
}
|
||||||
|
.graphic-presetadd-buttons {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.graphic-presetadd-buttons button {
|
||||||
|
margin-right: 10px;
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
.graphic-presetremove {
|
||||||
|
align-self: center;
|
||||||
|
margin-top: 50px;
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
.graphic-empty {
|
||||||
|
font-size: 0.7em;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
.graphic-delete {
|
||||||
|
align-self: center;
|
||||||
|
margin-top: 30px;
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
.graphic-label {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
.graphic-helper {
|
||||||
|
font-size: 0.7em;
|
||||||
|
color: #999;
|
||||||
|
margin: 5px 0 0;
|
||||||
|
}
|
||||||
|
.graphic-helper.bottom {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.graphic-property,
|
||||||
|
.graphic-preset {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.graphic-property-reorder,
|
||||||
|
.graphic-preset-reorder {
|
||||||
|
width: 62px;
|
||||||
|
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODQ5NDcyMzY3MTU5MTFFOEEwQjVFQUFCMEYzRDE2QjciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODQ5NDcyMzc3MTU5MTFFOEEwQjVFQUFCMEYzRDE2QjciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4NDk0NzIzNDcxNTkxMUU4QTBCNUVBQUIwRjNEMTZCNyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4NDk0NzIzNTcxNTkxMUU4QTBCNUVBQUIwRjNEMTZCNyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pla8e2wAAABFUExURXd3d////3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d0brzhMAAAAWdFJOUwAAN0NEVXJzdHV2d3h/iImKmaq73e5w9SzPAAAAlElEQVRIx+XT2wqDMBRE0Witt2piW53//9SCoETMwJkXkWY/CgtNBp0jDa/086JgwgNeIwEghpFVAMFOJoCaNNkFMNlIJBImRcrPodJ8ybQ/I285B7mLSCWX+/qL3EVTznK5T/mUu++P7OVyX/9U9T30sLyliUeobR8WmcZ6lnYTrf34HRX8xlbTaZfcA726yziyXX5Joj0NgTfsUQAAAABJRU5ErkJggg==") no-repeat transparent;
|
||||||
|
background-size: 25px;
|
||||||
|
background-position: center;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
.graphic-property input,
|
||||||
|
.graphic-preset input {
|
||||||
|
flex-grow: 2;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.graphic-property button,
|
||||||
|
.graphic-preset button {
|
||||||
|
width: 100px;
|
||||||
|
border: 1px solid #3f3f41;
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
.graphic-preset input {
|
||||||
|
padding-top: 1.5em;
|
||||||
|
padding-bottom: 1.5em;
|
||||||
|
}
|
||||||
|
.graphic-preset button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
.schedule-empty {
|
||||||
|
margin-top: 2em;
|
||||||
|
font-size: 1em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.settings-empty {
|
||||||
|
text-align: center;
|
||||||
|
margin: 50px 0 30px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.settings-empty-button {
|
||||||
|
align-self: center;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
/* Dragula */
|
||||||
|
|
||||||
|
#dragcontainer {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gu-mirror {
|
||||||
|
position: absolute !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
z-index: 9999 !important;
|
||||||
|
opacity: 0.8;
|
||||||
|
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
|
||||||
|
filter: alpha(opacity=80);
|
||||||
|
}
|
||||||
|
.gu-hide {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.gu-unselectable {
|
||||||
|
-webkit-user-select: none !important;
|
||||||
|
-moz-user-select: none !important;
|
||||||
|
-ms-user-select: none !important;
|
||||||
|
user-select: none !important;
|
||||||
|
}
|
||||||
|
.gu-transit {
|
||||||
|
opacity: 0.2;
|
||||||
|
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
|
||||||
|
filter: alpha(opacity=20);
|
||||||
|
}
|
||||||
|
/* Media queries */
|
||||||
|
body {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
@media only screen and (max-device-width: 600px) {
|
||||||
|
#container {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
nav {
|
||||||
|
width: auto;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
nav .header {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
nav a {
|
||||||
|
width: calc(50% - 8px);
|
||||||
|
}
|
||||||
|
nav input[type=text] {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
nav .status {
|
||||||
|
align-self: center;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
#content {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=main.css.map */
|
116
public/status.css
Normal file
116
public/status.css
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, h1, h2, h3, h4, h5, h6, p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #3f3f41;
|
||||||
|
color: #eb6e00;
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: Helvetica, sans-serif, Arial;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container h3 {
|
||||||
|
background: black;
|
||||||
|
font-size: 2.3em;
|
||||||
|
line-height: (2.3em);
|
||||||
|
color: #eb6e00;
|
||||||
|
height: (2.3em);
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 0.3em;
|
||||||
|
flex-grow: 2;
|
||||||
|
border-radius: 5px 0 0 5px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container button {
|
||||||
|
border: none;
|
||||||
|
color: black;
|
||||||
|
background: #eb6e00;
|
||||||
|
font-size: 2em;
|
||||||
|
width: 80px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 0 5px 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container .item {
|
||||||
|
display: flex;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container .empty {
|
||||||
|
flex-grow: 2;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h2 {
|
||||||
|
font-size: 3em;
|
||||||
|
line-height: 64px;
|
||||||
|
padding: 0 0.3em;
|
||||||
|
flex-grow: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .status {
|
||||||
|
padding: 0 20px 0 30px;
|
||||||
|
line-height: 64px;
|
||||||
|
font-size: 2em;
|
||||||
|
text-align: left;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .status::after {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: calc(50% - 7px);
|
||||||
|
content: '';
|
||||||
|
border: 10px solid #ec5840;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .status.green::after {
|
||||||
|
border-color: #00FF00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disconnected {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0,0,0,0.8);
|
||||||
|
color: white;
|
||||||
|
font-size: 3em;
|
||||||
|
display: flex;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
|
@ -1,44 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
/* eslint-disable no-console */
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const _ = require('lodash')
|
|
||||||
const config = require('../config')
|
|
||||||
let log = require('../log').default
|
|
||||||
|
|
||||||
// This is important for setup to run cleanly.
|
|
||||||
let knexConfig = _.cloneDeep(config.get('knex'))
|
|
||||||
knexConfig.pool = { min: 1, max: 1 }
|
|
||||||
|
|
||||||
let knex = require('knex')(knexConfig)
|
|
||||||
|
|
||||||
log.info(knexConfig, 'Connected to database')
|
|
||||||
|
|
||||||
let setup = module.exports = () =>
|
|
||||||
knex.migrate.latest({
|
|
||||||
directory: './migrations',
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (result[1].length === 0) {
|
|
||||||
return log.info('Database is up to date')
|
|
||||||
}
|
|
||||||
for (let i = 0; i < result[1].length; i++) {
|
|
||||||
log.info('Applied migration from', result[1][i].substr(result[1][i].lastIndexOf('\\') + 1))
|
|
||||||
}
|
|
||||||
return knex.destroy()
|
|
||||||
})
|
|
||||||
|
|
||||||
if (require.main === module) {
|
|
||||||
// Since we're running this as a script, we should output
|
|
||||||
// directly to the console.
|
|
||||||
log = console
|
|
||||||
log.info = console.log.bind(console)
|
|
||||||
|
|
||||||
setup().then(() => {
|
|
||||||
log.info('Setup ran successfully.')
|
|
||||||
}).catch((error) => {
|
|
||||||
log.error(error, 'Error while running setup.')
|
|
||||||
}).then(() => {
|
|
||||||
process.exit(0)
|
|
||||||
})
|
|
||||||
}
|
|
Loading…
Reference in a new issue