782 lines
21 KiB
JavaScript
782 lines
21 KiB
JavaScript
// vim:set noexpandtab:
|
|
/**************
|
|
SYSTEM INCLUDES
|
|
**************/
|
|
var http = require("http")
|
|
// var sys = require('sys');
|
|
var async = require("async")
|
|
var sanitizer = require("sanitizer")
|
|
var compression = require("compression")
|
|
var express = require("express")
|
|
var conf = require("./config.js").server
|
|
var headerBarUrl = require("./config.js").headerBarUrl
|
|
var logoUrl = require("./config.js").logoUrl
|
|
|
|
/**************
|
|
LOCAL INCLUDES
|
|
**************/
|
|
var rooms = require("./lib/rooms.js")
|
|
var data = require("./lib/data.js").db
|
|
|
|
/**************
|
|
GLOBALS
|
|
**************/
|
|
//Map of sids to user_names
|
|
var sids_to_user_names = []
|
|
|
|
/**************
|
|
SETUP EXPRESS
|
|
**************/
|
|
var app = express()
|
|
var router = express.Router()
|
|
|
|
app.use(compression())
|
|
app.use(conf.baseurl, router)
|
|
|
|
router.use(express.static(__dirname + "/client"))
|
|
|
|
var server = require("http").Server(app)
|
|
server.listen(conf.port)
|
|
|
|
console.log("Server running at http://127.0.0.1:" + conf.port + "/")
|
|
|
|
/**************
|
|
SETUP Socket.IO
|
|
**************/
|
|
var io = require("socket.io")(server, {
|
|
path: conf.baseurl == "/" ? "" : conf.baseurl + "/socket.io",
|
|
})
|
|
|
|
/**************
|
|
ROUTES
|
|
**************/
|
|
router.get("/", function (req, res) {
|
|
//console.log(req.header('host'));
|
|
url = req.header("host") + req.baseUrl
|
|
|
|
var connected = io.sockets.connected
|
|
clientsCount = Object.keys(connected).length
|
|
|
|
res.render("home.jade", {
|
|
url: url,
|
|
headerBarUrl: headerBarUrl,
|
|
logoUrl: logoUrl,
|
|
connected: clientsCount
|
|
})
|
|
})
|
|
|
|
router.get("/demo", function (req, res) {
|
|
res.render("index.jade", {
|
|
pageTitle: "Post-it - demo",
|
|
headerBarUrl: headerBarUrl,
|
|
logoUrl: logoUrl,
|
|
demo: true
|
|
})
|
|
})
|
|
|
|
router.get("/:id", function (req, res) {
|
|
res.render("index.jade", {
|
|
pageTitle: "Post-it - " + req.params.id,
|
|
headerBarUrl: headerBarUrl,
|
|
logoUrl: logoUrl
|
|
})
|
|
})
|
|
|
|
/**************
|
|
SOCKET.I0
|
|
**************/
|
|
//sanitizes text
|
|
function scrub(text) {
|
|
if (typeof text != "undefined" && text !== null) {
|
|
//clip the string if it is too long
|
|
if (text.length > 65535) {
|
|
text = text.substr(0, 65535)
|
|
}
|
|
|
|
return sanitizer.sanitize(text)
|
|
} else {
|
|
return null
|
|
}
|
|
}
|
|
|
|
io.sockets.on("connection", function (client) {
|
|
client.on("message", function (message) {
|
|
//console.log(message.action + " -- " + sys.inspect(message.data) );
|
|
|
|
var clean_data = {}
|
|
var clean_message = {}
|
|
var message_out = {}
|
|
|
|
if (!message.action) return
|
|
|
|
switch (message.action) {
|
|
case "initializeMe":
|
|
initClient(client)
|
|
break
|
|
|
|
case "joinRoom":
|
|
joinRoom(client, message.data, function (clients) {
|
|
client.json.send({ action: "roomAccept", data: "" })
|
|
})
|
|
|
|
break
|
|
|
|
case "moveCard":
|
|
//report to all other browsers
|
|
message_out = {
|
|
action: message.action,
|
|
data: {
|
|
id: scrub(message.data.id),
|
|
position: {
|
|
left: scrub(message.data.position.left),
|
|
top: scrub(message.data.position.top),
|
|
},
|
|
},
|
|
}
|
|
|
|
broadcastToRoom(client, message_out)
|
|
|
|
// console.log("-----" + message.data.id);
|
|
// console.log(JSON.stringify(message.data));
|
|
|
|
getRoom(client, function (room) {
|
|
db.cardSetXY(room, message.data.id, message.data.position.left, message.data.position.top)
|
|
})
|
|
|
|
break
|
|
|
|
case "createCard":
|
|
data = message.data
|
|
clean_data = {}
|
|
clean_data.text = scrub(data.text)
|
|
clean_data.id = scrub(data.id)
|
|
clean_data.x = scrub(data.x)
|
|
clean_data.y = scrub(data.y)
|
|
clean_data.rot = scrub(data.rot)
|
|
clean_data.colour = scrub(data.colour)
|
|
|
|
getRoom(client, function (room) {
|
|
createCard(
|
|
room,
|
|
clean_data.id,
|
|
clean_data.text,
|
|
clean_data.x,
|
|
clean_data.y,
|
|
clean_data.rot,
|
|
clean_data.colour
|
|
)
|
|
})
|
|
|
|
message_out = {
|
|
action: "createCard",
|
|
data: clean_data,
|
|
}
|
|
|
|
//report to all other browsers
|
|
broadcastToRoom(client, message_out)
|
|
break
|
|
|
|
case "editCard":
|
|
clean_data = {}
|
|
clean_data.value = scrub(message.data.value)
|
|
clean_data.id = scrub(message.data.id)
|
|
|
|
//send update to database
|
|
getRoom(client, function (room) {
|
|
db.cardEdit(room, clean_data.id, clean_data.value)
|
|
})
|
|
|
|
message_out = {
|
|
action: "editCard",
|
|
data: clean_data,
|
|
}
|
|
|
|
broadcastToRoom(client, message_out)
|
|
|
|
break
|
|
|
|
case "deleteCard":
|
|
clean_message = {
|
|
action: "deleteCard",
|
|
data: { id: scrub(message.data.id) },
|
|
}
|
|
|
|
getRoom(client, function (room) {
|
|
db.deleteCard(room, clean_message.data.id)
|
|
})
|
|
|
|
//report to all other browsers
|
|
broadcastToRoom(client, clean_message)
|
|
|
|
break
|
|
|
|
case "createColumn":
|
|
clean_message = { data: scrub(message.data) }
|
|
|
|
getRoom(client, function (room) {
|
|
db.createColumn(room, clean_message.data, function () {})
|
|
})
|
|
|
|
broadcastToRoom(client, clean_message)
|
|
|
|
break
|
|
|
|
case "deleteColumn":
|
|
getRoom(client, function (room) {
|
|
db.deleteColumn(room)
|
|
})
|
|
broadcastToRoom(client, { action: "deleteColumn" })
|
|
|
|
break
|
|
|
|
case "updateColumns":
|
|
var columns = message.data
|
|
|
|
if (!(columns instanceof Array)) break
|
|
|
|
var clean_columns = []
|
|
|
|
for (var i in columns) {
|
|
clean_columns[i] = scrub(columns[i])
|
|
}
|
|
getRoom(client, function (room) {
|
|
db.setColumns(room, clean_columns)
|
|
})
|
|
|
|
broadcastToRoom(client, { action: "updateColumns", data: clean_columns })
|
|
|
|
break
|
|
|
|
case "changeTheme":
|
|
clean_message = {}
|
|
clean_message.data = scrub(message.data)
|
|
|
|
getRoom(client, function (room) {
|
|
db.setTheme(room, clean_message.data)
|
|
})
|
|
|
|
clean_message.action = "changeTheme"
|
|
|
|
broadcastToRoom(client, clean_message)
|
|
break
|
|
|
|
case "setUserName":
|
|
clean_message = {}
|
|
|
|
clean_message.data = scrub(message.data)
|
|
|
|
setUserName(client, clean_message.data)
|
|
|
|
var msg = {}
|
|
msg.action = "nameChangeAnnounce"
|
|
msg.data = { sid: client.id, user_name: clean_message.data }
|
|
broadcastToRoom(client, msg)
|
|
break
|
|
|
|
case "addSticker":
|
|
var cardId = scrub(message.data.cardId)
|
|
var stickerId = scrub(message.data.stickerId)
|
|
|
|
getRoom(client, function (room) {
|
|
db.addSticker(room, cardId, stickerId)
|
|
})
|
|
|
|
broadcastToRoom(client, { action: "addSticker", data: { cardId: cardId, stickerId: stickerId } })
|
|
break
|
|
|
|
case "setBoardSize":
|
|
var size = {}
|
|
size.width = scrub(message.data.width)
|
|
size.height = scrub(message.data.height)
|
|
|
|
getRoom(client, function (room) {
|
|
db.setBoardSize(room, size)
|
|
})
|
|
|
|
broadcastToRoom(client, { action: "setBoardSize", data: size })
|
|
break
|
|
|
|
case "exportTxt":
|
|
exportBoard("txt", client, message.data)
|
|
break
|
|
|
|
case "exportCsv":
|
|
exportBoard("csv", client, message.data)
|
|
break
|
|
|
|
case "exportJson":
|
|
exportJson(client, message.data)
|
|
break
|
|
|
|
case "importJson":
|
|
importJson(client, message.data)
|
|
break
|
|
|
|
case "createRevision":
|
|
createRevision(client, message.data)
|
|
break
|
|
|
|
case "deleteRevision":
|
|
deleteRevision(client, message.data)
|
|
break
|
|
|
|
case "exportRevision":
|
|
exportRevision(client, message.data)
|
|
break
|
|
|
|
default:
|
|
//console.log('unknown action');
|
|
break
|
|
}
|
|
})
|
|
|
|
client.on("disconnect", function () {
|
|
leaveRoom(client)
|
|
})
|
|
|
|
//tell all others that someone has connected
|
|
//client.broadcast('someone has connected');
|
|
})
|
|
|
|
/**************
|
|
FUNCTIONS
|
|
**************/
|
|
function initClient(client) {
|
|
//console.log ('initClient Started');
|
|
getRoom(client, function (room) {
|
|
db.getAllCards(room, function (cards) {
|
|
client.json.send({
|
|
action: "initCards",
|
|
data: cards,
|
|
})
|
|
})
|
|
|
|
db.getAllColumns(room, function (columns) {
|
|
client.json.send({
|
|
action: "initColumns",
|
|
data: columns,
|
|
})
|
|
})
|
|
|
|
db.getRevisions(room, function (revisions) {
|
|
client.json.send({
|
|
action: "initRevisions",
|
|
data: revisions !== null ? Object.keys(revisions) : new Array(),
|
|
})
|
|
})
|
|
|
|
db.getTheme(room, function (theme) {
|
|
if (theme === null) theme = "bigcards"
|
|
|
|
client.json.send({
|
|
action: "changeTheme",
|
|
data: theme,
|
|
})
|
|
})
|
|
|
|
db.getBoardSize(room, function (size) {
|
|
if (size !== null) {
|
|
client.json.send({
|
|
action: "setBoardSize",
|
|
data: size,
|
|
})
|
|
}
|
|
})
|
|
|
|
roommates_clients = rooms.room_clients(room)
|
|
roommates = []
|
|
|
|
var j = 0
|
|
for (var i in roommates_clients) {
|
|
if (roommates_clients[i].id != client.id) {
|
|
roommates[j] = {
|
|
sid: roommates_clients[i].id,
|
|
user_name: sids_to_user_names[roommates_clients[i].id],
|
|
}
|
|
j++
|
|
}
|
|
}
|
|
|
|
//console.log('initialusers: ' + roommates);
|
|
client.json.send({
|
|
action: "initialUsers",
|
|
data: roommates,
|
|
})
|
|
})
|
|
}
|
|
|
|
function joinRoom(client, room, successFunction) {
|
|
var msg = {}
|
|
msg.action = "join-announce"
|
|
msg.data = { sid: client.id, user_name: client.user_name }
|
|
|
|
rooms.add_to_room_and_announce(client, room, msg)
|
|
successFunction()
|
|
}
|
|
|
|
function leaveRoom(client) {
|
|
//console.log (client.id + ' just left');
|
|
var msg = {}
|
|
msg.action = "leave-announce"
|
|
msg.data = { sid: client.id }
|
|
rooms.remove_from_all_rooms_and_announce(client, msg)
|
|
|
|
delete sids_to_user_names[client.id]
|
|
}
|
|
|
|
function broadcastToRoom(client, message) {
|
|
rooms.broadcast_to_roommates(client, message)
|
|
}
|
|
|
|
//----------------CARD FUNCTIONS
|
|
function createCard(room, id, text, x, y, rot, colour) {
|
|
var card = {
|
|
id: id,
|
|
colour: colour,
|
|
rot: rot,
|
|
x: x,
|
|
y: y,
|
|
text: text,
|
|
sticker: null,
|
|
}
|
|
|
|
db.createCard(room, id, card)
|
|
}
|
|
|
|
function roundRand(max) {
|
|
return Math.floor(Math.random() * max)
|
|
}
|
|
|
|
//------------ROOM STUFF
|
|
// Get Room name for the given Session ID
|
|
function getRoom(client, callback) {
|
|
room = rooms.get_room(client)
|
|
//console.log( 'client: ' + client.id + " is in " + room);
|
|
callback(room)
|
|
}
|
|
|
|
function setUserName(client, name) {
|
|
client.user_name = name
|
|
sids_to_user_names[client.id] = name
|
|
//console.log('sids to user names: ');
|
|
console.dir(sids_to_user_names)
|
|
}
|
|
|
|
function cleanAndInitializeDemoRoom() {
|
|
// DUMMY DATA
|
|
db.clearRoom("/demo", function () {
|
|
db.createColumn("/demo", "Pas commencé")
|
|
db.createColumn("/demo", "Commencé")
|
|
db.createColumn("/demo", "En test")
|
|
db.createColumn("/demo", "Validation")
|
|
db.createColumn("/demo", "Terminé")
|
|
|
|
createCard("/demo", "card1", "Salut, c'est fun", roundRand(600), roundRand(300), Math.random() * 10 - 5, "yellow")
|
|
createCard(
|
|
"/demo",
|
|
"card2",
|
|
"Salut, c'est une nouvelle histoire.",
|
|
roundRand(600),
|
|
roundRand(300),
|
|
Math.random() * 10 - 5,
|
|
"white"
|
|
)
|
|
createCard("/demo", "card3", ".", roundRand(600), roundRand(300), Math.random() * 10 - 5, "blue")
|
|
createCard("/demo", "card4", ".", roundRand(600), roundRand(300), Math.random() * 10 - 5, "green")
|
|
|
|
createCard("/demo", "card5", "Salut, c'est fun", roundRand(600), roundRand(300), Math.random() * 10 - 5, "yellow")
|
|
createCard(
|
|
"/demo",
|
|
"card6",
|
|
"Salut, c'est un nouveau mémo.",
|
|
roundRand(600),
|
|
roundRand(300),
|
|
Math.random() * 10 - 5,
|
|
"yellow"
|
|
)
|
|
createCard("/demo", "card7", ".", roundRand(600), roundRand(300), Math.random() * 10 - 5, "blue")
|
|
createCard("/demo", "card8", ".", roundRand(600), roundRand(300), Math.random() * 10 - 5, "green")
|
|
})
|
|
}
|
|
|
|
// Export board in txt or csv
|
|
function exportBoard(format, client, data) {
|
|
var result = new Array()
|
|
getRoom(client, function (room) {
|
|
db.getAllCards(room, function (cards) {
|
|
db.getAllColumns(room, function (columns) {
|
|
var text = new Array()
|
|
var cols = {}
|
|
if (columns.length > 0) {
|
|
for (var i = 0; i < columns.length; i++) {
|
|
cols[columns[i]] = new Array()
|
|
for (var j = 0; j < cards.length; j++) {
|
|
if (i === 0) {
|
|
if (cards[j]["x"] < (i + 1) * data) {
|
|
cols[columns[i]].push(cards[j])
|
|
}
|
|
} else if (i + 1 === columns.length) {
|
|
if (cards[j]["x"] >= i * data) {
|
|
cols[columns[i]].push(cards[j])
|
|
}
|
|
} else if (cards[j]["x"] >= i * data && cards[j]["x"] < (i + 1) * data) {
|
|
cols[columns[i]].push(cards[j])
|
|
}
|
|
}
|
|
cols[columns[i]].sort(function (a, b) {
|
|
if (a["y"] === b["y"]) {
|
|
return a["x"] - b["x"]
|
|
} else {
|
|
return a["y"] - b["y"]
|
|
}
|
|
})
|
|
}
|
|
if (format === "txt") {
|
|
for (var i = 0; i < columns.length; i++) {
|
|
if (i === 0) {
|
|
text.push("# " + columns[i])
|
|
} else {
|
|
text.push("\n# " + columns[i])
|
|
}
|
|
for (var j = 0; j < cols[columns[i]].length; j++) {
|
|
text.push("- " + cols[columns[i]][j]["text"])
|
|
}
|
|
}
|
|
} else if (format === "csv") {
|
|
var max = 0
|
|
var line = new Array()
|
|
var patt_vuln = new RegExp("^[=+-@]")
|
|
for (var i = 0; i < columns.length; i++) {
|
|
if (cols[columns[i]].length > max) {
|
|
max = cols[columns[i]].length
|
|
}
|
|
var val = columns[i].replace(/"/g, '""')
|
|
if (patt_vuln.test(val)) {
|
|
// prevent CSV Formula Injection
|
|
var val = "'" + val
|
|
}
|
|
line.push('"' + val + '"')
|
|
}
|
|
text.push(line.join(","))
|
|
for (var j = 0; j < max; j++) {
|
|
line = new Array()
|
|
for (var i = 0; i < columns.length; i++) {
|
|
var val = cols[columns[i]][j] !== undefined ? cols[columns[i]][j]["text"].replace(/"/g, '""') : ""
|
|
if (patt_vuln.test(val)) {
|
|
// prevent CSV Formula Injection
|
|
var val = "'" + val
|
|
}
|
|
line.push('"' + val + '"')
|
|
}
|
|
text.push(line.join(","))
|
|
}
|
|
}
|
|
} else {
|
|
for (var j = 0; j < cards.length; j++) {
|
|
if (format === "txt") {
|
|
text.push("- " + cards[j]["text"])
|
|
} else if (format === "csv") {
|
|
text.push('"' + cards[j]["text"].replace(/"/g, '""') + '"\n')
|
|
}
|
|
}
|
|
}
|
|
var result
|
|
if (format === "txt" || format === "csv") {
|
|
result = text.join("\n")
|
|
} else if (format === "json") {
|
|
result = JSON.stringify(cols)
|
|
}
|
|
client.json.send({
|
|
action: "export",
|
|
data: {
|
|
filename: room.replace("/", "") + "." + format,
|
|
text: result,
|
|
},
|
|
})
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
// Export board in json, suitable for import
|
|
function exportJson(client, data) {
|
|
var result = new Array()
|
|
getRoom(client, function (room) {
|
|
db.getAllCards(room, function (cards) {
|
|
db.getAllColumns(room, function (columns) {
|
|
db.getTheme(room, function (theme) {
|
|
db.getBoardSize(room, function (size) {
|
|
if (theme === null) theme = "bigcards"
|
|
if (size === null) size = { width: data.width, height: data.height }
|
|
result = JSON.stringify({
|
|
cards: cards,
|
|
columns: columns,
|
|
theme: theme,
|
|
size: size,
|
|
})
|
|
client.json.send({
|
|
action: "export",
|
|
data: {
|
|
filename: room.replace("/", "") + ".json",
|
|
text: result,
|
|
},
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
// Import board from json
|
|
function importJson(client, data) {
|
|
getRoom(client, function (room) {
|
|
db.clearRoom(room, function () {
|
|
db.getAllCards(room, function (cards) {
|
|
for (var i = 0; i < cards.length; i++) {
|
|
db.deleteCard(room, cards[i].id)
|
|
}
|
|
|
|
cards = data.cards
|
|
var cards2 = new Array()
|
|
for (var i = 0; i < cards.length; i++) {
|
|
var card = cards[i]
|
|
if (
|
|
card.id !== undefined &&
|
|
card.colour !== undefined &&
|
|
card.rot !== undefined &&
|
|
card.x !== undefined &&
|
|
card.y !== undefined &&
|
|
card.text !== undefined &&
|
|
card.sticker !== undefined
|
|
) {
|
|
var c = {
|
|
id: card.id,
|
|
colour: card.colour,
|
|
rot: card.rot,
|
|
x: card.x,
|
|
y: card.y,
|
|
text: scrub(card.text),
|
|
sticker: card.sticker,
|
|
}
|
|
db.createCard(room, c.id, c)
|
|
cards2.push(c)
|
|
}
|
|
}
|
|
var msg = { action: "initCards", data: cards2 }
|
|
broadcastToRoom(client, msg)
|
|
client.json.send(msg)
|
|
})
|
|
|
|
db.getAllColumns(room, function (columns) {
|
|
for (var i = 0; i < columns.length; i++) {
|
|
db.deleteColumn(room)
|
|
}
|
|
|
|
columns = data.columns
|
|
var columns2 = new Array()
|
|
for (var i = 0; i < columns.length; i++) {
|
|
var column = scrub(columns[i])
|
|
if (typeof column === "string") {
|
|
db.createColumn(room, column)
|
|
columns2.push(column)
|
|
}
|
|
}
|
|
msg = { action: "initColumns", data: columns2 }
|
|
broadcastToRoom(client, msg)
|
|
client.json.send(msg)
|
|
})
|
|
|
|
var size = data.size
|
|
if (size.width !== undefined && size.height !== undefined) {
|
|
size = { width: scrub(size.width), height: scrub(size.height) }
|
|
db.setBoardSize(room, size)
|
|
msg = { action: "setBoardSize", data: size }
|
|
broadcastToRoom(client, msg)
|
|
client.json.send(msg)
|
|
}
|
|
|
|
data.theme = scrub(data.theme)
|
|
if (data.theme === "smallcards" || data.theme === "bigcards") {
|
|
db.setTheme(room, data.theme)
|
|
msg = { action: "changeTheme", data: data.theme }
|
|
broadcastToRoom(client, msg)
|
|
client.json.send(msg)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
//
|
|
|
|
function createRevision(client, data) {
|
|
var result = new Array()
|
|
getRoom(client, function (room) {
|
|
db.getAllCards(room, function (cards) {
|
|
db.getAllColumns(room, function (columns) {
|
|
db.getTheme(room, function (theme) {
|
|
db.getBoardSize(room, function (size) {
|
|
if (theme === null) theme = "bigcards"
|
|
if (size === null) size = { width: data.width, height: data.height }
|
|
result = {
|
|
cards: cards,
|
|
columns: columns,
|
|
theme: theme,
|
|
size: size,
|
|
}
|
|
var timestamp = Date.now()
|
|
db.getRevisions(room, function (revisions) {
|
|
if (revisions === null) revisions = {}
|
|
revisions[timestamp + ""] = result
|
|
db.setRevisions(room, revisions)
|
|
msg = { action: "addRevision", data: timestamp }
|
|
broadcastToRoom(client, msg)
|
|
client.json.send(msg)
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
function deleteRevision(client, timestamp) {
|
|
getRoom(client, function (room) {
|
|
db.getRevisions(room, function (revisions) {
|
|
if (revisions !== null && revisions[timestamp + ""] !== undefined) {
|
|
delete revisions[timestamp + ""]
|
|
db.setRevisions(room, revisions)
|
|
}
|
|
msg = { action: "deleteRevision", data: timestamp }
|
|
broadcastToRoom(client, msg)
|
|
client.json.send(msg)
|
|
})
|
|
})
|
|
}
|
|
|
|
function exportRevision(client, timestamp) {
|
|
getRoom(client, function (room) {
|
|
db.getRevisions(room, function (revisions) {
|
|
if (revisions !== null && revisions[timestamp + ""] !== undefined) {
|
|
client.json.send({
|
|
action: "export",
|
|
data: {
|
|
filename: room.replace("/", "") + "-" + timestamp + ".json",
|
|
text: JSON.stringify(revisions[timestamp + ""]),
|
|
},
|
|
})
|
|
} else {
|
|
client.json.send({
|
|
action: "message",
|
|
data: "Unable to find revision " + timestamp + ".",
|
|
})
|
|
}
|
|
})
|
|
})
|
|
}
|
|
/**************
|
|
SETUP DATABASE ON FIRST RUN
|
|
**************/
|
|
// (runs only once on startup)
|
|
var db = new data(function () {
|
|
cleanAndInitializeDemoRoom()
|
|
})
|