Huge changes in webpage display. ACE now added to present a convenient editor

This commit is contained in:
Dominic Bosch 2014-02-21 17:18:58 +01:00
parent 724af9bb82
commit a8f85d74c3
49 changed files with 952 additions and 524 deletions

View file

@ -61,14 +61,10 @@ initRouting = ( port ) =>
# - **`GET` to _"/"_:** Static redirect to the _"webpages/public"_ directory
app.use '/', express.static path.resolve __dirname, '..', 'webpages', 'public'
# - **`GET` to _"/admin"_:** Only admins can issue requests to this handler
# - **`GET` to _"/admin"_:** Displays the admin console if user is admin
app.get '/admin', requestHandler.handleAdmin
# - **`GET` to _"/forge\_modules"_:** Webpage that lets the user create modules
app.get '/forge_modules', requestHandler.handleForgeModules
# - **`GET` to _"/forge\_rules"_:** Webpage that lets the user create rules
app.get '/forge_rules', requestHandler.handleForgeRules
# - **`GET` to _"/invoke\_event"_:** Webpage that lets the user invoke events
app.get '/invoke_event', requestHandler.handleInvokeEvent
# - **`GET` to _"/forge"_:** Displays different forge pages
app.get '/forge', requestHandler.handleForge
# POST Requests
@ -78,8 +74,10 @@ initRouting = ( port ) =>
app.post '/login', requestHandler.handleLogin
# - **`POST` to _"/logout"_:** User will be logged out
app.post '/logout', requestHandler.handleLogout
# - **`POST` to _"/user"_:** User requests are possible for all users with an account
# - **`POST` to _"/usercommand"_:** User requests are possible for all users with an account
app.post '/usercommand', requestHandler.handleUserCommand
# - **`POST` to _"/admincommand"_:** Admin requests are only possible for admins
app.post '/admincommand', requestHandler.handleAdminCommand
server = app.listen parseInt( port ) || 8111 # inbound event channel

View file

@ -756,7 +756,6 @@ because we only store hashes of passwords for security6 reasons.
@param {String} password
@param {function} cb
###
#TODO verify and test whole function
exports.loginUser = ( userId, password, cb ) =>
@log.info "DB | User '#{ userId }' tries to log in"
fCheck = ( pw ) =>

View file

