Implemented version 1.0
This commit is contained in:
parent
e9555b215e
commit
918f350dff
34 changed files with 1161 additions and 229 deletions
|
@ -1,36 +1,38 @@
|
|||
var socket = require('./socket')
|
||||
|
||||
socket.on('client.display', function(data) {
|
||||
var exists = document.getElementById(data.key)
|
||||
var engines = {
|
||||
text: require('./frontend/text'),
|
||||
countdown: require('./frontend/countdown'),
|
||||
}
|
||||
|
||||
var current = []
|
||||
|
||||
function display(data) {
|
||||
var exists = document.getElementById(data.graphic.name)
|
||||
|
||||
if (exists) {
|
||||
exists.tag.remove()
|
||||
exists.remove()
|
||||
|
||||
current.splice(current.indexOf(data.graphic.name), 1)
|
||||
}
|
||||
current.push(data.graphic.name)
|
||||
|
||||
var element = document.createElement('div')
|
||||
element.innerHTML = data.html
|
||||
element.id = data.key
|
||||
element.classList.add('root-element')
|
||||
let engine = data.graphic.engine
|
||||
|
||||
var styleElement = document.createElement('style')
|
||||
styleElement.setAttribute('type', 'text/css')
|
||||
styleElement.innerHTML = data.css
|
||||
if (engines[engine]) {
|
||||
engines[engine](data)
|
||||
}
|
||||
}
|
||||
|
||||
element.tag = styleElement
|
||||
|
||||
document.body.appendChild(element)
|
||||
document.head.appendChild(styleElement)
|
||||
|
||||
window.setTimeout(function (){
|
||||
element.classList.add('root-element-display')
|
||||
}, 50)
|
||||
})
|
||||
socket.on('client.display', display)
|
||||
|
||||
socket.on('client.hide', function (data) {
|
||||
var exists = document.getElementById(data.key)
|
||||
var exists = document.getElementById(data.name)
|
||||
|
||||
if (exists) {
|
||||
current.splice(current.indexOf(data.name), 1)
|
||||
|
||||
exists.classList.remove('root-element-display')
|
||||
|
||||
window.setTimeout(function () {
|
||||
|
@ -39,3 +41,7 @@ socket.on('client.hide', function (data) {
|
|||
}, 1500)
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('client.reset', function(data) {
|
||||
data.forEach(display)
|
||||
})
|
||||
|
|
52
app/controller/add.js
Normal file
52
app/controller/add.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
const m = require('mithril')
|
||||
const createModule = require('./module')
|
||||
const components = require('./components')
|
||||
const socket = require('../socket')
|
||||
|
||||
const Add = createModule({
|
||||
init: function() {
|
||||
this.monitor('engines', 'engine.all', [])
|
||||
this.graphic = { }
|
||||
},
|
||||
|
||||
updated: function(name, control) {
|
||||
this.graphic[name] = control.target.value
|
||||
},
|
||||
|
||||
create: function() {
|
||||
if (!Add.vm.graphic.engine) {
|
||||
Add.vm.graphic.engine = Add.vm.engines[0]
|
||||
}
|
||||
if (!Add.vm.graphic.name) {
|
||||
this.error = 'Name cannot be empty'
|
||||
return
|
||||
}
|
||||
|
||||
socket.emit('graphic.create', Add.vm.graphic)
|
||||
},
|
||||
}, function(ctrl) {
|
||||
return m('div', [
|
||||
m('h3.container-header', 'Add graphics'),
|
||||
m('div.container-panel.panel-add', [
|
||||
components.error(Add.vm.error),
|
||||
m('label', [
|
||||
'Name',
|
||||
m('input[type=text]', {
|
||||
oninput: Add.vm.updated.bind(Add.vm, 'name'),
|
||||
})
|
||||
]),
|
||||
m('label', [
|
||||
'Engine',
|
||||
m('select', {
|
||||
onchange: Add.vm.updated.bind(Add.vm, 'engine'),
|
||||
}, Add.vm.engines.map(engine =>
|
||||
m('option', { key: engine, value: engine }, engine)
|
||||
))
|
||||
]),
|
||||
m('a.button', {
|
||||
onclick: Add.vm.create.bind(Add.vm)
|
||||
}, 'Create'),
|
||||
]),
|
||||
])
|
||||
})
|
||||
module.exports = Add
|
41
app/controller/components.js
Normal file
41
app/controller/components.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
const m = require('mithril')
|
||||
|
||||
exports.error = function(error) {
|
||||
if (!error) return null
|
||||
|
||||
return m('div.error-box', error)
|
||||
}
|
||||
|
||||
exports.presetList = function(vm) {
|
||||
return [
|
||||
m('a.panel-graphic-preset-add.button', {
|
||||
onclick: vm.addPreset.bind(vm),
|
||||
}, 'Save to preset list'),
|
||||
m('a.panel-graphic-display.success.button', {
|
||||
onclick: vm.displayCurrent.bind(vm),
|
||||
}, 'Display'),
|
||||
m('label', 'Presets'),
|
||||
m('ul.panel-graphic-preset', vm.presets.map((item, index) =>
|
||||
m('li', { key: index }, [
|
||||
m('.row', { key: index }, [
|
||||
m('div', { class: 'small-8 columns panel-graphic-property-item' },
|
||||
m('input[type=text]', {
|
||||
readonly: true,
|
||||
value: item.values[graphic.settings.main],
|
||||
})
|
||||
),
|
||||
m('div', { class: 'small-2 columns' },
|
||||
m('a.panel-graphic-preset-remove.button.success', {
|
||||
onclick: vm.displayPreset.bind(vm, item),
|
||||
}, 'Display')
|
||||
),
|
||||
m('div', { class: 'small-2 columns' },
|
||||
m('a.panel-graphic-preset-remove.button.alert', {
|
||||
onclick: vm.removePreset.bind(vm, item),
|
||||
}, 'Remove')
|
||||
),
|
||||
])
|
||||
])
|
||||
))
|
||||
]
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
const m = require('mithril')
|
||||
const socket = require('../socket')
|
||||
const store = require('./store')
|
||||
|
||||
const Content = { }
|
||||
|
||||
Content.vm = (function() {
|
||||
let vm = {}
|
||||
|
||||
vm.storeUpdated = function() {
|
||||
vm.content = store.get('content') || {}
|
||||
m.redraw()
|
||||
}
|
||||
|
||||
vm.init = function() {
|
||||
vm.content = store.get('content') || {}
|
||||
store.listen('content', vm.storeUpdated)
|
||||
}
|
||||
|
||||
vm.onunload = function() {
|
||||
store.unlisten('content')
|
||||
}
|
||||
|
||||
vm.updated = function(name, control) {
|
||||
vm.content[name] = control.target.value
|
||||
store.set('content', vm.content)
|
||||
}
|
||||
|
||||
vm.display = function() {
|
||||
socket.emit('content.display', vm.content)
|
||||
}
|
||||
|
||||
vm.hide = function() {
|
||||
socket.emit('content.hide')
|
||||
}
|
||||
|
||||
return vm
|
||||
})()
|
||||
|
||||
Content.controller = function() {
|
||||
Content.vm.init()
|
||||
|
||||
this.onunload = Content.vm.onunload
|
||||
}
|
||||
|
||||
Content.view = function() {
|
||||
return m('div', [
|
||||
m('h3', 'Content'),
|
||||
m('div', { class: 'row' }, [
|
||||
m('div', { class: 'small-12 columns' }, [
|
||||
m('label', [
|
||||
'HTML (use <%- name %> and <%- title %> for values)',
|
||||
m('textarea', {
|
||||
rows: '4',
|
||||
oninput: Content.vm.updated.bind(null, 'html'),
|
||||
value: Content.vm.content.html || '',
|
||||
})
|
||||
]),
|
||||
]),
|
||||
m('div', { class: 'small-12 columns' }, [
|
||||
m('label', [
|
||||
'CSS',
|
||||
m('textarea', {
|
||||
rows: '4',
|
||||
oninput: Content.vm.updated.bind(null, 'css'),
|
||||
value: Content.vm.content.css || '',
|
||||
})
|
||||
]),
|
||||
]),
|
||||
m('div', { class: 'small-12 columns' }, [
|
||||
m('label', [
|
||||
'Name',
|
||||
m('input[type=text]', {
|
||||
oninput: Content.vm.updated.bind(null, 'name'),
|
||||
value: Content.vm.content.name || '',
|
||||
})
|
||||
]),
|
||||
]),
|
||||
m('div', { class: 'small-12 columns' }, [
|
||||
m('label', [
|
||||
'Title',
|
||||
m('input[type=text]', {
|
||||
oninput: Content.vm.updated.bind(null, 'title'),
|
||||
value: Content.vm.content.title || '',
|
||||
})
|
||||
]),
|
||||
]),
|
||||
m('a.button', {
|
||||
onclick: Content.vm.display
|
||||
}, 'Display'),
|
||||
m('a.button.alert', {
|
||||
onclick: Content.vm.hide
|
||||
}, 'Hide'),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
module.exports = Content
|
136
app/controller/graphic/controller.js
Normal file
136
app/controller/graphic/controller.js
Normal file
|
@ -0,0 +1,136 @@
|
|||
const _ = require('lodash')
|
||||
const m = require('mithril')
|
||||
const createModule = require('../module')
|
||||
const socket = require('../../socket')
|
||||
|
||||
const Graphic = createModule({
|
||||
init: function() {
|
||||
this.monitor('graphic', 'graphic.single', {}, m.route.param('id'))
|
||||
this.monitor('presets', 'preset.all', [], m.route.param('id'))
|
||||
|
||||
this.currentView = 'view'
|
||||
this.current = {}
|
||||
this.newProperty = m.prop('')
|
||||
},
|
||||
|
||||
updated: function(name, variable, control) {
|
||||
if (!control) {
|
||||
control = variable
|
||||
variable = 'graphic'
|
||||
}
|
||||
_.set(this[variable], name, control.target.value)
|
||||
|
||||
if (variable === 'graphic') {
|
||||
socket.emit('graphic.update', this.graphic)
|
||||
}
|
||||
},
|
||||
|
||||
addProperty: function() {
|
||||
if (!this.newProperty()) {
|
||||
this.error = 'Please type in property name'
|
||||
return
|
||||
}
|
||||
if (this.graphic.settings.properties.includes(this.newProperty())) {
|
||||
this.error = 'A property with that name already exists'
|
||||
return
|
||||
}
|
||||
|
||||
this.graphic.settings.properties.push(this.newProperty())
|
||||
this.newProperty('')
|
||||
|
||||
if (!this.graphic.settings.main) {
|
||||
this.graphic.settings.main = this.graphic.settings.properties[0]
|
||||
}
|
||||
|
||||
socket.emit('graphic.update', this.graphic)
|
||||
},
|
||||
|
||||
cleanCurrent: function() {
|
||||
if (this.graphic.engine === 'countdown') {
|
||||
this.current.text = this.graphic.settings.text
|
||||
this.current.countdown = this.graphic.settings.countdown
|
||||
this.current.finished = this.graphic.settings.finished
|
||||
|
||||
if (!this.current.countdown) {
|
||||
this.error = 'Count to had to be defined'
|
||||
}
|
||||
else {
|
||||
let test = new Date(this.current.countdown.replace(' ', 'T'))
|
||||
if (!test.getTime()) {
|
||||
this.error = 'Count to has to be valid date and time'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.graphic.settings.properties.forEach(prop => {
|
||||
if (!this.current[prop]) {
|
||||
this.current[prop] = ''
|
||||
}
|
||||
})
|
||||
}
|
||||
if (this.graphic.settings.main &&
|
||||
!this.current[this.graphic.settings.main]) {
|
||||
this.error = `Property "${this.graphic.settings.main}" cannot be empty`
|
||||
return
|
||||
}
|
||||
},
|
||||
|
||||
addPreset: function() {
|
||||
this.error = ''
|
||||
|
||||
this.cleanCurrent()
|
||||
|
||||
if (this.error) return
|
||||
|
||||
socket.emit('preset.add', {
|
||||
graphic_id: this.graphic.id,
|
||||
values: this.current,
|
||||
})
|
||||
},
|
||||
|
||||
removePreset: function(preset) {
|
||||
socket.emit('preset.remove', preset)
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
socket.emit('graphic.remove', this.graphic)
|
||||
m.route('/')
|
||||
},
|
||||
|
||||
displayPreset: function(preset) {
|
||||
socket.emit('content.display', {
|
||||
graphic: this.graphic,
|
||||
data: preset.values,
|
||||
})
|
||||
},
|
||||
|
||||
displayCurrent: function() {
|
||||
this.error = ''
|
||||
|
||||
this.cleanCurrent()
|
||||
|
||||
if (this.error) return
|
||||
|
||||
socket.emit('content.display', {
|
||||
graphic: this.graphic,
|
||||
data: this.current,
|
||||
})
|
||||
},
|
||||
|
||||
removeProperty: function(prop) {
|
||||
this.graphic.settings.properties.splice(
|
||||
this.graphic.settings.properties.indexOf(prop), 1)
|
||||
socket.emit('graphic.update', this.graphic)
|
||||
},
|
||||
|
||||
switchView: function() {
|
||||
if (Graphic.vm.currentView === 'view') {
|
||||
Graphic.vm.currentView = 'settings'
|
||||
return
|
||||
}
|
||||
Graphic.vm.currentView = 'view'
|
||||
},
|
||||
})
|
||||
|
||||
module.exports = Graphic
|
||||
|
||||
require('./view')
|
66
app/controller/graphic/engine/countdown.js
Normal file
66
app/controller/graphic/engine/countdown.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
const m = require('mithril')
|
||||
const components = require('../../components')
|
||||
|
||||
exports.view = function(ctlr, graphic, vm) {
|
||||
return [
|
||||
m('label', [
|
||||
'Text',
|
||||
m('input[type=text]', {
|
||||
value: vm.graphic.settings.text || '',
|
||||
oninput: vm.updated.bind(vm, 'settings.text'),
|
||||
}),
|
||||
]),
|
||||
m('label', [
|
||||
'Count to (format: "YYYY-MM-DD hh:mm")',
|
||||
m('input[type=text]', {
|
||||
value: vm.graphic.settings.countdown || '',
|
||||
oninput: vm.updated.bind(vm, 'settings.countdown'),
|
||||
}),
|
||||
]),
|
||||
m('label', [
|
||||
'Finished (gets displayed in the countdown upon reaching 0)',
|
||||
m('input[type=text]', {
|
||||
value: vm.graphic.settings.finished || '',
|
||||
oninput: vm.updated.bind(vm, 'settings.finished'),
|
||||
}),
|
||||
]),
|
||||
components.presetList(vm),
|
||||
]
|
||||
}
|
||||
|
||||
exports.settings = function(cltr, graphic, vm) {
|
||||
return [
|
||||
m('label', [
|
||||
'Name',
|
||||
m('input[type=text]', {
|
||||
value: graphic.name,
|
||||
oninput: vm.updated.bind(vm, 'name'),
|
||||
}),
|
||||
]),
|
||||
m('label', [
|
||||
'HTML (',
|
||||
m('a', { href: 'https://lodash.com/docs#template', target: '_blank' }, 'variables'),
|
||||
' available: <%- text %>',
|
||||
')',
|
||||
m('p', `<div id="${graphic.name}">`),
|
||||
m('textarea', {
|
||||
rows: '4',
|
||||
oninput: vm.updated.bind(null, 'settings.html'),
|
||||
value: graphic.settings.html || '',
|
||||
}),
|
||||
m('p', `</div>`),
|
||||
]),
|
||||
m('label', [
|
||||
'CSS',
|
||||
m('textarea', {
|
||||
rows: '4',
|
||||
oninput: vm.updated.bind(null, 'settings.css'),
|
||||
value: graphic.settings.css || '',
|
||||
})
|
||||
]),
|
||||
m('a.panel-graphic-delete.button.alert', {
|
||||
onclick: vm.remove.bind(vm),
|
||||
}, 'Delete graphic'),
|
||||
]
|
||||
}
|
||||
|
108
app/controller/graphic/engine/text.js
Normal file
108
app/controller/graphic/engine/text.js
Normal file
|
@ -0,0 +1,108 @@
|
|||
const m = require('mithril')
|
||||
const components = require('../../components')
|
||||
|
||||
exports.view = function(ctlr, graphic, vm) {
|
||||
if (!graphic.settings.properties) {
|
||||
graphic.settings.properties = []
|
||||
}
|
||||
if (graphic.settings.properties.length === 0) {
|
||||
return [
|
||||
m('p', 'No properties have been defined.'),
|
||||
m('p', 'Click settings to create and define properties to display.'),
|
||||
]
|
||||
}
|
||||
return [
|
||||
graphic.settings.properties.map((prop, index) =>
|
||||
m('label', { key: index }, [
|
||||
prop,
|
||||
m('input[type=text]', {
|
||||
value: vm.current[prop] || '',
|
||||
oninput: vm.updated.bind(vm, prop, 'current'),
|
||||
}),
|
||||
])
|
||||
),
|
||||
components.presetList(vm),
|
||||
]
|
||||
}
|
||||
|
||||
exports.settings = function(cltr, graphic, vm) {
|
||||
return [
|
||||
m('label', [
|
||||
'Name',
|
||||
m('input[type=text]', {
|
||||
value: graphic.name,
|
||||
oninput: vm.updated.bind(vm, 'name'),
|
||||
}),
|
||||
]),
|
||||
m('label', [
|
||||
'HTML (',
|
||||
m('a', { href: 'https://lodash.com/docs#template', target: '_blank' }, 'variables'),
|
||||
' available: ',
|
||||
graphic.settings.properties.map(prop =>
|
||||
`<%- ${prop} %>`
|
||||
).join(', '),
|
||||
')',
|
||||
m('p', `<div id="${graphic.name}">`),
|
||||
m('textarea', {
|
||||
rows: '4',
|
||||
oninput: vm.updated.bind(null, 'settings.html'),
|
||||
value: graphic.settings.html || '',
|
||||
}),
|
||||
m('p', `</div>`),
|
||||
]),
|
||||
m('label', [
|
||||
'CSS',
|
||||
m('textarea', {
|
||||
rows: '4',
|
||||
oninput: vm.updated.bind(null, 'settings.css'),
|
||||
value: graphic.settings.css || '',
|
||||
})
|
||||
]),
|
||||
m('label', [
|
||||
'Main property',
|
||||
m('select', {
|
||||
onchange: vm.updated.bind(vm, 'settings.main'),
|
||||
}, graphic.settings.properties.map((prop, index) =>
|
||||
m('option', {
|
||||
key: 'prop-list-' + index,
|
||||
value: prop,
|
||||
selected: prop === graphic.settings.main,
|
||||
}, prop)
|
||||
))
|
||||
]),
|
||||
m('label', 'Properties'),
|
||||
m('div', [
|
||||
graphic.settings.properties.map((prop, index) =>
|
||||
m('.row', { key: 'add-prop-' + index }, [
|
||||
m('div', { class: 'small-10 columns panel-graphic-property-item' },
|
||||
m('input[type=text]', {
|
||||
readonly: true,
|
||||
value: prop,
|
||||
})
|
||||
),
|
||||
m('div', { class: 'small-2 columns' },
|
||||
m('a.panel-graphic-property-remove.button.alert', {
|
||||
onclick: vm.removeProperty.bind(vm, prop),
|
||||
}, 'Remove')
|
||||
)
|
||||
])
|
||||
),
|
||||
]),
|
||||
m('.row', [
|
||||
m('div', { class: 'small-10 columns panel-graphic-property-item' },
|
||||
m('input[type=text]', {
|
||||
value: vm.newProperty(),
|
||||
oninput: m.withAttr('value', vm.newProperty),
|
||||
})
|
||||
),
|
||||
m('div', { class: 'small-2 columns' },
|
||||
m('a.panel-graphic-property-add.button', {
|
||||
onclick: vm.addProperty.bind(vm),
|
||||
}, 'Add')
|
||||
),
|
||||
]),
|
||||
m('a.panel-graphic-delete.button.alert', {
|
||||
onclick: vm.remove.bind(vm),
|
||||
}, 'Delete graphic'),
|
||||
]
|
||||
}
|
27
app/controller/graphic/view.js
Normal file
27
app/controller/graphic/view.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
const m = require('mithril')
|
||||
const Graphic = require('./controller')
|
||||
const components = require('../components')
|
||||
|
||||
const engines = {
|
||||
text: require('./engine/text'),
|
||||
countdown: require('./engine/countdown'),
|
||||
}
|
||||
|
||||
Graphic.view = function(ctrl) {
|
||||
graphic = Graphic.vm.graphic
|
||||
|
||||
return m('div', [
|
||||
m('h3.container-header', 'Graphic'),
|
||||
m('div.container-panel.panel-graphic',
|
||||
!graphic.name && m('p', 'Loading...') ||
|
||||
[
|
||||
m('a.panel-graphic-settings.button', {
|
||||
onclick: Graphic.vm.switchView
|
||||
}, Graphic.vm.currentView === 'view' && 'Settings' || 'Control'),
|
||||
m('h4', graphic.name),
|
||||
components.error(Graphic.vm.error),
|
||||
engines[graphic.engine][Graphic.vm.currentView](ctrl, graphic, Graphic.vm),
|
||||
]
|
||||
),
|
||||
])
|
||||
}
|
30
app/controller/header.js
Normal file
30
app/controller/header.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
const m = require('mithril')
|
||||
const createModule = require('./module')
|
||||
const socket = require('../socket')
|
||||
|
||||
const Header = createModule({
|
||||
init: function() {
|
||||
this.monitor('list', 'content.list', [])
|
||||
},
|
||||
|
||||
hide: function(item) {
|
||||
socket.emit('content.hide', {
|
||||
name: item.name,
|
||||
})
|
||||
},
|
||||
}, function(ctrl) {
|
||||
return m('div.header', Header.vm.list.length > 0 && [
|
||||
m('h3.container-header', 'Currently active'),
|
||||
m('ul.header-list', [
|
||||
Header.vm.list.map((item, index) =>
|
||||
m('li.header-item', { key: 'header-' + index, }, [
|
||||
m('a.header-item-hide.button.alert', {
|
||||
onclick: Header.vm.hide.bind(Header.vm, item),
|
||||
}, 'Hide'),
|
||||
m('div.header-item-display', `${item.name} - ${item.display}`),
|
||||
])
|
||||
),
|
||||
]),
|
||||
] || '')
|
||||
})
|
||||
module.exports = Header
|
|
@ -1,23 +1,24 @@
|
|||
const m = require('mithril')
|
||||
const createModule = require('./module')
|
||||
|
||||
const Menu = {
|
||||
controller: function() {
|
||||
return {}
|
||||
},
|
||||
|
||||
view: function(ctrl) {
|
||||
return m('div', [
|
||||
m('h3', 'Menu'),
|
||||
m('ul', [
|
||||
m('li', [
|
||||
m('a', { href: '/', config: m.route }, 'Home'),
|
||||
]),
|
||||
m('li', [
|
||||
m('a', { href: '/content', config: m.route }, 'Content'),
|
||||
])
|
||||
const Menu = createModule({
|
||||
init: function() {
|
||||
this.monitor('list', 'graphic.all', [])
|
||||
}
|
||||
}, function(ctrl) {
|
||||
return m('div', [
|
||||
m('h3.container-header', 'Graphics'),
|
||||
m('div.container-panel.menu', [
|
||||
m('ul.menu-list', [
|
||||
// m('a', { href: `/`, config: m.route }, 'Home'),
|
||||
Menu.vm.list.map((item) =>
|
||||
m('li.menu-item', [
|
||||
m('a', { href: `/graphic/${item.id}`, config: m.route }, item.name),
|
||||
])
|
||||
)
|
||||
]),
|
||||
])
|
||||
},
|
||||
}
|
||||
|
||||
m('a.menu-item-add', { href: '/add', config: m.route }, 'Add graphic' ),
|
||||
]),
|
||||
])
|
||||
})
|
||||
module.exports = Menu
|
||||
|
|
51
app/controller/module.js
Normal file
51
app/controller/module.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
const m = require('mithril')
|
||||
const _ = require('lodash')
|
||||
const store = require('./store')
|
||||
const socket = require('../socket')
|
||||
|
||||
function createModule(vm, view) {
|
||||
let newModule = { }
|
||||
let listening = []
|
||||
|
||||
newModule.vm = _.defaults(vm, {
|
||||
_init: function() {
|
||||
this.error = null
|
||||
newModule.vm.init()
|
||||
},
|
||||
|
||||
_storeUpdated: function(key, name, id) {
|
||||
this[key] = store.get(name, id)
|
||||
m.redraw()
|
||||
},
|
||||
|
||||
init: function() { },
|
||||
|
||||
monitor: function(key, name, fallback, id) {
|
||||
this[key] = store.get(name, id) || fallback || { }
|
||||
|
||||
listening.push(name)
|
||||
|
||||
store.listen(name, this._storeUpdated.bind(this, key, name, id), id)
|
||||
|
||||
socket.emit(name, { id: id })
|
||||
},
|
||||
|
||||
onunload: function() {
|
||||
listening.forEach((item) => {
|
||||
store.unlisten(item)
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
newModule.controller = function() {
|
||||
newModule.vm._init()
|
||||
|
||||
this.onunload = newModule.vm.onunload
|
||||
}
|
||||
|
||||
newModule.view = view
|
||||
|
||||
return newModule
|
||||
}
|
||||
|
||||
module.exports = createModule
|
|
@ -1,39 +1,62 @@
|
|||
const _ = require('lodash')
|
||||
const socket = require('../socket')
|
||||
const storage = {}
|
||||
const events = {}
|
||||
|
||||
// Listen on all events
|
||||
let onevent = socket.onevent
|
||||
|
||||
socket.onevent = function(packet) {
|
||||
let args = packet.data || []
|
||||
onevent.call(this, packet) // original call
|
||||
packet.data = ['*'].concat(args)
|
||||
onevent.call(this, packet)
|
||||
}
|
||||
|
||||
function genId(name, id) {
|
||||
if (id) {
|
||||
return `${name}:${id}`
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
const store = {
|
||||
get: function(name) {
|
||||
return storage[name]
|
||||
get: function(name, id) {
|
||||
return storage[genId(name, id)]
|
||||
},
|
||||
|
||||
set: function(name, value, dontSend) {
|
||||
storage[name] = value
|
||||
|
||||
if (dontSend) {
|
||||
if (events[name]) {
|
||||
events[name]()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
socket.emit('store', {
|
||||
name,
|
||||
value,
|
||||
})
|
||||
},
|
||||
|
||||
listen: function(name, caller) {
|
||||
events[name] = caller
|
||||
listen: function(name, caller, id) {
|
||||
events[genId(name, id)] = caller
|
||||
},
|
||||
|
||||
unlisten: function(name) {
|
||||
delete events[name]
|
||||
delete storage[name]
|
||||
},
|
||||
|
||||
events: events,
|
||||
storage: storage,
|
||||
}
|
||||
|
||||
socket.on('store', (data) => {
|
||||
store.set(data.name, data.value, true)
|
||||
socket.on('*', (event, data) => {
|
||||
let name = genId(event, data && data.id)
|
||||
if (events[name]) {
|
||||
storage[name] = data
|
||||
events[name]()
|
||||
}
|
||||
if (event.contains('single')) {
|
||||
let check = event.replace('single', 'all')
|
||||
if (events[name]) {
|
||||
let index = _.findIndex(storage[check], { id: data.id })
|
||||
if (index > -1) {
|
||||
storage[check][index] = data
|
||||
events[name]()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
window.store = store
|
||||
|
||||
module.exports = store
|
||||
|
|
67
app/frontend/countdown.js
Normal file
67
app/frontend/countdown.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
|
||||
module.exports = function(data) {
|
||||
var element = document.createElement('div')
|
||||
element.innerHTML = data.html
|
||||
element.id = data.graphic.name
|
||||
element.classList.add('root-element')
|
||||
|
||||
var styleElement = document.createElement('style')
|
||||
styleElement.setAttribute('type', 'text/css')
|
||||
styleElement.innerHTML = data.css
|
||||
|
||||
element.tag = styleElement
|
||||
|
||||
document.body.appendChild(element)
|
||||
document.head.appendChild(styleElement)
|
||||
|
||||
window.setTimeout(function (){
|
||||
element.classList.add('root-element-display')
|
||||
}, 100)
|
||||
|
||||
var timeElement = document.getElementById(data.graphic.name + '-countdown-timer')
|
||||
var time = new Date(data.data.countdown.replace(' ', 'T'))
|
||||
|
||||
function pad(n) { return (n < 10) ? ('0' + n) : n }
|
||||
|
||||
function timer() {
|
||||
var days = 0
|
||||
var hours = 0
|
||||
var mins = 0
|
||||
var secs = 0
|
||||
|
||||
now = new Date()
|
||||
difference = (time - now)
|
||||
|
||||
timeElement = document.getElementById(data.graphic.name + '-countdown-timer')
|
||||
|
||||
if (difference < 0 || !timeElement) {
|
||||
clearInterval(data.timer)
|
||||
if (timeElement) {
|
||||
timeElement.innerHTML = data.data.finished || ''
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (timeElement.tag !== time) {
|
||||
clearInterval(data.timer)
|
||||
return
|
||||
}
|
||||
|
||||
days = Math.floor(difference / (60 * 60 * 1000 * 24) * 1);
|
||||
hours = Math.floor((difference % (60 * 60 * 1000 * 24)) / (60 * 60 * 1000) );
|
||||
mins = Math.floor(((difference % (60 * 60 * 1000 * 24)) % (60 * 60 * 1000)) / (60 * 1000) * 1);
|
||||
secs = Math.floor((((difference % (60 * 60 * 1000 * 24)) % (60 * 60 * 1000)) % (60 * 1000)) / 1000 * 1);
|
||||
|
||||
var text = pad(hours) + ':' + pad(mins) + ':' + pad(secs);
|
||||
if (days > 0) {
|
||||
text = days.toString() + ' dag' + (days > 1 && 'a' || '') + ' ' + text;
|
||||
}
|
||||
timeElement.innerHTML = text
|
||||
}
|
||||
|
||||
if (timeElement) {
|
||||
timeElement.tag = time
|
||||
timer()
|
||||
data.timer = setInterval(timer, 1000)
|
||||
}
|
||||
}
|
20
app/frontend/text.js
Normal file
20
app/frontend/text.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
module.exports = function(data) {
|
||||
var element = document.createElement('div')
|
||||
element.innerHTML = data.html
|
||||
element.id = data.graphic.name
|
||||
element.classList.add('root-element')
|
||||
|
||||
var styleElement = document.createElement('style')
|
||||
styleElement.setAttribute('type', 'text/css')
|
||||
styleElement.innerHTML = data.css
|
||||
|
||||
element.tag = styleElement
|
||||
|
||||
document.body.appendChild(element)
|
||||
document.head.appendChild(styleElement)
|
||||
|
||||
window.setTimeout(function (){
|
||||
element.classList.add('root-element-display')
|
||||
}, 100)
|
||||
}
|
|
@ -16,12 +16,16 @@ require('./socket')
|
|||
require('./controller/store')
|
||||
|
||||
const m = require('mithril')
|
||||
const Header = require('./controller/header')
|
||||
const Menu = require('./controller/menu')
|
||||
const Content = require('./controller/content')
|
||||
const Add = require('./controller/add')
|
||||
const Graphic = require('./controller/graphic/controller')
|
||||
|
||||
m.mount(document.getElementById('header'), Header)
|
||||
m.mount(document.getElementById('menu'), Menu)
|
||||
|
||||
m.route(document.getElementById('content'), '/', {
|
||||
'/': {},
|
||||
'/content': Content,
|
||||
'/add': Add,
|
||||
'/graphic/:id': Graphic,
|
||||
});
|
||||
|
|
29
migrations/20160412123858_graphics.js
Normal file
29
migrations/20160412123858_graphics.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* eslint-disable */
|
||||
'use strict';
|
||||
|
||||
exports.up = function(knex, Promise) {
|
||||
return Promise.all([
|
||||
knex.schema.dropTable('store'),
|
||||
knex.schema.createTable('graphics', function(table) {
|
||||
table.increments()
|
||||
table.text('name')
|
||||
table.text('engine')
|
||||
table.text('settings')
|
||||
table.boolean('is_deleted')
|
||||
}),
|
||||
knex.schema.createTable('presets', function(table) {
|
||||
table.increments()
|
||||
table.integer('graphic_id').references('graphics.id')
|
||||
table.text('values')
|
||||
table.integer('sort')
|
||||
table.boolean('is_deleted')
|
||||
}),
|
||||
]);
|
||||
};
|
||||
|
||||
exports.down = function(knex, Promise) {
|
||||
return Promise.all([
|
||||
knex.schema.dropTable('graphics'),
|
||||
knex.schema.dropTable('presets'),
|
||||
]);
|
||||
};
|
|
@ -11,7 +11,7 @@
|
|||
"watch-client:js": "watchify app/client.js -o public/client.js --debug",
|
||||
"build": "npm run build-main:js && npm run build-client:js",
|
||||
"build:watch": "parallelshell \"npm run watch-main:js\" \"npm run watch-client:js\"",
|
||||
"start": "node index.js",
|
||||
"start": "node index.js | bunyan",
|
||||
"start:dev": "nodemon index.js | bunyan"
|
||||
},
|
||||
"repository": {
|
||||
|
|
|
@ -22,6 +22,20 @@ body {
|
|||
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;
|
||||
}
|
||||
div
|
||||
{
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.root-element {
|
||||
opacity: 0;
|
||||
transition: opacity 1s;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<title>CasparCG Client</title>
|
||||
<link href="client.css" rel="stylesheet" />
|
||||
<link href="/client.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<script src="client.js"></script>
|
||||
|
|
|
@ -6,9 +6,12 @@
|
|||
<link href="main.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="row">
|
||||
<div class="small-3 columns" id="menu"></div>
|
||||
<div class="small-9 columns" id="content"></div>
|
||||
<div class="expanded row">
|
||||
<div class="small-12 columns container" id="header"></div>
|
||||
</div>
|
||||
<div class="expanded row">
|
||||
<div class="small-3 columns container" id="menu"></div>
|
||||
<div class="small-9 columns container" id="content"></div>
|
||||
</div>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
|
|
185
public/main.css
185
public/main.css
|
@ -1,4 +1,181 @@
|
|||
a.button {
|
||||
margin: 0 1rem;
|
||||
width: 10rem;
|
||||
}
|
||||
body {
|
||||
background: #3f3f41;
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
/* Container */
|
||||
|
||||
.container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.container-header {
|
||||
font-size: 1.5rem;
|
||||
margin-left: 1rem;
|
||||
color: #777777;
|
||||
}
|
||||
|
||||
.container-panel {
|
||||
border: 1px solid #3f3f3f;
|
||||
background: #2d2d30;
|
||||
padding: 1rem;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
|
||||
.header-list {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header-item-hide {
|
||||
float: right;
|
||||
width: 5rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.header-item-display {
|
||||
background: #070707;
|
||||
color: #eb6e00;
|
||||
border-radius: 6px;
|
||||
padding: 0.5rem 1rem;
|
||||
margin-right: 5.5rem;
|
||||
}
|
||||
|
||||
/* Menu */
|
||||
|
||||
.menu-list {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.menu a {
|
||||
color: #007acc;
|
||||
display: block;
|
||||
border: 1px solid #2d2d30;
|
||||
padding: 0.2rem 0.5rem;
|
||||
}
|
||||
|
||||
.menu a:hover {
|
||||
color: #f1f1f1;
|
||||
border: 1px solid #007acc;
|
||||
}
|
||||
|
||||
.menu-item-add {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
/* Add */
|
||||
|
||||
.panel-add {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.panel-graphic-property-add,
|
||||
.panel-graphic-property-remove {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Graphic */
|
||||
|
||||
.panel-graphic-delete {
|
||||
|
||||
}
|
||||
|
||||
.panel-graphic-settings {
|
||||
float: right;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.panel-graphic-property-item {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.panel-graphic-preset-add {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.panel-graphic-preset {
|
||||
margin-top: 1rem;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.panel-graphic-preset a {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Components */
|
||||
|
||||
.error-box {
|
||||
margin: 0rem 0rem 2rem 0;
|
||||
color: #FF0000;
|
||||
}
|
||||
|
||||
/* Inputs */
|
||||
|
||||
label {
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
textarea {
|
||||
background: #333337;
|
||||
border-color: #3f3f3f;
|
||||
color: #999999;
|
||||
transition-property: none !important;
|
||||
}
|
||||
|
||||
input[type="text"]:hover,
|
||||
textarea:hover {
|
||||
color: #f1f1f1;
|
||||
border-color: #007acc;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
textarea:focus {
|
||||
background: #333337;
|
||||
color: #f1f1f1;
|
||||
border-color: #007acc;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
input[readonly],
|
||||
input[readonly]:hover {
|
||||
background: #2d2d30 !important;
|
||||
border-color: #3f3f3f;
|
||||
}
|
||||
|
||||
select {
|
||||
background: #333337;
|
||||
border-color: #3f3f3f;
|
||||
color: #999999;
|
||||
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;
|
||||
}
|
||||
|
||||
select
|
||||
|
||||
a.button {
|
||||
margin: 0 1rem;
|
||||
width: 10rem;
|
||||
}
|
||||
|
|
1
run.bat
Normal file
1
run.bat
Normal file
|
@ -0,0 +1 @@
|
|||
npm run build && npm start
|
|
@ -37,12 +37,8 @@ shelf.createModel = (attr, opts) => {
|
|||
this.on('fetching', this.checkFetching)
|
||||
},
|
||||
|
||||
remove() {
|
||||
return this.destroy()
|
||||
},
|
||||
|
||||
checkFetching(model, columns, options) {
|
||||
// options.query.where({ is_deleted: false })
|
||||
options.query.where({ is_deleted: false })
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -52,7 +48,7 @@ shelf.createModel = (attr, opts) => {
|
|||
return this.forge(data).save()
|
||||
},
|
||||
|
||||
getSingle(id, withRelated = [], required = true) {
|
||||
getSingle(id, withRelated = [], require = true) {
|
||||
let where = { id: Number(id) || 0 }
|
||||
|
||||
return this.query({ where })
|
||||
|
@ -60,6 +56,8 @@ shelf.createModel = (attr, opts) => {
|
|||
},
|
||||
|
||||
getAll(where = {}, withRelated = []) {
|
||||
where.is_deleted = false
|
||||
|
||||
return this.query({ where })
|
||||
.fetchAll({ withRelated })
|
||||
},
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import _ from 'lodash'
|
||||
import Store from './store/model'
|
||||
|
||||
import { reset, list } from './content/routes'
|
||||
|
||||
export function register(ctx, name, method) {
|
||||
if (_.isPlainObject(method)) {
|
||||
|
@ -10,9 +11,7 @@ export function register(ctx, name, method) {
|
|||
}
|
||||
|
||||
ctx.socket.on(name, async (data) => {
|
||||
if (name !== 'store') {
|
||||
ctx.log.info(`Got event ${name}`)
|
||||
}
|
||||
ctx.log.info('Got event', name)
|
||||
|
||||
try {
|
||||
await method(ctx, data)
|
||||
|
@ -26,9 +25,6 @@ export function register(ctx, name, method) {
|
|||
export async function newConnection(ctx) {
|
||||
ctx.log.info('Got new socket connection')
|
||||
|
||||
let data = await Store.getAll()
|
||||
|
||||
data.forEach(item =>
|
||||
ctx.socket.emit('store', item.toJSON())
|
||||
)
|
||||
list(ctx)
|
||||
reset(ctx)
|
||||
}
|
||||
|
|
|
@ -1,18 +1,55 @@
|
|||
import _ from 'lodash'
|
||||
|
||||
export const active = { }
|
||||
|
||||
function getSocket(ctx, all) {
|
||||
if (all === true) return ctx.io
|
||||
return ctx.socket
|
||||
}
|
||||
|
||||
export function display(ctx, data) {
|
||||
let compiled = _.template(data.html)
|
||||
let html = compiled(data)
|
||||
let compiled = _.template(data.graphic.settings.html)
|
||||
let html = compiled(data.data)
|
||||
|
||||
ctx.io.emit('client.display', {
|
||||
key: 'content',
|
||||
let payload = {
|
||||
graphic: data.graphic,
|
||||
html,
|
||||
css: data.css,
|
||||
})
|
||||
css: data.graphic.settings.css,
|
||||
data: data.data,
|
||||
}
|
||||
|
||||
active[data.graphic.name] = payload
|
||||
ctx.io.emit('client.display', payload)
|
||||
|
||||
list(ctx, true)
|
||||
}
|
||||
|
||||
export function hide(ctx) {
|
||||
export function hide(ctx, data) {
|
||||
delete active[data.name]
|
||||
|
||||
ctx.io.emit('client.hide', {
|
||||
key: 'content',
|
||||
name: data.name,
|
||||
})
|
||||
|
||||
list(ctx, true)
|
||||
}
|
||||
|
||||
function generateDisplayText(item) {
|
||||
if (item.graphic.engine === 'countdown') {
|
||||
return `${item.data[item.graphic.settings.main]} - ${item.data.countdown}`
|
||||
}
|
||||
return item.data[item.graphic.settings.main]
|
||||
}
|
||||
|
||||
export function list(ctx, all) {
|
||||
let payload = Object.keys(active).map(key => ({
|
||||
name: active[key].graphic.name,
|
||||
display: generateDisplayText(active[key]),
|
||||
}))
|
||||
|
||||
getSocket(ctx, all).emit('content.list', payload)
|
||||
}
|
||||
|
||||
export function reset(ctx) {
|
||||
ctx.socket.emit('client.reset', _.values(active))
|
||||
}
|
||||
|
|
4
server/io/engine/routes.js
Normal file
4
server/io/engine/routes.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
export function all(ctx) {
|
||||
ctx.socket.emit('engine.all', ['text', 'countdown'])
|
||||
}
|
30
server/io/graphic/model.js
Normal file
30
server/io/graphic/model.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
import bookshelf from '../../bookshelf'
|
||||
|
||||
/* Graphic model:
|
||||
{
|
||||
id,
|
||||
name,
|
||||
engine,
|
||||
settings,
|
||||
is_deleted,
|
||||
}
|
||||
*/
|
||||
|
||||
const Graphic = bookshelf.createModel({
|
||||
tableName: 'graphics',
|
||||
|
||||
format(attributes) {
|
||||
attributes.settings = JSON.stringify(attributes.settings)
|
||||
return attributes
|
||||
},
|
||||
|
||||
parse(attributes) {
|
||||
if (attributes.settings) {
|
||||
attributes.settings = JSON.parse(attributes.settings)
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
}, {
|
||||
})
|
||||
|
||||
export default Graphic
|
65
server/io/graphic/routes.js
Normal file
65
server/io/graphic/routes.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
import Graphic from './model'
|
||||
|
||||
function getSocket(ctx, all) {
|
||||
if (all === true) return ctx.io
|
||||
return ctx.socket
|
||||
}
|
||||
|
||||
export async function all(ctx, all) {
|
||||
let data = await Graphic.getAll()
|
||||
|
||||
getSocket(ctx, all).emit('graphic.all', data.toJSON())
|
||||
}
|
||||
|
||||
export async function single(ctx, data, all) {
|
||||
if (!data || !data.id) {
|
||||
ctx.log.warn('called graphic get single but no id specified')
|
||||
return
|
||||
}
|
||||
|
||||
let graphic = await Graphic.getSingle(data.id)
|
||||
|
||||
getSocket(ctx, all).emit('graphic.single', graphic.toJSON())
|
||||
}
|
||||
|
||||
export async function create(ctx, data) {
|
||||
data.settings = {}
|
||||
data.is_deleted = false
|
||||
|
||||
if (data.engine === 'countdown') {
|
||||
data.settings.html = `<span id="${data.name}-countdown-timer">countdown appears here</span>`
|
||||
data.settings.main = 'text'
|
||||
}
|
||||
|
||||
await Graphic.create(data)
|
||||
|
||||
await all(ctx, true)
|
||||
}
|
||||
|
||||
export async function remove(ctx, data) {
|
||||
if (!data || !data.id) {
|
||||
ctx.log.warn('called graphic get single but no id specified')
|
||||
return
|
||||
}
|
||||
|
||||
let graphic = await Graphic.getSingle(data.id)
|
||||
graphic.set({ is_deleted: true })
|
||||
await graphic.save()
|
||||
|
||||
await all(ctx, true)
|
||||
}
|
||||
|
||||
export async function update(ctx, data) {
|
||||
if (!data || !data.id) {
|
||||
ctx.log.warn('called graphic update but no id specified')
|
||||
return
|
||||
}
|
||||
|
||||
let graphic = await Graphic.getSingle(data.id)
|
||||
|
||||
graphic.set(data)
|
||||
|
||||
await graphic.save()
|
||||
|
||||
await single(ctx, data, true)
|
||||
}
|
30
server/io/preset/model.js
Normal file
30
server/io/preset/model.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
import bookshelf from '../../bookshelf'
|
||||
|
||||
/* Preset model:
|
||||
{
|
||||
id,
|
||||
graphic_id,
|
||||
values,
|
||||
sort,
|
||||
is_deleted,
|
||||
}
|
||||
*/
|
||||
|
||||
const Preset = bookshelf.createModel({
|
||||
tableName: 'presets',
|
||||
|
||||
format(attributes) {
|
||||
attributes.values = JSON.stringify(attributes.values)
|
||||
return attributes
|
||||
},
|
||||
|
||||
parse(attributes) {
|
||||
if (attributes.values) {
|
||||
attributes.values = JSON.parse(attributes.values)
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
}, {
|
||||
})
|
||||
|
||||
export default Preset
|
43
server/io/preset/routes.js
Normal file
43
server/io/preset/routes.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
import Preset from './model'
|
||||
|
||||
function getSocket(ctx, all) {
|
||||
if (all === true) return ctx.io
|
||||
return ctx.socket
|
||||
}
|
||||
|
||||
export async function all(ctx, payload, all) {
|
||||
let id = Number(payload.graphic_id || payload.id)
|
||||
|
||||
let data = await Preset.getAll({ graphic_id: id })
|
||||
|
||||
getSocket(ctx, all).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, true)
|
||||
}
|
||||
|
||||
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, true)
|
||||
}
|
|
@ -2,7 +2,9 @@ import logger from '../../log'
|
|||
import { register, newConnection } from './connection'
|
||||
|
||||
import * as content from './content/routes'
|
||||
import * as store from './store/routes'
|
||||
import * as engine from './engine/routes'
|
||||
import * as graphic from './graphic/routes'
|
||||
import * as preset from './preset/routes'
|
||||
|
||||
function onConnection(server, data) {
|
||||
const io = server.socket
|
||||
|
@ -16,7 +18,9 @@ function onConnection(server, data) {
|
|||
newConnection(ctx)
|
||||
|
||||
register(ctx, 'content', content)
|
||||
register(ctx, 'store', store.updateStore)
|
||||
register(ctx, 'engine', engine)
|
||||
register(ctx, 'graphic', graphic)
|
||||
register(ctx, 'preset', preset)
|
||||
}
|
||||
|
||||
export default onConnection
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
import bookshelf from '../../bookshelf'
|
||||
|
||||
const Store = bookshelf.createModel({
|
||||
tableName: 'store',
|
||||
|
||||
format(attributes) {
|
||||
attributes.value = JSON.stringify(attributes.value)
|
||||
return attributes
|
||||
},
|
||||
|
||||
parse(attributes) {
|
||||
attributes.value = JSON.parse(attributes.value)
|
||||
return attributes
|
||||
}
|
||||
}, {
|
||||
getSingle(name, withRelated = [], required = true) {
|
||||
let where = { name }
|
||||
|
||||
return this.query({ where })
|
||||
.fetch({ require, withRelated })
|
||||
},
|
||||
})
|
||||
|
||||
export default Store
|
|
@ -1,11 +0,0 @@
|
|||
import Store from './model'
|
||||
|
||||
export async function updateStore(ctx, data) {
|
||||
let item = await Store.getSingle(data.name)
|
||||
|
||||
item.set('value', data.value)
|
||||
|
||||
await item.save()
|
||||
|
||||
ctx.socket.broadcast.emit('store', item.toJSON())
|
||||
}
|
2
update_run.bat
Normal file
2
update_run.bat
Normal file
|
@ -0,0 +1,2 @@
|
|||
git pull
|
||||
npm install && run
|
Loading…
Reference in a new issue