summaryrefslogtreecommitdiff
path: root/www
diff options
context:
space:
mode:
Diffstat (limited to 'www')
-rw-r--r--www/index.html118
-rw-r--r--www/js/apps.js154
-rw-r--r--www/js/auth.js160
-rw-r--r--www/js/clock.js34
-rw-r--r--www/js/importer.js86
-rw-r--r--www/js/jingles.js10
-rw-r--r--www/js/musicgrid.js12
-rw-r--r--www/js/musicpools.js59
-rw-r--r--www/js/router.js176
-rw-r--r--www/js/shows.js76
-rw-r--r--www/js/utils.js8
-rw-r--r--www/styles/jingles.css10
-rw-r--r--www/styles/main-style.css11
-rw-r--r--www/styles/musicgrid.css6
-rw-r--r--www/styles/musicpools.css8
-rw-r--r--www/styles/shows.css10
16 files changed, 566 insertions, 372 deletions
diff --git a/www/index.html b/www/index.html
index eb8e3d2..56130cc 100644
--- a/www/index.html
+++ b/www/index.html
@@ -15,20 +15,6 @@
<link href="/styles/jingles.css" rel="stylesheet">
<link href="/styles/musicpools.css" rel="stylesheet">
<link href="/styles/musicgrid.css" rel="stylesheet">
- <script src="/javascript/jquery/jquery.min.js"></script>
- <script src="/javascript/bootstrap/js/bootstrap.min.js"></script>
- <script src="/js/dropzone.js"></script>
- <script src="/js/rdxport.js"></script>
- <script src="/js/rdxport.rh.js"></script>
- <script src="/js/utils.js"></script>
- <script src="/js/clock.js"></script>
- <script src="/js/importer.js"></script>
- <script src="/js/auth.js"></script>
- <script src="/js/apps.js"></script>
- <script src="/js/shows.js"></script>
- <script src="/js/jingles.js"></script>
- <script src="/js/musicpools.js"></script>
- <script src="/js/musicgrid.js"></script>
</head>
<body>
@@ -40,7 +26,7 @@
<img class="visible-xs-block" src="/img/helsinki-small.png" alt="radio helsinki logo" />
<div class="loginspacer hidden-xs">&nbsp;</div>
<div class="loginspacer hidden-xs hidden-sm">&nbsp;</div>
- <img class="hidden-xs" src="/img/helsinki.png" alt="radio helsinki logo" />
+ <img class="hidden-xs" src="/img/helsinki.png" alt="radio helsinki logo" width="176" height="176" />
<h1 class="form-auth-heading">Radio Helsinki - Import</h1>
<input id="username" type="text" class="form-control" placeholder="Benutzername" required autofocus>
<input id="password" type="password" class="form-control" placeholder="Passwort" required>
@@ -67,16 +53,16 @@
<div class="collapse navbar-collapse" id="navbar-collapse-1">
<div class="navbar-left">
<ul class="nav navbar-nav">
- <li id="nav-btn-shows"><a href="/shows/" onclick="event.preventDefault(); apps_select('shows')">Sendungen</a></li>
- <li id="nav-btn-jingles"><a href="/jingles/" onclick="event.preventDefault(); apps_select('jingles')">Jingles</a></li>
- <li id="nav-btn-musicpools"><a href="/musicpools/" onclick="event.preventDefault(); apps_select('musicpools')">Musikpools</a></li>
- <li id="nav-btn-musicgrid"><a href="/musicgrid/" onclick="event.preventDefault(); apps_select('musicgrid')">Musikgrid</a></li>
+ <li id="nav-btn-shows"><a href="/shows/">Sendungen</a></li>
+ <li id="nav-btn-jingles"><a href="/jingles/">Jingles</a></li>
+ <li id="nav-btn-musicpools"><a href="/musicpools/">Musikpools</a></li>
+ <li id="nav-btn-musicgrid"><a href="/musicgrid/">Musikgrid</a></li>
</ul>
</div>
<div class="navbar-right">
<p class="navbar-text">angemeldet als <strong id="username-field">UNKNOWN</strong></p>
- <button type="button" class="btn btn-danger navbar-btn" onclick="auth_logout()">
+ <button type="button" class="btn btn-danger navbar-btn logout">
<span class="glyphicon glyphicon-log-out" aria-hidden="true"></span>&nbsp;&nbsp;Abmelden
</button>
</div>
@@ -128,18 +114,28 @@
</div>
</div>
- <div id="app-shows" class="container-fluid">
+ <div id="app-shows" class="container-fluid app-tab">
<div class="alertbox"></div>
<div class="row">
<div class="col-md-10">
- <form class="well form-inline">
- <!-- todo: fix html error: h3 is not allowed in label -->
- <label class="control-label" for="show-selector">
- <h3>Sendung auswählen</h3>
- </label>&nbsp;&nbsp;
- <select id="show-selector" class="main-selector">
- </select>
- </form>
+ <div class="row">
+ <div class="col-md-12" id="show-header-spacer">
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-3">
+ <div class="dropdown">
+ <button type="button" class="btn btn-lg btn-primary" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+ Sendung auswählen <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu" id="show-selector">
+ </ul>
+ </div>
+ </div>
+ <div class="col-md-9">
+ <h2 id="show-title"></h2>
+ </div>
+ </div>
</div>
<div class="col-md-2">
<div id="clock">
@@ -149,18 +145,16 @@
</div>
</div>
</div>
- <div class="row">
- <div class="col-md-12">
- <h2 id="show-title"></h2>
- </div>
- </div>
<div class="row" id="show-details">
<div class="col-md-2">&nbsp;</div>
<div class="col-md-2">
<strong>Tag:</strong> <span id="show-dow"></span>
</div>
<div class="col-md-2">
- <strong>Rythmus:</strong> <span id="show-rhythm"></span>
+ <strong>Rythmus:</strong> <span id="show-rhythm-w1" class="label label-default">1</span>
+ <span id="show-rhythm-w2" class="label label-default">2</span>
+ <span id="show-rhythm-w3" class="label label-default">3</span>
+ <span id="show-rhythm-w4" class="label label-default">4</span>
</div>
<div class="col-md-2">
<strong>Startzeit:</strong> <span id="show-starttime"></span>
@@ -191,7 +185,7 @@
</div>
</div>
- <div id="app-jingles" class="container-fluid">
+ <div id="app-jingles" class="container-fluid app-tab">
<div class="alertbox"></div>
<div class="groups">
@@ -200,26 +194,34 @@
</div>
- <div id="app-musicpools" class="container-fluid">
+ <div id="app-musicpools" class="container-fluid app-tab">
<div class="alertbox"></div>
<div class="row">
- <form class="well form-inline">
- <!-- todo: fix html error: h3 is not allowed in label -->
- <label class="control-label" for="musicpool-selector">
- <h3>Musikpool auswählen</h3>
- </label>&nbsp;&nbsp;
- <select id="musicpool-selector" class="main-selector">
- </select>
- </form>
+ <div class="col-md-12" id="musicpool-header-spacer">
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-3">
+ <div class="dropdown">
+ <button type="button" class="btn btn-lg btn-primary" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+ Musikpool auswählen <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu" id="musicpool-selector">
+ </ul>
+ </div>
+ </div>
+ <div class="col-md-9">
+ <h2 id="musicpool-title"></h2>
+ </div>
</div>
<div class="musicpoolContainer">
</div>
</div>
- <div id="app-musicgrid" class="container-fluid">
+ <div id="app-musicgrid" class="container-fluid app-tab">
<div class="alertbox"></div>
- <h1>Musikgrid</h1>
+ <h2 id="musicgrid-title">Musikgrid</h2>
<table class="table table-striped">
<thead>
<tr>
@@ -306,7 +308,7 @@
<div class="row group jingleGroupTemplate">
<div class="col-md-12">
- <h2></h2>
+ <h2 class="jingle-title"></h2>
<table class="table table-striped">
<thead>
<tr>
@@ -329,7 +331,6 @@
<div class="row musicpool musicpoolTemplate">
<div class="col-md-12">
- <h2></h2>
<table class="table table-striped">
<thead>
<tr>
@@ -427,11 +428,20 @@
</div>
- <script type="text/javascript">
- auth_init();
- apps_init();
- clock_init();
- </script>
+ <script src="/javascript/jquery/jquery.min.js"></script>
+ <script src="/javascript/bootstrap/js/bootstrap.min.js"></script>
+ <script src="/js/dropzone.js"></script>
+ <script src="/js/rdxport.js"></script>
+ <script src="/js/rdxport.rh.js"></script>
+ <script src="/js/utils.js"></script>
+ <script src="/js/clock.js"></script>
+ <script src="/js/importer.js"></script>
+ <script src="/js/auth.js"></script>
+ <script src="/js/shows.js"></script>
+ <script src="/js/jingles.js"></script>
+ <script src="/js/musicpools.js"></script>
+ <script src="/js/musicgrid.js"></script>
+ <script src="/js/router.js"></script>
</body>
</html>
diff --git a/www/js/apps.js b/www/js/apps.js
deleted file mode 100644
index dd5b337..0000000
--- a/www/js/apps.js
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * rhwebimport
- *
- * Copyright (C) 2014-2016 Christian Pointner <equinox@helsinki.at>
- * Copyright (C) 2015-2016 Peter Grassberger <petertheone@gmail.com>
- *
- * This file is part of rhwebimport.
- *
- * rhwebimport is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * any later version.
- *
- * rhwebimport is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with rhwebimport. If not, see <http://www.gnu.org/licenses/>.
- */
-
-'use strict';
-
-var Rdxport = Rdxport || {};
-
-var apps_current = null;
-var rdxport = null;
-var importer = null;
-
-function apps_select(app) {
- if (importer && importer.isUploading()) {
- alert('Achtung: Es laufen noch imports.');
- return;
- }
-
- $('.container').removeClass('fullWidth');
-
- shows_cleanup();
- jingles_cleanup();
- musicpools_cleanup();
- musicgrid_cleanup();
-
- switch(app) {
- case "musicgrid":
- $('#app-shows').hide();
- $('#nav-btn-shows').removeClass('active');
- $('#app-jingles').hide();
- $('#nav-btn-jingles').removeClass('active');
- $('#app-musicpools').hide();
- $('#nav-btn-musicpools').removeClass('active');
-
- $('.container').addClass('fullWidth');
- $('#app-musicgrid').show();
- $('#nav-btn-musicgrid').addClass('active');
-
- apps_current = app;
- musicgrid_init();
- break;
- case "musicpools":
- $('#app-shows').hide();
- $('#nav-btn-shows').removeClass('active');
- $('#app-jingles').hide();
- $('#nav-btn-jingles').removeClass('active');
- $('#app-musicgrid').hide();
- $('#nav-btn-musicgrid').removeClass('active');
-
- $('#app-musicpools').show();
- $('#nav-btn-musicpools').addClass('active');
-
- apps_current = app;
- musicpools_init();
- break;
- case "jingles":
- $('#app-shows').hide();
- $('#nav-btn-shows').removeClass('active');
- $('#app-musicpools').hide();
- $('#nav-btn-musicpools').removeClass('active');
- $('#app-musicgrid').hide();
- $('#nav-btn-musicgrid').removeClass('active');
-
- $('#app-jingles').show();
- $('#nav-btn-jingles').addClass('active');
-
- apps_current = app;
- jingles_init();
- break;
- default:
- $('#app-jingles').hide();
- $('#nav-btn-jingles').removeClass('active');
- $('#app-musicpools').hide();
- $('#nav-btn-musicpools').removeClass('active');
- $('#app-musicgrid').hide();
- $('#nav-btn-musicgrid').removeClass('active');
-
- $('#app-shows').show();
- $('#nav-btn-shows').addClass('active');
-
- apps_current = app = 'shows';
- shows_init();
- }
- if (locationHrefValue() !== app) {
- history.pushState(null, null, '/' + app + '/');
- }
-}
-
-function apps_init() {
- importer = new Rdxport.Importer();
-
- apps_current = locationHrefValue();
-
- if(auth_token && auth_username) {
- // todo: do this at a central place
- rdxport = new Rdxport.Rdxport(auth_username, auth_token, '/rd-bin/rdxport.cgi');
- rdxport.setListDropboxesEndpoint('/rh-bin/listdropboxes.cgi');
- rdxport.setMusicgridEndpoint('/rh-bin/musicgrid.cgi');
-
- apps_select(apps_current);
- }
-
- $(window).on('popstate', function(event) {
- if(auth_token && auth_username) {
- // todo: do this at a central place
- rdxport = new Rdxport.Rdxport(auth_username, auth_token, '/rd-bin/rdxport.cgi');
- rdxport.setListDropboxesEndpoint('/rh-bin/listdropboxes.cgi');
- rdxport.setMusicgridEndpoint('/rh-bin/musicgrid.cgi');
-
- apps_select(locationHrefValue());
- }
- });
-
- $(document).ajaxError(function(event, jqXHR, settings, thrownError) {
- //todo: add errors
- });
-
- window.onbeforeunload = function(e) {
- if (importer && importer.isUploading()) {
- return 'Achtung: Es laufen noch imports.';
- }
- };
-}
-
-function apps_cleanup() {
- shows_cleanup();
- jingles_cleanup();
- musicpools_cleanup();
- musicgrid_cleanup();
-
- $(window).off('popstate');
-
- importer = null;
- rdxport = null;
- apps_current = null;
-}
diff --git a/www/js/auth.js b/www/js/auth.js
index d01b70d..691a19e 100644
--- a/www/js/auth.js
+++ b/www/js/auth.js
@@ -24,94 +24,84 @@
var Rdxport = Rdxport || {};
-var auth_username = null;
-var auth_fullname = null;
-var auth_token = null;
-
-function auth_loginSuccess(data) {
- if (data.status == 'OK') {
- auth_username = data.username;
- auth_fullname = data.fullname;
- auth_token = data.token;
-
- sessionStorage.setItem("auth_username", auth_username);
- sessionStorage.setItem("auth_fullname", auth_fullname);
- sessionStorage.setItem("auth_token", auth_token);
-
- // todo: do this at a central place
- rdxport = new Rdxport.Rdxport(auth_username, auth_token, '/rd-bin/rdxport.cgi');
- rdxport.setListDropboxesEndpoint('/rh-bin/listdropboxes.cgi');
- rdxport.setMusicgridEndpoint('/rh-bin/musicgrid.cgi');
-
- apps_select(apps_current);
-
- $('#username-field').html(auth_fullname + ' (' + auth_username + ')');
- $('#loginbox').slideUp();
- $('#mainwindow').fadeIn();
- } else {
- alertbox.error('loginbox', "Fehler beim Login", data.errorstring);
- auth_cleanup();
- }
-}
-
-function auth_loginError(req, status, error) {
- var message = req.status + ': ' + error;
- if(req.status == 401) {
- message = "Benutzer und/oder Passwort sind falsch!";
- }
- alertbox.error('loginbox', "Fehler beim Login", message);
- $("#password").val('');
-}
-
-function auth_logout() {
- if (importer && importer.isUploading()) {
- alert('Achtung: Es laufen noch imports.');
- return;
- }
-
- auth_cleanup();
- apps_cleanup();
+Rdxport.Auth = function() {
+ this.username = sessionStorage.getItem('auth_username');
+ this.fullname = sessionStorage.getItem('auth_fullname');
+ this.token = sessionStorage.getItem('auth_token');
+};
+
+Rdxport.Auth.prototype.isLoggedIn = function() {
+ return this.username && this.fullname && this.token;
+};
+
+Rdxport.Auth.prototype.set = function(username, fullname, token) {
+ this.username = username;
+ this.fullname = fullname;
+ this.token = token;
+
+ sessionStorage.setItem('auth_username', this.username);
+ sessionStorage.setItem('auth_fullname', this.fullname);
+ sessionStorage.setItem('auth_token', this.token);
+};
+
+Rdxport.Auth.prototype.cleanup = function() {
+ sessionStorage.removeItem('auth_username');
+ sessionStorage.removeItem('auth_fullname');
+ sessionStorage.removeItem('auth_token');
+
+ this.username = null;
+ this.fullname = null;
+ this.token = null;
+};
+
+Rdxport.AuthView = function(model) {
+ this.model = model;
+};
+
+Rdxport.AuthView.prototype.renderLoggedIn = function() {
+ $('#loginbox').slideUp();
+ $('#mainwindow').fadeIn();
+ $('#username-field').text(this.model.fullname + ' (' + this.model.username + ')');
+
+ $('button.logout').off().on('click', function() {
+ router.route('logout');
+ });
+};
- $(".alert").alert('close');
- $("#username").val('');
- $("#password").val('');
- $("#mainwindow").fadeOut();
- $('#username-field').html('');
+Rdxport.AuthView.prototype.renderLoginForm = function() {
+ $('.alert').alert('close');
$('#loginbox').slideDown();
-}
+ $('#mainwindow').fadeOut();
+ $('#username-field').empty();
-function auth_init() {
- auth_username = sessionStorage.getItem("auth_username");
- auth_fullname = sessionStorage.getItem("auth_fullname");
- auth_token = sessionStorage.getItem("auth_token");
-
- if(auth_token && auth_username && auth_fullname) {
- $("#loginbox").hide();
- $('#username-field').html(auth_fullname + ' (' + auth_username + ')');
- } else {
- $("#mainwindow").hide();
- }
- $("#loginform").submit(function(event) {
+ var self = this;
+ $('#loginform').on('submit', function(event) {
event.preventDefault();
-
Rdxport.Rdxport.authLogin(
- '/rh-bin/authtoken.json',
- $("#username").val(),
- $("#password").val(),
- auth_loginSuccess
- ).fail(auth_loginError);
+ '/rh-bin/authtoken.json',
+ $("#username").val(),
+ $("#password").val(),
+ function(data) {
+ if (data.status == 'OK') {
+ self.model.set(
+ data.username,
+ data.fullname,
+ data.token
+ );
+
+ router.route();
+ } else {
+ alertbox.error('loginbox', "Fehler beim Login", data.errorstring);
+ self.model.cleanup();
+ }
+ }
+ ).fail(function(req, status, error) {
+ var message = req.status + ': ' + error;
+ if(req.status == 401) {
+ message = "Benutzer und/oder Passwort sind falsch!";
+ }
+ alertbox.error('loginbox', "Fehler beim Login", message);
+ $("#password").val('');
+ });
});
-}
-
-function auth_cleanup() {
- sessionStorage.removeItem("auth_username");
- sessionStorage.removeItem("auth_fullname");
- sessionStorage.removeItem("auth_token");
-
- auth_username = null;
- auth_fullname = null;
- auth_token = null;
-
- $("#username").val('').focus();
- $("#password").val('');
-}
+};
diff --git a/www/js/clock.js b/www/js/clock.js
index 96ec9e4..458d1c7 100644
--- a/www/js/clock.js
+++ b/www/js/clock.js
@@ -32,8 +32,16 @@ function Clock() {
this.clock_rtt = 0;
this.state = 'NEW';
+ this.getRDTimeMS = function() {
+ return (+new Date()) + (this.last_message.tz_offset * 1000) + this.clock_offset;
+ }
+
+ this.now = function() {
+ return new Date(this.getRDTimeMS());
+ }
+
this.redraw = function() {
- var rdtime_ms = (+new Date()) + (this.last_message.tz_offset * 1000) + this.clock_offset;
+ var rdtime_ms = this.getRDTimeMS();
var rdtime = new Date(rdtime_ms);
var date_str = weekday_short[rdtime.getUTCDay()] + ', ';
@@ -44,11 +52,11 @@ function Clock() {
time_str += (rdtime.getUTCSeconds() > 9 ? ':' : ':0') + rdtime.getUTCSeconds();
this.draw_callbacks.fireWith(window, [date_str, time_str, get_rd_week(rdtime_ms)]);
- }
+ };
this.addCallback = function(cb) {
this.draw_callbacks.add(cb);
- }
+ };
this.ntp_update = function(event) {
var t4 = (+new Date());
@@ -59,18 +67,18 @@ function Clock() {
this.clock_offset = ((msg.t2 - msg.t1) + (msg.t3 - msg.t4)) / 2;
this.clock_rtt = (msg.t4 - msg.t1) - (msg.t3 - msg.t2);
// console.log('got new ntp message from rhrdtime (rtt=' + this.clock_rtt + ' ms): new offset = ' + this.clock_offset + ' ms');
- }
+ };
this.ntp_request = function() {
this.sock.send(JSON.stringify({ t1: (+new Date()), t2: 0, t3: 0, t4: 0, tz_offset: 0, week: 0 }));
- }
+ };
this.sock_onopen = function() {
// console.log('clock websocket connection established');
this.state = 'CONNECTED';
this.ntp_request();
this.interval_request = setInterval(this.ntp_request.bind(this), 2000);
- }
+ };
this.sock_onclose = function(event) {
if(this.state == 'STOPPED') {
@@ -84,7 +92,7 @@ function Clock() {
setTimeout(this.connect.bind(this), 1000);
this.state = 'RECONNECTING';
}
- }
+ };
this.connect = function() {
this.sock = new WebSocket('wss://' + window.location.host + '/ntp');
@@ -92,12 +100,12 @@ function Clock() {
this.sock.onopen = this.sock_onopen.bind(this);
this.sock.onclose = this.sock_onclose.bind(this);
this.state = 'CONNECTING';
- }
+ };
this.start = function() {
this.connect();
this.interval_redraw = setInterval(this.redraw.bind(this), 200);
- }
+ };
this.stop = function() {
this.state = 'STOPPED';
@@ -106,13 +114,7 @@ function Clock() {
clearInterval(this.interval_request);
delete this.interval_request;
this.sock.close();
- }
-}
-
-var clock = new Clock();
-
-function clock_init() {
- clock.start();
+ };
}
function clock_add_callback(cb) {
diff --git a/www/js/importer.js b/www/js/importer.js
index 4ea55b3..b34c4b6 100644
--- a/www/js/importer.js
+++ b/www/js/importer.js
@@ -24,10 +24,88 @@
var Rdxport = Rdxport || {};
-Rdxport.Importer = function() {
- this.$el = $('#uploadModal');
+Rdxport.Importer = function(username, token) {
+ this.username = username;
+ this.token = token;
+ this.$el = $('#uploadModal');
this.uploads = [];
+ this.webSocket = null;
+
+ this.initWebSocket();
+};
+
+Rdxport.Importer.CMD_LIST = 'list';
+Rdxport.Importer.CMD_NEW = 'new';
+Rdxport.Importer.CMD_RECONNECT = 'reconnect';
+
+Rdxport.Importer.prototype.initWebSocket = function() {
+ var importer = this;
+
+ var webSocket = new WebSocket('wss://import.helsinki.at/rhimportd');
+
+ webSocket.onclose = function(code, reason) {
+ console.log('close');
+ console.log(code);
+ console.log(reason);
+ };
+
+ webSocket.onerror = function() {
+ console.log('error');
+ };
+
+ webSocket.onopen = function() {
+ console.log('open');
+
+ console.log('send new');
+ var sendOptions = {
+ COMMAND: Rdxport.Importer.CMD_NEW,
+ LOGIN_NAME: importer.username,
+ PASSWORD: importer.token,
+ TIMEOUT: 200,
+ REFERENCE_ID: "999",
+ SHOW_ID: 10000,
+ CLEAR_SHOW_CARTS: true,
+ SOURCE_URI: 'archiv://2016/03/31/05'
+ };
+ console.log(sendOptions);
+ this.send(JSON.stringify(sendOptions));
+ };
+
+ webSocket.onmessage = function(event) {
+ console.log('message');
+ console.log(event.data);
+ };
+
+ /*this.webSocket = new WebSocket('wss://import.helsinki.at/rhimportd');
+
+ this.webSocket.onclose = function(code, reason) {
+ console.log('close');
+ console.log(code);
+ console.log(reason);
+ };
+
+ this.webSocket.onerror = function() {
+ console.log('error');
+ };
+
+ this.webSocket.onopen = function() {
+ console.log('open');
+
+ console.log('send reconnect');
+ var reconnectOptions = {
+ COMMAND: Rdxport.Importer.CMD_RECONNECT,
+ LOGIN_NAME: importer.username,
+ PASSWORD: importer.token
+ };
+ console.log(reconnectOptions);
+ this.send(JSON.stringify(reconnectOptions));
+ };
+
+ this.webSocket.onmessage = function(event) {
+ console.log('message');
+ console.log(event.data);
+ };*/
};
Rdxport.Importer.prototype.resetModal = function() {
@@ -258,8 +336,8 @@ Rdxport.Upload.prototype.addCut = function(file) {
file.cutNumber = cutNumberLeading;
formData.append('COMMAND', 2);
- formData.append('LOGIN_NAME', auth_username);
- formData.append('PASSWORD', auth_token);
+ formData.append('LOGIN_NAME', auth.username);
+ formData.append('PASSWORD', auth.token);
formData.append('CART_NUMBER', self.cart.number);
formData.append('CUT_NUMBER', cutNumber);
formData.append('CHANNELS', 2);
diff --git a/www/js/jingles.js b/www/js/jingles.js
index 02070ae..e819570 100644
--- a/www/js/jingles.js
+++ b/www/js/jingles.js
@@ -44,7 +44,7 @@ Rdxport.JingleGroupListView = function(model) {
this.jingleGroupViews = [];
- $('#app-jingles .groups').html('');
+ $('#app-jingles .groups').empty();
var self = this;
$(this.model).on('update', function() {
@@ -87,7 +87,7 @@ Rdxport.JingleGroupView = function(model) {
var self = this;
$(this.model).on('update', function() {
- $('table > tbody', self.$el).html('');
+ $('table > tbody', self.$el).empty();
self.model.mainCart = self.model.carts[0];
self.mainCartView = new Rdxport.JingleCartView(self.model.mainCart, self, true);
@@ -105,7 +105,7 @@ Rdxport.JingleGroupView.prototype.render = function() {
this.$el = $('#hiddenTemplates .jingleGroupTemplate').clone().removeClass('jingleGroupTemplate');
this.$el.appendTo('#app-jingles .groups');
- $('h2', this.$el).html(this.model.title);
+ $('h2', this.$el).text(this.model.title);
$('table tbody tr', this.$el).remove();
$('.uploadButton', this.$el).on('click', function() {
@@ -114,7 +114,7 @@ Rdxport.JingleGroupView.prototype.render = function() {
};
Rdxport.JingleGroupView.prototype.destroy = function() {
- $('table > tbody', this.$el).html('');
+ $('table > tbody', this.$el).empty();
};
Rdxport.JingleGroupView.prototype.uploadProgress = function(upload, file) {
@@ -149,7 +149,7 @@ Rdxport.JingleGroupView.prototype.uploadError = function(upload, file, msg, xhr,
//var msg = $(xmlDoc);
//var responseCode = msg.find('ResponseCode').text();
//var errorString = msg.find('ErrorString').text();
- var reason = $('<span>').addClass('label').addClass('label-danger').text(responseCode).after($('<b>').html('&nbsp;' + errorString));
+ var reason = $('<span>').addClass('label').addClass('label-danger').text(responseCode).after($('<b>').text('&nbsp;' + errorString));
var dismiss_button = '<button class="btn btn-info btn-xs">' +
'<span class="glyphicon glyphicon-remove"></span>&nbsp;&nbsp;Ok</button>';
diff --git a/www/js/musicgrid.js b/www/js/musicgrid.js
index dbb2210..a0a0c10 100644
--- a/www/js/musicgrid.js
+++ b/www/js/musicgrid.js
@@ -58,7 +58,7 @@ Rdxport.MusicgridView = function(model) {
Rdxport.MusicgridView.prototype.clean = function() {
$('tr td', this.$el)
- .html('')
+ .empty()
.removeClass('clock')
.css('background-color', '')
.css('color', '')
@@ -71,7 +71,7 @@ Rdxport.MusicgridView.prototype.update = function() {
$(this.model.clocks).each(function(index, clock) {
var $td = $('tr[data-dow="' + clock.dow + '"] td[data-hour="' + clock.hour +'"]', this.$el);
$td.addClass('clock');
- $td.html(clock.name);
+ $td.text(clock.name);
$td.attr('title', clock.title);
$td.css('background-color', clock.color);
if($td.isBackgroundDark()) {
@@ -105,9 +105,9 @@ Rdxport.MusicpoolModal.prototype.selectClock = function(dow, hour, clockName) {
var $modalHeader = $('#musicpoolModal .modal-header');
var $modalBody = $('#musicpoolModal .modal-body');
- $('h4', $modalHeader).html('Musikpool auswählen für Tag: ' + dow + ' Stunde: ' + hour + '.');
+ $('h4', $modalHeader).text('Musikpool auswählen für Tag: ' + dow + ' Stunde: ' + hour + '.');
- $('tbody', $modalBody).html('');
+ $('tbody', $modalBody).empty();
var self = this;
$(this.model).off().on('update', function() {
@@ -128,8 +128,8 @@ Rdxport.MusicpoolModal.prototype.selectClock = function(dow, hour, clockName) {
});
var $tr = $('<tr>');
- $tr.append($('<td>').html(musicpool.clock));
- $tr.append($('<td>').html(musicpool.title));
+ $tr.append($('<td>').text(musicpool.clock));
+ $tr.append($('<td>').text(musicpool.title));
$tr.append($('<td>').html($button));
$('tbody', $modalBody).append($tr);
diff --git a/www/js/musicpools.js b/www/js/musicpools.js
index 400c0bd..5b3d491 100644
--- a/www/js/musicpools.js
+++ b/www/js/musicpools.js
@@ -26,9 +26,9 @@ var Rdxport = Rdxport || {};
var musicpoolsView = null;
-function musicpools_init() {
+function musicpools_init(subpage) {
var musicpools = new Rdxport.GroupList();
- musicpoolsView = new Rdxport.MusicpoolsView(musicpools);
+ musicpoolsView = new Rdxport.MusicpoolsView(musicpools, subpage);
}
function musicpools_cleanup() {
@@ -36,11 +36,13 @@ function musicpools_cleanup() {
musicpoolsView = null;
}
-Rdxport.MusicpoolsView = function(model) {
+Rdxport.MusicpoolsView = function(model, subpage) {
this.model = model;
this.musicpoolViews = [];
- this.currentPoolId = sessionStorage.getItem('currentPoolId');
+ this.currentPoolId = null;
+
+ this.setCurrentPoolId(subpage);
var self = this;
$(this.model).on('update', function() {
@@ -54,6 +56,16 @@ Rdxport.MusicpoolsView = function(model) {
};
Rdxport.MusicpoolsView.prototype.setCurrentPoolId = function(currentPoolId) {
+ if (!currentPoolId) {
+ return;
+ }
+ if (this.currentPoolId !== currentPoolId) {
+ if (this.currentPoolId) {
+ history.pushState(null, null, '/musicpools/' + currentPoolId + '/');
+ } else {
+ history.replaceState(null, null, '/musicpools/' + currentPoolId + '/');
+ }
+ }
this.currentPoolId = currentPoolId;
sessionStorage.setItem('currentPoolId', this.currentPoolId);
};
@@ -84,22 +96,31 @@ Rdxport.MusicpoolsView.prototype.updateSelector = function() {
var $musicpoolSelector = $('#musicpool-selector');
$musicpoolSelector.off();
- $('option', $musicpoolSelector).remove();
-
- $(this.model.groups).each(function(index, musicpool) {
- var name = musicpool.title + ' (' + musicpool.clock + ')';
- $musicpoolSelector.append($('<option>').attr('value', musicpool.clock).text(name));
+ $('li', $musicpoolSelector).remove();
+
+ $(this.model.groups).sort(function(a, b) {
+ return a.title.toLowerCase() >= b.title.toLowerCase()
+ }).each(function(index, musicpool) {
+ var name = '<strong>' + musicpool.title + '</strong> (' + musicpool.clock + ')';
+ var link = $('<a>').attr('href', '#').html(name).click(function() {
+ self.setCurrentPoolId(musicpool.clock);
+ self.getCurrentPoolView().model.fetchCarts();
+ });
+ $musicpoolSelector.append($('<li>').append(link));
});
-
- if (this.currentPoolId === null) {
- this.setCurrentPoolId(this.model.groups[0].clock);
+ if($musicpoolSelector.children().length == 0) {
+ $musicpoolSelector.append($('<li>').append($('<a>').text('Keinen Musikpool gefunden!')));
}
- $('option[value="' + this.currentPoolId + '"]', $musicpoolSelector).attr('selected', 'selected');
- $musicpoolSelector.on('change', function() {
- self.setCurrentPoolId($('option:selected', $musicpoolSelector).attr('value'));
- self.getCurrentPoolView().model.fetchCarts();
- });
+ // todo: maybe integrate this into setCurrentShowId?
+ if (!this.currentPoolId) {
+ var currentPoolId = sessionStorage.getItem('currentPoolId');
+ if (currentPoolId) {
+ this.setCurrentPoolId(currentPoolId);
+ } else {
+ this.setCurrentPoolId(this.model.groups[0].id);
+ }
+ }
this.getCurrentPoolView().model.fetchCarts();
};
@@ -136,7 +157,7 @@ Rdxport.MusicpoolView.prototype.render = function() {
this.$el = $('#hiddenTemplates .musicpoolTemplate').clone().removeClass('musicpoolTemplate');
$('#app-musicpools .musicpoolContainer').html(this.$el);
- $('h2', this.$el).html(this.model.title);
+ $('#musicpool-title').text(this.model.title);
$('table tbody tr', this.$el).remove();
this.cartViews = [];
@@ -184,7 +205,7 @@ Rdxport.MusicpoolView.prototype.uploadError = function(upload, file, msg, xhr, a
//var msg = $(xmlDoc);
//var responseCode = msg.find('ResponseCode').text();
//var errorString = msg.find('ErrorString').text();
- var reason = $('<span>').addClass('label').addClass('label-danger').text(responseCode).after($('<b>').html('&nbsp;' + errorString));
+ var reason = $('<span>').addClass('label').addClass('label-danger').text(responseCode).after($('<b>').text('&nbsp;' + errorString));
var dismiss_button = '<button class="btn btn-info btn-xs">' +
'<span class="glyphicon glyphicon-remove"></span>&nbsp;&nbsp;Ok</button>';
diff --git a/www/js/router.js b/www/js/router.js
new file mode 100644
index 0000000..cfeaf23
--- /dev/null
+++ b/www/js/router.js
@@ -0,0 +1,176 @@
+/*
+ * rhwebimport
+ *
+ * Copyright (C) 2014-2016 Christian Pointner <equinox@helsinki.at>
+ * Copyright (C) 2015-2016 Peter Grassberger <petertheone@gmail.com>
+ *
+ * This file is part of rhwebimport.
+ *
+ * rhwebimport is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhwebimport is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with rhwebimport. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+'use strict';
+
+var clock = null;
+var auth = null;
+var router = null;
+var importer = null;
+var rdxport = null;
+
+$(document).ready(function() {
+ clock = new Clock();
+ clock.start();
+ auth = new Rdxport.Auth();
+ router = new Rdxport.Router(auth);
+ router.route();
+});
+
+Rdxport.Router = function(auth) {
+ this.auth = auth;
+ this.authView = new Rdxport.AuthView(this.auth);
+};
+
+Rdxport.Router.prototype.route = function(page, subpage) {
+ if (!this.auth.isLoggedIn()) {
+ this.login();
+ return;
+ }
+ if (importer && importer.isUploading()) {
+ alert('Achtung: Es laufen noch imports.');
+ return;
+ }
+
+ if (!importer) {
+ importer = new Rdxport.Importer(this.auth.username, this.auth.token);
+ window.onbeforeunload = function(event) {
+ if (importer.isUploading()) {
+ return 'Achtung: Es laufen noch imports.';
+ }
+ };
+ }
+ if (!rdxport) {
+ rdxport = new Rdxport.Rdxport(this.auth.username, this.auth.token, '/rd-bin/rdxport.cgi');
+ rdxport.setListDropboxesEndpoint('/rh-bin/listdropboxes.cgi');
+ rdxport.setMusicgridEndpoint('/rh-bin/musicgrid.cgi');
+ }
+
+ /*$(document).ajaxError(function(event, jqXHR, settings, thrownError) {
+ //todo: add errors
+ });*/
+
+ var href = ['', ''];
+ if (!page && !subpage) {
+ href = locationHrefValue();
+ page = href[1];
+ subpage = href[2];
+ }
+
+ shows_cleanup();
+ jingles_cleanup();
+ musicpools_cleanup();
+ musicgrid_cleanup();
+
+ this.authView.renderLoggedIn();
+ $('.navbar-nav li').removeClass('active');
+ $('.app-tab').hide();
+ $('.container').removeClass('fullWidth');
+
+ var self = this;
+ $('.navbar-nav li a').off().on('click', function(event) {
+ event.preventDefault();
+ var href = $(this).attr('href').split('/');
+ self.route(href[1], href[2]);
+ });
+ $(window).off('popstate').on('popstate', function(event) {
+ var href = locationHrefValue();
+ self.route(href[1], href[2]);
+ });
+
+ switch (page) {
+ default :
+ page = 'shows';
+ // fallthrough
+ case 'shows':
+ this.shows(subpage);
+ break;
+ case 'jingles':
+ this.jingles();
+ break;
+ case 'musicpools':
+ this.musicpools(subpage);
+ break;
+ case 'musicgrid':
+ this.musicgrid();
+ break;
+ case 'logout':
+ this.logout();
+ break;
+ }
+
+ href = locationHrefValue();
+ if (href[1] !== page && page !== 'logout' && (!href[2] || href[2] !== subpage)) {
+ var url = '/' + page + '/';
+ if (subpage) {
+ url += subpage + '/';
+ }
+ history.pushState(null, null, url);
+ }
+};
+
+Rdxport.Router.prototype.login = function() {
+ this.authView.renderLoginForm();
+};
+
+Rdxport.Router.prototype.logout = function() {
+ if (importer && importer.isUploading()) {
+ alert('Achtung: Es laufen noch imports.');
+ return;
+ }
+
+ shows_cleanup();
+ jingles_cleanup();
+ musicpools_cleanup();
+ musicgrid_cleanup();
+ this.auth.cleanup();
+
+ importer = null;
+ rdxport = null;
+
+ this.login();
+};
+
+Rdxport.Router.prototype.shows = function(subpage) {
+ $('#app-shows').show();
+ $('#nav-btn-shows').addClass('active');
+ shows_init(subpage);
+};
+
+Rdxport.Router.prototype.jingles = function() {
+ $('#app-jingles').show();
+ $('#nav-btn-jingles').addClass('active');
+ jingles_init();
+};
+
+Rdxport.Router.prototype.musicpools = function(subpage) {
+ $('#app-musicpools').show();
+ $('#nav-btn-musicpools').addClass('active');
+ musicpools_init(subpage);
+};
+
+Rdxport.Router.prototype.musicgrid = function() {
+ $('.container').addClass('fullWidth');
+ $('#app-musicgrid').show();
+ $('#nav-btn-musicgrid').addClass('active');
+ musicgrid_init();
+};
diff --git a/www/js/shows.js b/www/js/shows.js
index 8fcba8b..233a5c3 100644
--- a/www/js/shows.js
+++ b/www/js/shows.js
@@ -26,9 +26,9 @@ var Rdxport = Rdxport || {};
var showListView = null;
-function shows_init() {
+function shows_init(subpage) {
var showList = new Rdxport.GroupList();
- showListView = new Rdxport.ShowListView(showList);
+ showListView = new Rdxport.ShowListView(showList, subpage);
drawClock('Do, 1.1.1970', '00:00:00', 0);
clock_add_callback(drawClock);
@@ -39,11 +39,13 @@ function shows_cleanup() {
importer.cancelAllUploads();
}
-Rdxport.ShowListView = function(model) {
+Rdxport.ShowListView = function(model, subpage) {
this.model = model;
this.showViews = [];
- this.currentShowId = sessionStorage.getItem('currentShowId');
+ this.currentShowId = null;
+
+ this.setCurrentShowId(subpage);
var self = this;
$(this.model).on('update', function() {
@@ -57,6 +59,16 @@ Rdxport.ShowListView = function(model) {
};
Rdxport.ShowListView.prototype.setCurrentShowId = function(currentShowId) {
+ if (!currentShowId) {
+ return;
+ }
+ if (this.currentShowId !== currentShowId) {
+ if (this.currentShowId) {
+ history.pushState(null, null, '/shows/' + currentShowId + '/');
+ } else {
+ history.replaceState(null, null, '/shows/' + currentShowId + '/');
+ }
+ }
this.currentShowId = currentShowId;
sessionStorage.setItem('currentShowId', this.currentShowId);
};
@@ -68,9 +80,6 @@ Rdxport.ShowListView.prototype.getCurrentShowView = function() {
if (this.showViews.length === 0) {
return null;
}
- if (this.currentShowId === null) {
- this.setCurrentShowId(this.model.groups[0].id);
- }
var self = this;
var showViewFound = null;
$(this.showViews).each(function(index, showView) {
@@ -87,22 +96,34 @@ Rdxport.ShowListView.prototype.updateSelector = function() {
var $showSelector = $('#show-selector');
$showSelector.off();
- $('option', $showSelector).remove();
+ $('li', $showSelector).remove();
- $(this.model.groups).each(function(index, show) {
- var name = show.title + ' (' + show.rhythm + ', ' + weekday[show.dayofweek] + ', ' + show.starttime + ', ' + show.length + ' Min.)';
- $showSelector.append($('<option>').attr('value', show.id).text(name));
+ $(this.model.groups).sort(function(a, b) {
+ if(a.title.toLowerCase() == b.title.toLowerCase()) {
+ return b.rhythm - a.rhythm;
+ }
+ return a.title.toLowerCase() >= b.title.toLowerCase()
+ }).each(function(index, show) {
+ var name = show.id + ' | <strong>' + show.title + '</strong> (' + show.rhythm + ', ' + weekday[show.dayofweek] + ', ' + show.starttime + ', ' + show.length + ' Min.)';
+ var link = $('<a>').attr('href', '#').html(name).click(function() {
+ self.setCurrentShowId(show.id);
+ self.getCurrentShowView().model.fetchCarts();
+ });
+ $showSelector.append($('<li>').append(link));
});
-
- if (this.currentShowId === null) {
- this.setCurrentShowId(this.model.groups[0].id);
+ if($showSelector.children().length == 0) {
+ $showSelector.append($('<li>').append($('<a>').text('Keine Sendung gefunden!')));
}
- $('option[value="' + this.currentShowId + '"]', $showSelector).attr('selected', 'selected');
- $showSelector.on('change', function() {
- self.setCurrentShowId($('option:selected', $showSelector).attr('value'));
- self.getCurrentShowView().model.fetchCarts();
- });
+ // todo: maybe integrate this into setCurrentShowId?
+ if (!this.currentShowId) {
+ var currentShowId = sessionStorage.getItem('currentShowId');
+ if (currentShowId) {
+ this.setCurrentShowId(currentShowId);
+ } else {
+ this.setCurrentShowId(this.model.groups[0].id);
+ }
+ }
this.getCurrentShowView().model.fetchCarts();
};
@@ -189,10 +210,23 @@ Rdxport.ShowView = function(model) {
Rdxport.ShowView.prototype.render = function() {
$('#show-title').text(this.model.title);
$('#show-dow').text(weekday[this.model.dayofweek]);
- $('#show-rhythm').text(this.model.rhythm);
$('#show-starttime').text(this.model.starttime);
$('#show-length').text(this.model.length + ' Min.');
+ for(var w = 0; w < 4; w++) {
+ if(this.model.rhythm.charAt(w) == '1') {
+ var s = $('#show-rhythm-w' + (w+1)).attr('class', 'label')
+ switch(w+1) {
+ case 1: s.addClass('label-info'); break;
+ case 2: s.addClass('label-warning'); break;
+ case 3: s.addClass('label-success'); break;
+ case 4: s.addClass('label-danger'); break;
+ }
+ } else {
+ $('#show-rhythm-w' + (w+1)).attr('class', 'label label-disabled')
+ }
+ }
+
var $tableBody = $('#app-shows table tbody');
$('tr', $tableBody).remove();
@@ -249,7 +283,7 @@ Rdxport.ShowView.prototype.uploadError = function(upload, file, msg, xhr, acknow
//var msg = $(xmlDoc);
//var responseCode = msg.find('ResponseCode').text();
//var errorString = msg.find('ErrorString').text();
- var reason = $('<span>').addClass('label').addClass('label-danger').text(responseCode).after($('<b>').html('&nbsp;' + errorString));
+ var reason = $('<span>').addClass('label').addClass('label-danger').text(responseCode).after($('<b>').text('&nbsp;' + errorString));
var dismiss_button = '<button class="btn btn-info btn-xs">' +
'<span class="glyphicon glyphicon-remove"></span>&nbsp;&nbsp;Ok</button>';
diff --git a/www/js/utils.js b/www/js/utils.js
index 26b0822..7e7a656 100644
--- a/www/js/utils.js
+++ b/www/js/utils.js
@@ -108,8 +108,8 @@ function get_rd_week(msEpoch) {
}
function locationHrefValue() {
- var value = window.location.href.match(/import.helsinki.at\/([a-z]+)\/?.*/);
- return value ? value[1] : '';
+ var value = window.location.href.match(/import.helsinki.at\/([a-z]+)\/?([a-z0-9]+)?\/?.*/);
+ return value ? value : '';
}
/*
@@ -141,6 +141,10 @@ jQuery.fn.brightness = function() {
}
};
+jQuery.fn.sort = function() {
+ return this.pushStack(jQuery.makeArray([].sort.apply(this, arguments)));
+};
+
function updateProgressBar($el, upload) {
if(upload.uploadprogress.progress < 99) {
var bytes_str = Number((upload.uploadprogress.bytesSent/1024)/1024).toFixed(1) + " von " +
diff --git a/www/styles/jingles.css b/www/styles/jingles.css
index 1dc50c1..d8d5116 100644
--- a/www/styles/jingles.css
+++ b/www/styles/jingles.css
@@ -21,10 +21,14 @@
*/
#app-jingles .group {
- margin-bottom: 40px;
+ margin-bottom: 40px;
}
#app-jingles table .btn {
- margin-top: 0.3em;
- margin-left: 0.6em;
+ margin-top: 0.3em;
+ margin-left: 0.6em;
+}
+
+h2.jingle-title {
+ font-weight: bold;
}
diff --git a/www/styles/main-style.css b/www/styles/main-style.css
index fc7e357..a3e0c7a 100644
--- a/www/styles/main-style.css
+++ b/www/styles/main-style.css
@@ -32,6 +32,14 @@ body {
width: 100% !important;
}
+#loginbox, #mainwindow {
+ display: none;
+}
+
+#app-shows, #app-jingles, #app-musicpools, #app-musicgrid {
+ display: none;
+}
+
.progress {
margin-bottom: 0;
}
@@ -68,9 +76,8 @@ body {
}
#clock span.clock-time {
- padding: 0.2em;
font-weight: bold;
- font-size: 1.6em;
+ font-size: 1.8em;
}
#uploadModal div.modal-body {
diff --git a/www/styles/musicgrid.css b/www/styles/musicgrid.css
index 44e72c0..53e2c01 100644
--- a/www/styles/musicgrid.css
+++ b/www/styles/musicgrid.css
@@ -21,5 +21,9 @@
*/
#app-musicgrid {
- padding: 0;
+ padding: 0;
+}
+
+#musicgrid-title {
+ font-weight: bold;
}
diff --git a/www/styles/musicpools.css b/www/styles/musicpools.css
index beca7bc..c0652f1 100644
--- a/www/styles/musicpools.css
+++ b/www/styles/musicpools.css
@@ -19,3 +19,11 @@
* You should have received a copy of the GNU Affero General Public License
* along with rhwebimport. If not, see <http://www.gnu.org/licenses/>.
*/
+
+#musicpool-title {
+ font-weight: bold;
+}
+
+#musicpool-header-spacer {
+ margin-top: 1.3em;
+}
diff --git a/www/styles/shows.css b/www/styles/shows.css
index 2daa8b6..045ae9c 100644
--- a/www/styles/shows.css
+++ b/www/styles/shows.css
@@ -22,9 +22,19 @@
#show-title {
text-align: center;
+ font-weight: bold;
}
#show-details {
margin-top: 1.5em;
margin-bottom: 1em;
}
+
+.label-disabled {
+ background-color: #EEE;
+ color: #888;
+}
+
+#show-header-spacer {
+ margin-top: 1.3em;
+}