@ -27,15 +27,21 @@ mustache = require 'mustache'
crypto = require 'crypto-js'
# Prepare the user command handlers which are invoked via HTTP requests.
dirHandlers = path.resolve __dirname, '..', 'webpages', 'handlers'
exports = module.exports = ( args ) =>
log = args.logger
@log = args.logger
# Register the request service
@userRequestHandler = args[ 'request-service' ]
# Register the shutdown handler to the admin command.
@objAdminCmds =
shutdown: ( args, answerHandler ) ->
answerHandler.answerSuccess 'Shutting down... BYE!'
setTimeout args[ 'shutdown-function' ], 500
db args
# Load the standard users from the user config file
users = JSON.parse fs.readFileSync path.resolve __dirname, '..', 'config', 'users.json'
db.storeUser user for user in users
module.exports
@ -52,11 +58,11 @@ objects.*
@public handleEvent( *req, resp* )
###
exports.handleEvent = ( req, resp ) =>
exports.handleEvent = ( req, resp ) ->
body = ''
req.on 'data', ( data ) ->
body += data
req.on 'end', =>
req.on 'end', ->
obj = qs.parse body
# If required event properties are present we process the event #
if obj and obj.event and obj.eventid
@ -67,6 +73,7 @@ exports.handleEvent = ( req, resp ) =>
###
Associates the user object with the session if login is successful.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
@ -75,25 +82,22 @@ objects.*
@public handleLogin( *req, resp* )
###
exports.handleLogin = ( req, resp ) ->
exports.handleLogin = ( req, resp ) =>
body = ''
req.on 'data', ( data ) -> body += data
req.on 'end', ->
if not req.session or not req.session.user
obj = qs.parse body
db.loginUser obj.username, obj.password, ( err, usr ) ->
if(err)
# Tapping on fingers, at least in log...
log.warn err, "RH | AUTH-UH-OH (#{obj.username})"
else
# no error, so we can associate the user object from the DB to the session
req.session.user = usr
if req.session.user
resp.send 'OK!'
else
resp.send 401, 'NO!'
else
resp.send 'Welcome ' + req.session.user.name + '!'
req.on 'end', =>
obj = qs.parse body
db.loginUser obj.username, obj.password, ( err, usr ) =>
if err
# Tapping on fingers, at least in log...
@log.warn "RH | AUTH-UH-OH ( #{ obj.username } ): #{ err.message }"
else
# no error, so we can associate the user object from the DB to the session
req.session.user = usr
if req.session.user
resp.send 'OK!'
else
resp.send 401, 'NO!'
###
A post request retrieved on this handler causes the user object to be
@ -119,106 +123,101 @@ Resolves the path to a handler webpage.
@param {String} name
###
getHandlerPath = ( name ) ->
path.resolve __dirname, '..', 'webpages', 'handlers', name + '.html'
path.join dirHandlers, name + '.html'
###
Resolves the path to a handler webpage and returns it as a string.
Fetches a template.
@private getHandlerFileAsString( *name* )
@private getTemplate( *name* )
@param {String} name
###
getHandlerFileAsString = ( name ) ->
fs.readFileSync getHandlerPath( name ), 'utf8'
###
Fetches an include file.
@private getIncludeFileAsString( *name* )
@param {String} name
###
getIncludeFileAsString = ( name ) ->
pth = path.resolve __dirname, '..', 'webpages', 'handlers', 'includes', name + '.html'
getTemplate = ( name ) ->
pth = path.join dirHandlers, 'templates', name + '.html'
fs.readFileSync pth, 'utf8'
###
Renders a page depending on the user session and returns it.
Fetches a script.
@private renderPage( *name, sess* )
@private getScript( *name* )
@param {String} name
###
getScript = ( name ) ->
pth = path.join dirHandlers, 'js', name + '.js'
fs.readFileSync pth, 'utf8'
###
Fetches remote scripts snippets.
@private getRemoteScripts( *name* )
@param {String} name
###
getRemoteScripts = ( name ) ->
pth = path.join dirHandlers, 'remote-scripts', name + '.html'
fs.readFileSync pth, 'utf8'
###
Renders a page, with helps of mustache, depending on the user session and returns it.
@private renderPage( *name, sess, msg* )
@param {String} name
@param {Object} sess
@param {Object} msg
###
renderPage = ( name, sess, msg ) ->
template = getHandlerFileAsString name
menubar = getIncludeFileAsString 'menubar'
requires = getIncludeFileAsString 'requires'
view =
user: sess.user,
head_requires: requires,
div_menubar: menubar,
renderPage = ( name, req, resp, msg ) ->
# Grab the skeleton
pathSkel = path.join dirHandlers, 'skeleton.html'
skeleton = fs.readFileSync pathSkel, 'utf8'
code = 200
data =
message: msg
mustache.render template, view
# Try to grab the script belonging to this page. But don't bother if it's not existing
try
script = getScript name
# Try to grab the remote scripts belonging to this page. But don't bother if it's not existing
try
remote_scripts = getRemoteScripts name
# Now try to find the page the user requested.
try
content = getTemplate name
catch err
# If the page doesn't exist we return the error page, load the error script into it
# and render the error page with some additional data
content = getTemplate 'error'
script = getScript 'error'
code = 404
data =
message: 'Invalid Page!'
content = mustache.render content, data
if req.session.user
menubar = getTemplate 'menubar'
view =
user: req.session.user,
content: content,
script: script,
remote_scripts: remote_scripts,
menubar: menubar
resp.send code, mustache.render skeleton, view
###
Sends the desired page or the login to the user.
Present the desired forge page to the user.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public renderPageOrLogin( *req, resp, pagename* )
@param {String} pagename
@public handleForge( *req, resp* )
###
sendLoginOrPage = ( pagename, req, resp ) ->
if !req.session
req.session = {}
if !req.session.user
pagename = 'login'
# resp.send renderPage pagename, req.session
# else
# resp.sendfile getHandlerPath 'login'
resp.send renderPage pagename, req.session
###
Present the module forge to the user.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public handleForgeModules( *req, resp* )
###
exports.handleForgeModules = ( req, resp ) ->
sendLoginOrPage 'forge_modules', req, resp
###
Present the rules forge to the user.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public handleForgeRules( *req, resp* )
###
exports.handleForgeRules = ( req, resp ) ->
sendLoginOrPage 'forge_rules', req, resp
###
Present the event invoke page to the user.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public handleInvokeEvent( *req, resp* )
###
exports.handleInvokeEvent = ( req, resp ) ->
sendLoginOrPage 'push_event', req, resp
exports.handleForge = ( req, resp ) ->
page = req.query.page
if not req.session.user
page = 'login'
renderPage page, req, resp
###
Handles the user command requests.
@ -230,23 +229,41 @@ objects.*
@public handleUser( *req, resp* )
###
exports.handleUserCommand = ( req, resp ) ->
exports.handleUserCommand = ( req, resp ) =>
if not req.session or not req.session.user
resp.send 401, 'Login first!'
else
body = ''
req.on 'data', ( data ) ->
body += data
req.on 'end', ->
req.on 'end', =>
obj = qs.parse body
console.log obj
@userRequestHandler req.session.user, obj, ( err, obj) ->
console.log 'user request handler sent answer!'
console.log obj
if !err
resp.send 'yay!'
@userRequestHandler req.session.user, obj, ( err, obj ) ->
if err
resp.send 404, 'Rethink your request!'
else
resp.send 404, 'Command unknown!'
resp.send obj
###
Present the admin console to the user if he's allowed to see it.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public handleForge( *req, resp* )
###
exports.handleAdmin = ( req, resp ) ->
if not req.session.user
page = 'login'
# TODO isAdmin should come from the db role
else if req.session.user.isAdmin isnt "true"
page = 'login'
msg = 'You need to be admin!'
else
page = 'admin'
renderPage page, req, resp, msg
###
Handles the admin command requests.
@ -256,13 +273,13 @@ the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public handleAdmin( *req, resp* )
@public handleAdminCommand( *req, resp* )
###
exports.handleAdmin = ( req, resp ) =>
exports.handleAdminCommand = ( req, resp ) =>
if req.session and req.session.user
if req.session.user.isAdmin is "true"
q = req.query
log.info 'RH | Received admin request: ' + req.originalUrl
@log.info 'RH | Received admin request: ' + req.originalUrl
if q.cmd
@objAdminCmds[q.cmd]? q, answerHandler req, resp, true
else
@ -273,7 +290,7 @@ exports.handleAdmin = ( req, resp ) =>
resp.sendfile getHandlerPath 'login'
#TODO remove this ugly legacy blob
answerHandler = (req, resp, ntbr) ->
request = req
response = resp

View file

@ -178,7 +178,7 @@ Shuts down the server.
###
shutDown = () =>
@log.warn 'RS | Received shut down command!'
# db?.shutDown()
db?.shutDown()
engine?.shutDown()
# We need to call process.exit() since the express server in the http-listener
# can't be stopped gracefully. Why would you stop this system anyways!??

3
compile_pagescripts.sh Executable file
View file

@ -0,0 +1,3 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo "Started listening on file changes to compile them!..."
coffee -wc -o $DIR/webpages/handlers/js $DIR/webpages/handlers/coffee

View file

@ -35,6 +35,11 @@ exports.addListener = function( evt, eh ) {
exports.processRequest = function( user, obj, cb ) {
console.log('module manager needs to process request: ');
console.log(obj.command);
var answ = {
test: 'object',
should: 'work'
}
cb(null, answ);
}
exports.requireFromString = function(src, name, dir) {

View file

@ -58,13 +58,12 @@ HTTP Listener
_this.log.info('HL | no session backbone');
app.use('/', express["static"](path.resolve(__dirname, '..', 'webpages', 'public')));
app.get('/admin', requestHandler.handleAdmin);
app.get('/forge_modules', requestHandler.handleForgeModules);
app.get('/forge_rules', requestHandler.handleForgeRules);
app.get('/invoke_event', requestHandler.handleInvokeEvent);
app.get('/forge', requestHandler.handleForge);
app.post('/event', requestHandler.handleEvent);
app.post('/login', requestHandler.handleLogin);
app.post('/logout', requestHandler.handleLogout);
app.post('/usercommand', requestHandler.handleUserCommand);
app.post('/admincommand', requestHandler.handleAdminCommand);
server = app.listen(parseInt(port) || 8111);
server.on('listening', function() {
var addr;

View file

@ -11,7 +11,7 @@ Request Handler
(function() {
var answerHandler, crypto, db, exports, fs, getHandlerFileAsString, getHandlerPath, getIncludeFileAsString, mustache, path, qs, renderPage, sendLoginOrPage,
var answerHandler, crypto, db, dirHandlers, exports, fs, getHandlerPath, getRemoteScripts, getScript, getTemplate, mustache, path, qs, renderPage,
_this = this;
db = require('./persistence');
@ -26,9 +26,11 @@ Request Handler
crypto = require('crypto-js');
dirHandlers = path.resolve(__dirname, '..', 'webpages', 'handlers');
exports = module.exports = function(args) {
var log, user, users, _i, _len;
log = args.logger;
var user, users, _i, _len;
_this.log = args.logger;
_this.userRequestHandler = args['request-service'];
_this.objAdminCmds = {
shutdown: function(args, answerHandler) {
@ -77,6 +79,7 @@ Request Handler
};
/*
Associates the user object with the session if login is successful.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
@ -95,23 +98,19 @@ Request Handler
});
return req.on('end', function() {
var obj;
if (!req.session || !req.session.user) {
obj = qs.parse(body);
return db.loginUser(obj.username, obj.password, function(err, usr) {
if (err) {
log.warn(err, "RH | AUTH-UH-OH (" + obj.username + ")");
} else {
req.session.user = usr;
}
if (req.session.user) {
return resp.send('OK!');
} else {
return resp.send(401, 'NO!');
}
});
} else {
return resp.send('Welcome ' + req.session.user.name + '!');
}
obj = qs.parse(body);
return db.loginUser(obj.username, obj.password, function(err, usr) {
if (err) {
_this.log.warn("RH | AUTH-UH-OH ( " + obj.username + " ): " + err.message);
} else {
req.session.user = usr;
}
if (req.session.user) {
return resp.send('OK!');
} else {
return resp.send(401, 'NO!');
}
});
});
};
@ -144,127 +143,119 @@ Request Handler
getHandlerPath = function(name) {
return path.resolve(__dirname, '..', 'webpages', 'handlers', name + '.html');
return path.join(dirHandlers, name + '.html');
};
/*
Resolves the path to a handler webpage and returns it as a string.
Fetches a template.
@private getHandlerFileAsString( *name* )
@private getTemplate( *name* )
@param {String} name
*/
getHandlerFileAsString = function(name) {
return fs.readFileSync(getHandlerPath(name), 'utf8');
};
/*
Fetches an include file.
@private getIncludeFileAsString( *name* )
@param {String} name
*/
getIncludeFileAsString = function(name) {
getTemplate = function(name) {
var pth;
pth = path.resolve(__dirname, '..', 'webpages', 'handlers', 'includes', name + '.html');
pth = path.join(dirHandlers, 'templates', name + '.html');
return fs.readFileSync(pth, 'utf8');
};
/*
Renders a page depending on the user session and returns it.
Fetches a script.
@private renderPage( *name, sess* )
@private getScript( *name* )
@param {String} name
*/
getScript = function(name) {
var pth;
pth = path.join(dirHandlers, 'js', name + '.js');
return fs.readFileSync(pth, 'utf8');
};
/*
Fetches remote scripts snippets.
@private getRemoteScripts( *name* )
@param {String} name
*/
getRemoteScripts = function(name) {
var pth;
pth = path.join(dirHandlers, 'remote-scripts', name + '.html');
return fs.readFileSync(pth, 'utf8');
};
/*
Renders a page, with helps of mustache, depending on the user session and returns it.
@private renderPage( *name, sess, msg* )
@param {String} name
@param {Object} sess
@param {Object} msg
*/
renderPage = function(name, sess, msg) {
var menubar, requires, template, view;
template = getHandlerFileAsString(name);
menubar = getIncludeFileAsString('menubar');
requires = getIncludeFileAsString('requires');
view = {
user: sess.user,
head_requires: requires,
div_menubar: menubar,
renderPage = function(name, req, resp, msg) {
var code, content, data, err, menubar, pathSkel, remote_scripts, script, skeleton, view;
pathSkel = path.join(dirHandlers, 'skeleton.html');
skeleton = fs.readFileSync(pathSkel, 'utf8');
code = 200;
data = {
message: msg
};
return mustache.render(template, view);
try {
script = getScript(name);
} catch (_error) {}
try {
remote_scripts = getRemoteScripts(name);
} catch (_error) {}
try {
content = getTemplate(name);
} catch (_error) {
err = _error;
content = getTemplate('error');
script = getScript('error');
code = 404;
data = {
message: 'Invalid Page!'
};
}
content = mustache.render(content, data);
if (req.session.user) {
menubar = getTemplate('menubar');
}
view = {
user: req.session.user,
content: content,
script: script,
remote_scripts: remote_scripts,
menubar: menubar
};
return resp.send(code, mustache.render(skeleton, view));
};
/*
Sends the desired page or the login to the user.
Present the desired forge page to the user.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public renderPageOrLogin( *req, resp, pagename* )
@param {String} pagename
@public handleForge( *req, resp* )
*/
sendLoginOrPage = function(pagename, req, resp) {
if (!req.session) {
req.session = {};
}
exports.handleForge = function(req, resp) {
var page;
page = req.query.page;
if (!req.session.user) {
pagename = 'login';
page = 'login';
}
return resp.send(renderPage(pagename, req.session));
};
/*
Present the module forge to the user.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public handleForgeModules( *req, resp* )
*/
exports.handleForgeModules = function(req, resp) {
return sendLoginOrPage('forge_modules', req, resp);
};
/*
Present the rules forge to the user.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public handleForgeRules( *req, resp* )
*/
exports.handleForgeRules = function(req, resp) {
return sendLoginOrPage('forge_rules', req, resp);
};
/*
Present the event invoke page to the user.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public handleInvokeEvent( *req, resp* )
*/
exports.handleInvokeEvent = function(req, resp) {
return sendLoginOrPage('push_event', req, resp);
return renderPage(page, req, resp);
};
/*
@ -291,20 +282,42 @@ Request Handler
return req.on('end', function() {
var obj;
obj = qs.parse(body);
console.log(obj);
return this.userRequestHandler(req.session.user, obj, function(err, obj) {
console.log('user request handler sent answer!');
console.log(obj);
if (!err) {
return resp.send('yay!');
return _this.userRequestHandler(req.session.user, obj, function(err, obj) {
if (err) {
return resp.send(404, 'Rethink your request!');
} else {
return resp.send(404, 'Command unknown!');
return resp.send(obj);
}
});
});
}
};
/*
Present the admin console to the user if he's allowed to see it.
*Requires
the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest)
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public handleForge( *req, resp* )
*/
exports.handleAdmin = function(req, resp) {
var msg, page;
if (!req.session.user) {
page = 'login';
} else if (req.session.user.isAdmin !== "true") {
page = 'login';
msg = 'You need to be admin!';
} else {
page = 'admin';
}
return renderPage(page, req, resp, msg);
};
/*
Handles the admin command requests.
@ -313,16 +326,16 @@ Request Handler
and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse)
objects.*
@public handleAdmin( *req, resp* )
@public handleAdminCommand( *req, resp* )
*/
exports.handleAdmin = function(req, resp) {
exports.handleAdminCommand = function(req, resp) {
var q, _base, _name;
if (req.session && req.session.user) {
if (req.session.user.isAdmin === "true") {
q = req.query;
log.info('RH | Received admin request: ' + req.originalUrl);
_this.log.info('RH | Received admin request: ' + req.originalUrl);
if (q.cmd) {
return typeof (_base = _this.objAdminCmds)[_name = q.cmd] === "function" ? _base[_name](q, answerHandler(req, resp, true)) : void 0;
} else {

View file

@ -172,6 +172,9 @@ WebAPI-ECA Engine
shutDown = function() {
_this.log.warn('RS | Received shut down command!');
if (db != null) {
db.shutDown();
}
if (engine != null) {
engine.shutDown();
}

View file

@ -2,6 +2,7 @@ http = require 'http'
path = require 'path'
events = require 'events'
cp = require 'child_process'
qs = require 'querystring'
logger = require path.join '..', 'js-coffee', 'logging'
log = logger.getLogger
nolog: true
@ -15,17 +16,19 @@ createRequest = ( query, origUrl ) ->
req
createLoggedInRequest = ( query, origUrl ) ->
req = createRequest()
req = createRequest query, origUrl
req.session =
user:
name: 'unittester'
username: 'unittester'
password: 'unittesterpw'
req
createAdminRequest = ( query, origUrl ) ->
req = createRequest()
req.session =
user:
name: 'adminunittester'
username: 'adminunittester'
password: 'adminunittesterpw'
isAdmin: true
req
@ -45,50 +48,25 @@ createResponse = ( cb ) ->
cb code, msg
exports.setUp = ( cb ) =>
@db = require path.join '..', 'js-coffee', 'persistence'
args =
logger: log
args[ 'db-port' ] = 6379
@db args
@rh = require path.join '..', 'js-coffee', 'request-handler'
cb()
# test: ( test ) ->
# test.expect 1
# args =
# logger: log
# args[ 'request-service' ] = ( usr, obj, cb ) ->
# console.log 'got a request to answer!'
# args[ 'shutdown-function' ] = () ->
# console.log 'request handler wants us to shutdown!'
# @rh args
# test.ok true, 'yay'
# test.done()
exports.tearDown = ( cb ) =>
@db.shutDown()
cb()
exports.events =
setUp: ( cb ) =>
@db = require path.join '..', 'js-coffee', 'persistence'
args =
logger: log
args[ 'db-port' ] = 6379
@db args
@db.purgeEventQueue()
cb()
tearDown: ( cb ) =>
@db.shutDown()
cb()
testEvent: ( test ) =>
test.expect 1
doPolling = true
fPollEventQueue = ( cb ) =>
console.log 'polling'
if doPolling
@db.popEvent cb
setTimeout fPollEventQueue, 5
fPollEventQueue ( err, obj ) ->
console.log 'got something? :'
console.log err
console.log obj
testCorrectEvent: ( test ) =>
test.expect 2
args =
logger: log
@ -98,21 +76,229 @@ exports.events =
test.ok false, 'testEvent should not cause a system shutdown'
@rh args
evt = 'event=unittest&eventid=unittest0'
oEvt =
event: 'unittest'
eventid: 'ut234'
data: 'a lot of data'
semaphore = 2
fPopEvent = () =>
fCb = ( err, obj ) ->
test.deepEqual obj, oEvt, 'Caught event is not what we expected'
if --semaphore is 0
test.done()
@db.popEvent fCb
req = createRequest()
console.log req
resp = createResponse ( code, msg ) ->
console.log 'got response: ' + msg
doPolling = false
@rh.handleEvent req, resp
fWait = () ->
postRequestData req, evt
setTimeout fWait, 50
# req.emit 'data', evt
# req.emit 'end'
test.strictEqual code, 200
if --semaphore is 0
test.done()
@rh.handleEvent req, resp # set the handler to listening
postRequestData req, qs.stringify oEvt # emit the data post event
setTimeout fPopEvent, 200 # try to fetch the db entry
testIncorrectEvent: ( test ) =>
test.expect 2
args =
logger: log
args[ 'request-service' ] = ( usr, obj, cb ) ->
test.ok false, 'testEvent should not cause a service request call'
args[ 'shutdown-function' ] = () ->
test.ok false, 'testEvent should not cause a system shutdown'
@rh args
oEvt =
event: 'unittest'
data: 'event misses eventid property'
semaphore = 2
fPopEvent = () =>
fCb = ( err, obj ) ->
test.deepEqual obj, null, 'We caught an event!?'
if --semaphore is 0
test.done()
@db.popEvent fCb
req = createRequest()
resp = createResponse ( code, msg ) ->
test.strictEqual code, 400
if --semaphore is 0
test.done()
@rh.handleEvent req, resp # set the handler to listening
postRequestData req, qs.stringify oEvt # emit the data post event
setTimeout fPopEvent, 200 # try to fetch the db entry
exports.session =
setUp: ( cb ) =>
@user =
username: 'unittester'
password: 'unittesterpw'
@db = require path.join '..', 'js-coffee', 'persistence'
args =
logger: log
args[ 'db-port' ] = 6379
@db args
@db.storeUser @user
cb()
tearDown: ( cb ) =>
@db.deleteUser @user.username
@db.shutDown()
cb()
testLoginAndOut: ( test ) =>
test.expect 6
args =
logger: log
args[ 'request-service' ] = ( usr, obj, cb ) ->
test.ok false, 'testEvent should not cause a service request call'
args[ 'shutdown-function' ] = () ->
test.ok false, 'testEvent should not cause a system shutdown'
@rh args
req = createRequest()
resp = createResponse ( code, msg ) =>
# Check Login
test.strictEqual code, 200, 'Login failed'
test.deepEqual req.session.user, @user, 'Session user not what we expected'
req = createLoggedInRequest()
resp = createResponse ( code, msg ) =>
# Check Login again
test.strictEqual code, 200, 'Login again did nothing different'
test.deepEqual req.session.user, @user, 'Session user not what we expected after relogin'
req = createRequest()
resp = createResponse ( code, msg ) ->
# Check logout
test.strictEqual code, 200, 'Logout failed'
test.strictEqual req.session.user, null, 'User not removed from session'
test.done()
@rh.handleLogout req, resp # set the handler to listening
@rh.handleLogin req, resp # set the handler to listening
postRequestData req, qs.stringify @user # emit the data post event
@rh.handleLogin req, resp # set the handler to listening
postRequestData req, qs.stringify @user # emit the data post event
test.ok true, 'yay'
testWrongLogin: ( test ) =>
test.expect 2
args =
logger: log
args[ 'request-service' ] = ( usr, obj, cb ) ->
test.ok false, 'testEvent should not cause a service request call'
args[ 'shutdown-function' ] = () ->
test.ok false, 'testEvent should not cause a system shutdown'
@rh args
req = createRequest()
resp = createResponse ( code, msg ) =>
test.strictEqual code, 401, 'Login did not fail?'
test.strictEqual req.session.user, undefined, 'User in session?'
test.done()
usr =
username: @user.username
password: 'wrongpassword'
@rh.handleLogin req, resp # set the handler to listening
postRequestData req, qs.stringify usr # emit the data post event
exports.testLoginOrPage = ( test ) =>
test.expect 3
args =
logger: log
args[ 'request-service' ] = ( usr, obj, cb ) ->
test.ok false, 'testEvent should not cause a service request call'
args[ 'shutdown-function' ] = () ->
test.ok false, 'testEvent should not cause a system shutdown'
@rh args
req = createRequest()
req.query =
page: 'forge_event'
resp = createResponse ( code, msg ) =>
# Ensure we have to login first
test.ok msg.indexOf( 'document.title = \'Login\'' ) > 0, 'Didn\'t get login page?'
req = createLoggedInRequest()
req.query =
page: 'forge_event'
resp = createResponse ( code, msg ) =>
# After being logged in we should get the expected page
test.ok msg.indexOf( 'document.title = \'Event Forge!\'' ) > 0, 'Didn\' get forge page?'
req = createLoggedInRequest()
req.query =
page: 'wrongpage'
resp = createResponse ( code, msg ) =>
# A wrong page request should give back an error page
test.ok msg.indexOf( 'document.title = \'Error!\'' ) > 0, 'Didn\' get forge page?'
test.done()
@rh.handleForge req, resp # set the handler to listening
@rh.handleForge req, resp # set the handler to listening
@rh.handleForge req, resp # set the handler to listening
exports.testUserCommandsNoLogin = ( test ) =>
test.expect 1
args =
logger: log
args[ 'request-service' ] = ( usr, obj, cb ) ->
test.ok false, 'testEvent should not cause a service request call'
args[ 'shutdown-function' ] = () ->
test.ok false, 'testEvent should not cause a system shutdown'
@rh args
req = createRequest()
resp = createResponse ( code, msg ) =>
test.strictEqual code, 401, 'Login did not fail?'
test.done()
@rh.handleUserCommand req, resp # set the handler to listening
exports.testUserCommands = ( test ) =>
test.expect 3
oReqData =
command: 'get_something'
# store_action
# store_event
# store_rule
# get_eventmodules
# get_actionmodules
oRespData =
some: 'very'
important: 'data'
args =
logger: log
args[ 'request-service' ] = ( usr, obj, cb ) ->
test.ok true, 'Yay we got the request!'
cb null, oRespData
args[ 'shutdown-function' ] = () ->
test.ok false, 'testEvent should not cause a system shutdown'
@rh args
req = createLoggedInRequest()
resp = createResponse ( code, msg ) =>
test.strictEqual code, 200, 'Service wasn\'t happy with our request'
test.deepEqual msg, oRespData, 'Service didn\'t return expected'
test.done()
@rh.handleUserCommand req, resp # set the handler to listening
postRequestData req, qs.stringify oReqData # emit the data post event

View file

@ -0,0 +1,7 @@
fOnLoad = () ->
document.title = 'Error!'
$( '#pagetitle' ).text 'Error processing your request!'
$( '#pagetitle' ).attr 'class', 'error'
window.addEventListener 'load', fOnLoad, true

View file

@ -0,0 +1,23 @@
fOnLoad = () ->
document.title = 'Event Forge!'
$( '#pagetitle' ).text 'Invoke your custom event!'
editor = ace.edit "editor"
editor.setTheme "ace/theme/monokai"
editor.getSession().setMode "ace/mode/json"
editor.setShowPrintMargin false
$('#but_submit').click () ->
try
data = JSON.parse editor.getValue()
$.post('/event', data)
.done ( data ) ->
alert data
.fail (err) ->
alert 'Posting of event failed: ' + err.responseText
catch err
alert 'You have errors in your JSON object!'
window.addEventListener 'load', fOnLoad, true

View file

@ -0,0 +1,54 @@
fOnLoad = () ->
document.title = 'Forge Event Poller'
$( '#pagetitle' ).text 'Forge your custom event poller!'
editor = ace.edit "editor"
editor.setTheme "ace/theme/monokai"
editor.getSession().setMode "ace/mode/coffee"
editor.setShowPrintMargin false
$( '#editor_mode' ).change ( el ) ->
if $( this ).val() is '0'
editor.getSession().setMode "ace/mode/coffee"
else
editor.getSession().setMode "ace/mode/javascript"
$( '#but_submit' ).click () ->
try
data = JSON.parse editor.getValue()
$.post('/event', data)
.done ( data ) ->
alert data
.fail (err) ->
alert 'Posting of event failed: ' + err.responseText
catch err
alert 'You have errors in your JSON object!'
fChangeCrosses = () ->
$( '#listParams img' ).each ( id ) ->
par = $( this ).parent 'tr'
if par.is ':last-child' or par.is ':only-child'
$( this ).hide()
else
$( this ).show()
$( '#listParams' ).on 'click', 'img', () ->
par = $( this ).parent 'tr'
if not par.is ':last-child'
par.remove()
fChangeCrosses()
$( '#listParams' ).on 'keyup', 'input', ->
console.log 'weees'
if $( this ).parent( 'tr' ).is ':last-child'
tr = $ '<tr>'
td = tr.append 'td'
td.append $( '<img>' ).attr 'src', 'red_cross_small.png'
td.append $( '<input>' ).attr 'type', 'text'
$( '#listParams' ).append tr
fChangeCrosses()
fChangeCrosses()
window.addEventListener 'load', fOnLoad, true

View file

@ -0,0 +1,21 @@
fOnLoad = () ->
document.title = 'Login'
$( '#pagetitle' ).text 'Login!'
if not window.CryptoJS
$( '#info' ).text 'CryptoJS library missing! Are you connected to the internet?'
$( '#but_submit' ).click () ->
hp = CryptoJS.SHA3 $( '#password' ).val(),
outputLength: 512
data =
username: $( '#username' ).val()
password: hp.toString()
$.post( '/login', data )
.done ( data ) ->
window.location.href = document.URL
.fail ( err ) ->
alert 'Authentication not successful!'
window.addEventListener 'load', fOnLoad, true

View file

@ -1,17 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Command Result</title>
{{{head_requires}}}
</head>
<body>
{{{div_menubar}}}
<div id="mainbody">
<div id="pagetitle">Hi {{user.username}}, response to your command:</div>
<p>
{{message}}
</p>
</div>
<div id="info"></div>
</body>
</html>

View file

@ -1,17 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Error!</title>
{{{head_requires}}}
</head>
<body>
{{{div_menubar}}}
<div id="mainbody">
<div id="pagetitle">Sorry {{user.username}}, there was an error!</div>
<p>
Error: {{message}}
</p>
</div>
<div id="info"></div>
</body>
</html>

View file

@ -1,11 +0,0 @@
<link rel="stylesheet" type="text/css" href="style.css">
<script src='//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js' type='text/javascript'></script>
<script type='text/javascript'>
var checkIncludes = function () {
if( window.$ === undefined ) {
document.getElementById('info').innerHTML = 'Not all required libraries have been loaded. Are you connected to the internet?';
}
};
window.addEventListener( "load" , checkIncludes, true );
</script>

View file

@ -0,0 +1,13 @@
// Generated by CoffeeScript 1.6.3
(function() {
var fOnLoad;
fOnLoad = function() {
document.title = 'Error!';
$('#pagetitle').text('Error processing your request!');
return $('#pagetitle').attr('class', 'error');
};
window.addEventListener('load', fOnLoad, true);
}).call(this);

View file

@ -0,0 +1,5 @@
// Generated by CoffeeScript 1.6.3
(function() {
}).call(this);

View file

@ -0,0 +1,31 @@
// Generated by CoffeeScript 1.6.3
(function() {
var fOnLoad;
fOnLoad = function() {
var editor;
document.title = 'Event Forge!';
$('#pagetitle').text('Invoke your custom event!');
editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/json");
editor.setShowPrintMargin(false);
return $('#but_submit').click(function() {
var data, err;
try {
data = JSON.parse(editor.getValue());
return $.post('/event', data).done(function(data) {
return alert(data);
}).fail(function(err) {
return alert('Posting of event failed: ' + err.responseText);
});
} catch (_error) {
err = _error;
return alert('You have errors in your JSON object!');
}
});
};
window.addEventListener('load', fOnLoad, true);
}).call(this);

View file

@ -0,0 +1,70 @@
// Generated by CoffeeScript 1.6.3
(function() {
var fOnLoad;
fOnLoad = function() {
var editor, fChangeCrosses;
document.title = 'Forge Event Poller';
$('#pagetitle').text('Forge your custom event poller!');
editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/coffee");
editor.setShowPrintMargin(false);
$('#editor_mode').change(function(el) {
if ($(this).val() === '0') {
return editor.getSession().setMode("ace/mode/coffee");
} else {
return editor.getSession().setMode("ace/mode/javascript");
}
});
$('#but_submit').click(function() {
var data, err;
try {
data = JSON.parse(editor.getValue());
return $.post('/event', data).done(function(data) {
return alert(data);
}).fail(function(err) {
return alert('Posting of event failed: ' + err.responseText);
});
} catch (_error) {
err = _error;
return alert('You have errors in your JSON object!');
}
});
fChangeCrosses = function() {
return $('#listParams img').each(function(id) {
var par;
par = $(this).parent('tr');
if (par.is(':last-child' || par.is(':only-child'))) {
return $(this).hide();
} else {
return $(this).show();
}
});
};
$('#listParams').on('click', 'img', function() {
var par;
par = $(this).parent('tr');
if (!par.is(':last-child')) {
par.remove();
}
return fChangeCrosses();
});
$('#listParams').on('keyup', 'input', function() {
var td, tr;
console.log('weees');
if ($(this).parent('tr').is(':last-child')) {
tr = $('<tr>');
td = tr.append('td');
td.append($('<img>').attr('src', 'red_cross_small.png'));
td.append($('<input>').attr('type', 'text'));
$('#listParams').append(tr);
return fChangeCrosses();
}
});
return fChangeCrosses();
};
window.addEventListener('load', fOnLoad, true);
}).call(this);

View file

@ -0,0 +1,5 @@
// Generated by CoffeeScript 1.6.3
(function() {
}).call(this);

View file

@ -0,0 +1,30 @@
// Generated by CoffeeScript 1.6.3
(function() {
var fOnLoad;
fOnLoad = function() {
document.title = 'Login';
$('#pagetitle').text('Login!');
if (!window.CryptoJS) {
$('#info').text('CryptoJS library missing! Are you connected to the internet?');
}
return $('#but_submit').click(function() {
var data, hp;
hp = CryptoJS.SHA3($('#password').val(), {
outputLength: 512
});
data = {
username: $('#username').val(),
password: hp.toString()
};
return $.post('/login', data).done(function(data) {
return window.location.href = document.URL;
}).fail(function(err) {
return alert('Authentication not successful!');
});
});
};
window.addEventListener('load', fOnLoad, true);
}).call(this);

View file

@ -1,34 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Login</title>
{{{head_requires}}}
<!-- <link rel="stylesheet" type="text/css" href="style.css">
<script src='//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js' type='text/javascript'></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha3.js"></script>
-->
</head>
<body>
<div id="mainbody">
<div id="pagetitle">Login</div>
<table>
<tr><td>username: </td><td><input type="text" id="username" autofocus /></td></tr>
<tr><td>password: </td><td><input type="password" id="password" /></td></tr>
</table>
<button id="but_submit">login</button>
</div>
<div id="info"></div>
<script>
$('#but_submit').click(function() {
var hashedPassword = (CryptoJS.SHA3($('#password').val(), { outputLength: 512 })).toString();
$.post('/login', { username: $('#username').val(), password: hashedPassword })
.done(function(data) {
window.location.href = document.URL;
})
.fail(function(err) {
alert('Authentication not successful!');
});
});
</script>
</body>
</html>

View file

@ -1,40 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Invoke an event</title>
{{{head_requires}}}
</head>
<body>
{{{div_menubar}}}
<div id="mainbody">
<div id="pagetitle">Hi {{user.username}}, invoke your own Event!</div>
<p>
<textarea id="textarea_event" rows="20" cols="50">
{
"event": "mail",
"eventid": "mail_0",
"payload": {
"subject": "hello"
}
}
</textarea>
</p>
<p>
<button id="but_submit">invoke</button>
</p>
</div>
<div id="info"></div>
<script>
$('#but_submit').click(function() {
$.post('/event', JSON.parse($('#textarea_event').val()))
.done(function(data) {
alert(data);
})
.fail(function(err) {
alert('Posting of event failed: ' + err.responseText);
});
});
</script>
</body>
</html>

View file

@ -0,0 +1 @@
<script src="ace-src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>

View file

@ -0,0 +1 @@
<script src="ace-src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>

View file

@ -0,0 +1 @@
<script src="ace-src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>

View file

@ -0,0 +1 @@
<script src="ace-src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>

View file

@ -0,0 +1 @@
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha3.js"></script>

View file

@ -0,0 +1,30 @@
<!DOCTYPE HTML>
<html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src='//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js' type='text/javascript'></script>
<script type='text/javascript'>
var checkIncludes = function () {
if( window.$ === undefined ) {
document.getElementById('info').innerHTML = 'Not all required libraries have been loaded. Are you connected to the internet?';
}
};
window.addEventListener( "load" , checkIncludes, true );
</script>
<script type='text/javascript'>
{{{script}}}
</script>
{{{remote_scripts}}}
</head>
<body>
{{{menubar}}}
<div id="info"></div>
<div id="mainbody">
<div id="pagetitle"></div>
{{{content}}}
</div>
</body>
</html>

View file

@ -0,0 +1 @@
admin page yay

View file

@ -0,0 +1 @@
<div class="error">Error: {{{message}}}</div>

View file

@ -0,0 +1,14 @@
<p>
<div id="editor">
{
"event": "mail",
"eventid": "mail_0",
"payload": {
"subject": "hello"
}
}
</div>
</p>
<p>
<button id="but_submit">invoke</button>
</p>

View file

@ -0,0 +1,40 @@
<select id="editor_mode">
<option value="0">CoffeeScript</option>
<option value="1">JavaScript</option>
</select>
<table id="editor_table">
<tr>
<td id="editor_col" valign="top">
<p>
<div id="editor">
#
# EmailYak EVENT POLLER
#
url = 'https://api.emailyak.com/v1/' + credentials.key + '/json/get/new/email/'
exports.newMail = ( pushEvent ) ->
needle.get url, ( err, resp, body ) ->
if not err and resp.statusCode is 200
mails = JSON.parse( body ).Emails
pushEvent mail for mail in mails
else
log.error 'Error in EmailYak EM newMail: ' + err.message
</div>
</p>
<p>
<button id="but_submit">save</button>
</p>
</td>
<td id="params_col" valign="top">
This event poller requires user-specific properties:
<table id="tableParams">
<tr>
<td><img src="red_cross_small.png"></td>
<td><input type="text" /></td>
</tr>
</table>
</td>
</tr>
</table>

View file

@ -0,0 +1,12 @@
<div class="error">{{{message}}}</div>
<table>
<tr>
<td>username: </td>
<td><input type="text" id="username" autofocus /></td>
</tr>
<tr>
<td>password: </td>
<td><input type="password" id="password" /></td>
</tr>
</table>
<button id="but_submit">login</button>

View file

@ -1,17 +1,21 @@
<div id="menubar">
<div id="menubar_modules">forge modules</div>
<div id="menubar_rules">forge rules</div>
<div id="menubar_event">invoke events</div>
<div id="menubar_events">forge event poller</div>
<div id="menubar_actions">forge action invoker</div>
<div id="menubar_rules">forge rules</div>
<div id="menubar_logout">logout</div>
<script>
$( '#menubar_modules' ).click( function() {
window.location.href = 'forge_modules';
$( '#menubar_event' ).click( function() {
window.location.href = 'forge?page=forge_event';
});
$( '#menubar_events' ).click( function() {
window.location.href = 'forge?page=forge_event_poller';
});
$( '#menubar_actions' ).click( function() {
window.location.href = 'forge?page=forge_action_invoker';
});
$( '#menubar_rules' ).click( function() {
window.location.href = 'forge_rules';
});
$( '#menubar_event' ).click( function() {
window.location.href = 'invoke_event';
window.location.href = 'forge?page=forge_rule';
});
$( '#menubar_logout' ).click( function() {
$.post( '/logout' ).done( function() {

View file

@ -47,8 +47,8 @@ exports.myOwnEventFunction = function( callback ) {
<div id="pagetitle">Hi {{user.username}}, forge your own modules!</div>
<p>
<select id="select_type">
<option value="0">Action Module</option>
<option value="1">Event Module</option>
<option value="0">Action Invoker</option>
<option value="1">Event Poller</option>
</select>
</p>
<p>
@ -102,7 +102,7 @@ exports.myOwnEventFunction = function( callback ) {
else obj.command = 'store_event';
$.post('/usercommand', obj)
.done(function(data) {
alert(data);
console.log(data);
})
.fail(function(err) {
console.error(err);

View file

@ -1,18 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Unauthorized {{user.username}}</title>
{{{head_requires}}}
</head>
<body>
{{{div_menubar}}}
<div id="mainbody">
<div id="pagetitle">Tsts... {{user.username}}, that was unauthorized!</div>
<p>
Sorry this roles is missing for you.<br />
You only have these privileges: {{user.roles}}
</p>
</div>
<div id="info"></div>
</body>
</html>

View file

@ -1,18 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Welcome {{user.username}}!</title>
{{{head_requires}}}
</head>
<body>
{{{div_menubar}}}
<div id="mainbody">
<div id="pagetitle">Welcome {{user.username}}!</div>
<p>
We're glad you're back!<br />
Your roles are: {{user.roles}}
</p>
</div>
<div id="info"></div>
</body>
</html>

View file

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>HTML</title>
<style>
#editor {
position: relative !important;
border: 1px solid lightgray;
margin: auto;
height: 200px;
width: 80%;
}
</style>
</head>
<body>
<table>
<tr>
<td>test</td>
<td>test</td>
</tr>
<tr>
<td>test</td>
<td>test</td>
</tr>
</table>
<div id="editor">
function foo(items) {
var x = "All this is syntax highlighted";
return x;
}
</div>
<script src="ace-src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
var editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/javascript");
</script>
</body>
</html>

BIN
webpages/public/cross.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

View file

@ -5,12 +5,22 @@ body {
margin: 0px;
}
textarea {
font-family: Tahoma, sans-serif;
font-size: 0.9em;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
input[type=text] {
-webkit-transition: all 0.30s ease-in-out;
-moz-transition: all 0.30s ease-in-out;
-ms-transition: all 0.30s ease-in-out;
-o-transition: all 0.30s ease-in-out;
outline: none;
padding: 3px 0px 3px 3px;
margin: 5px 1px 3px 0px;
border: 1px solid #DDDDDD;
}
input[type=text]:focus {
box-shadow: 0 0 5px rgba(81, 203, 238, 1);
padding: 3px 0px 3px 3px;
margin: 5px 1px 3px 0px;
border: 1px solid rgba(81, 203, 238, 1);
}
#menubar {
@ -27,22 +37,28 @@ textarea {
padding-left: 10px;
padding-right: 10px;
cursor: pointer;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-transition: all 0.30s ease-in-out;
-moz-transition: all 0.30s ease-in-out;
-ms-transition: all 0.30s ease-in-out;
-o-transition: all 0.30s ease-in-out;
}
#menubar div:hover {
background-color: #AAA;
}
#info {
padding-left: 10px;
padding-top: 30px;
padding: 10px;
font-family: Tahoma, sans-serif;
font-size: 0.75em;
color: #F33;
}
#menubar div:hover {
background-color: #AAA;
}
#mainbody {
padding: 5px;
padding-left: 50px;
padding-right: 50px;
}
#pagetitle {
@ -50,23 +66,41 @@ textarea {
font-weight: bold;
}
#div_left {
#editor_table {
width: 100%;
}
#editor_col {
width: 80%;
}
#editor {
float: left;
position: relative !important;
border: 1px solid lightgray;
margin: auto;
height: 500px;
width: 100%;
}
#div_middle li {
font-size: 78%;
list-style-type: none;
#params_col {
padding-top: 15px;
padding-left: 15px;
}
#div_right li {
font-size: 78%;
list-style-type: none;
#tableParams td:first-child {
width: 10px;
}
#editor {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
#tableParams input {
width: 100px;
margin-left: 10px;
}
.error {
padding: 10px;
font-family: Tahoma, sans-serif;
font-size: 1em;
font-weight: bold;
color: #F33;
}