1.2.9 beta

git-svn-id: file:///root/webif/svn/pkg/webif/trunk@2871 2a923420-c742-0410-a762-8d5b09965624
This commit is contained in:
hummypkg 2016-04-26 19:54:56 +00:00
parent 9ce18af5cf
commit 58da098a11
53 changed files with 3030 additions and 887 deletions

View File

@ -1,10 +1,10 @@
Package: webif
Priority: optional
Section: web
Version: 1.2.8-16
Version: 1.2.9-0
Architecture: mipsel
Maintainer: af123@hpkg.tv
Depends: tcpfix,webif-channelicons(>=1.1.20),lighttpd(>=1.4.39-1),jim(>=0.76-1),jim-oo,jim-sqlite3(>=0.76),jim-cgi(>=0.7-1),jim-binary(>=0.76),service-control(>=2.1),busybox(>=1.20.2-1),lsof(>=4.87),epg(>=1.2.1-1),hmt(>=2.0.9),ssmtp,cron-daemon(>=1.18.3-3),at(>=3.1.18),anacron,trm(>=1.1),openssl-command,nicesplice,id3v2,file,rsvsync(>=1.0.2),webif-charts(>=1.2-1),stripts(>=1.2.5-3),tmenu(>=1.08),ffmpeg,id3v2,multienv(>=1.6),tcpping(>=1.1),e2fsprogs,wireless-tools(>=29-1),dbupdate,recmon(>=2.0.7)
Depends: tcpfix,webif-channelicons(>=1.1.20),lighttpd(>=1.4.39-1),jim(>=0.76-2),jim-oo,jim-sqlite3(>=0.76),jim-cgi(>=0.7-1),jim-binary(>=0.76),service-control(>=2.1),busybox(>=1.20.2-1),lsof(>=4.87),epg(>=1.2.1-1),hmt(>=2.0.9),ssmtp,cron-daemon(>=1.18.3-3),at(>=3.1.18),anacron,trm(>=1.1),openssl-command,nicesplice,id3v2,file,rsvsync(>=1.0.2),webif-charts(>=1.2-1),stripts(>=1.2.5-3),tmenu(>=1.08),ffmpeg,id3v2,multienv(>=1.6),tcpping(>=1.1),e2fsprogs,wireless-tools(>=29-1),dbupdate,recmon(>=2.0.7)
Suggests:
Description: An evolving web interface for the Humax.
Tags: http://hummy.tv/forum/threads/6484/

View File

@ -29,7 +29,7 @@ fi
ana=$PKG_ROOT/etc/anacrontab
egrep -v 'backup/backup.jim|bin/diskattrs' $ana > $tmpf
cat $tmpf - << EOM > $ana
1 8 sched_backup /mod/webif/html/backup/backup.jim
1 8 sched_backup /mod/webif/html/sched/backup/backup.jim
1 5 diskattrs /mod/webif/lib/bin/diskattrs
EOM

View File

@ -25,14 +25,20 @@ if {[[settings] chanchangenc]} {
$record process_sched
if {[$record get sched_type] < 1} {
set tuners [system tuners]
set conflicts [rsv conflicts [$record get start] [$record get duration]]
if {$conflicts >= $tuners} {
set conflicts [rsv checkconflict \
[$record get start] [$record get duration] \
[system tuners]]
if {[llength $conflicts]} {
puts {
<div class=warningbox style="width: 100%; margin: 0.2em"><div>
This event conflicts with items in your scheduled recording list.
</div></div>
This event conflicts with the following items in your scheduled recording list.
<ul>
}
foreach x $conflicts {
puts "<li>$x</li>\n"
}
puts "</div></div>"
}
}

View File

@ -27,7 +27,7 @@ if {[$event percent] > 0} {
}
set r [rsv construct $event $type]
if {[catch {$r insert} msg]} {
if {[catch {$r insert pending 1} msg]} {
puts "Error encountered while scheduling: <i>$msg</i>"
} else {
puts "Successfully scheduled <i>[$event get name]</i>"

View File

@ -1,38 +0,0 @@
$(function() {
$('#epgswitch').button().click(function() {
window.location = '/epg/list.jim';
});
$('button.nav').click(function() {
window.location = '/cgi-bin/xepg.jim?stt=' + $(this).attr('tt') +
'&pos=' + $('#xegrid').scrollTop();
});
$('#xepg_dp').datepicker({
buttonImage: '/img/cal.gif',
buttonImageOnly: true,
showOn: 'button',
dateFormat: '@',
minDate: 0,
maxDate: 8,
onSelect: function(val, sel) {
var stt = $(this).attr('stt');
// Extract date part
dval = Math.round(val / 86400000.0);
// Extract current time part
var tm = ~~(stt % 86400);
var ret = dval * 86400 + tm;
window.location = '/cgi-bin/xepg.jim?stt=' + ret;
}
});
$('img.ui-datepicker-trigger').hover(
function() { $(this).css('cursor', 'pointer'); },
function() { $(this).css('cursor', 'auto'); }
);
});

View File

@ -1,69 +0,0 @@
function refresh_files()
{
$('#backup_files').load('files.jim', function() {
$('input.restore').change(function() {
$('#restore_button').removeAttr('disabled')
.button('option', 'disabled', false);
$('#delete_button').removeAttr('disabled')
.button('option', 'disabled', false);
$('#view_button').removeAttr('disabled')
.button('option', 'disabled', false);
});
});
}
$(document).ready(function() {
$('button').button();
refresh_files();
$('#backup_button').click(function() {
$('#backup_working').slideDown();
$('#results').load('backup.jim?' +
$('#backup_name').serialize(), function() {
$('#results').slideDown(function() {
$('#backup_working').slideUp();
refresh_files();
});
});
});
$('#delete_button').click(function() {
var backup = $('input.restore:checked').val();
if (confirm('Confirm deletion of ' + backup))
{
$('#results').load('delete.jim?' +
$('input.restore').serialize(), function() {
$('#results').slideDown(function() {
refresh_files();
});
});
}
});
$('#view_button').click(function() {
var backup = $('input.restore:checked').val();
$('#results').load('view.jim?' +
$('input.restore').serialize(), function() {
$('#results').slideDown(function() {
refresh_files();
});
});
});
$('#restore_button').click(function() {
var backup = $('input.restore:checked').val();
if (confirm('!!!!!!!!!!!!!!!!!!!!!!!!! PLEASE CONFIRM !!!!!!!!!!!!!!!!!!!!!!!!!\n\nAre you sure you wish to erase all scheduled recordings and favourite channels and then restore them from\n' + backup + '?'))
{
$('#restore_working').slideDown();
$('#results').load('restore.jim?' +
$('input.restore').serialize(), function() {
$('#results').slideDown(function() {
$('#restore_working').slideUp();
refresh_files();
$('#restore_warning').slideDown();
$('#restart_block').slideDown('slow');
});
});
}
});
});

View File

@ -109,7 +109,7 @@ div.warningbox
top: -2px;
left: -2px;
font-weight: bold;
z-index: 30;
#z-index: 30;
box-shadow: 2px 2px 11px #666;
-moz-box-shadow: 2px 2px 11px #666;
-webkit-box-shadow: 2px 2px 11px #666;
@ -174,22 +174,22 @@ table.keyval td
pre, .pre, .prelike
{
font-family: Consolas, 'Courier New', Courier, monospace;
color: black;
background: #f9d9b0 url('/img/prebg.png') repeat-x top;
font-family: Consolas, 'Courier New', Courier, monospace;
color: black;
background: #f9d9b0 url('/img/prebg.png') repeat-x top;
line-height: 1.24;
padding: 3px 8px;
margin: 0 5em 1em 5em;
border-color: #f9d9b0;
border-bottom: 1px solid #f9bc6d;
border-top-left-radius: 4px;
-webkit-border-top-left-radius: 4px;
-moz-border-radius-topleft: 4px;
-khtml-border-top-left-radius: 4px;
border-top-right-radius: 4px;
-webkit-border-top-right-radius: 4px;
-moz-border-radius-topright: 4px;
-khtml-border-top-right-radius: 4px;
padding: 3px 8px;
margin: 0 5em 1em 5em;
border-color: #f9d9b0;
border-bottom: 1px solid #f9bc6d;
border-top-left-radius: 4px;
-webkit-border-top-left-radius: 4px;
-moz-border-radius-topleft: 4px;
-khtml-border-top-left-radius: 4px;
border-top-right-radius: 4px;
-webkit-border-top-right-radius: 4px;
-moz-border-radius-topright: 4px;
-khtml-border-top-right-radius: 4px;
}
pre, .pre
@ -291,7 +291,7 @@ tr.blueshade > td, .blueshade
.pinkshade
{
background: #ffccff;
background: #ffccff !important;
color: black;
}
@ -312,12 +312,18 @@ tr.blueshade > td, .blueshade
color: black;
}
.orangeshade
tr.orangeshade > td, .orangeshade
{
background: #ffeeaa;
background: #ff8040 !important;
color: black;
}
tr.purpleshade > td, .purpleshade
{
background: #7e587e !important;
color: white;
}
.redshade
{
background: #ff4000;
@ -594,3 +600,56 @@ img.rollimg
clear: right;
}
/* Minimal tabs */
#minimaltabbar
{
background: transparent;
border: none;
padding: 0 0 0 0;
position: relative;
top: -40px;
}
#minimaltabbar .ui-widget-header
{
background: transparent;
border: none;
border-bottom: 1px solid #c0c0c0;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
}
#minimaltabbar .ui-tabs-nav
{
margin-bottom: 15px;
}
#minimaltabbar .ui-tabs-nav .ui-state-default
{
background: transparent;
border: none;
}
#minimaltabbar .ui-tabs-nav .ui-state-active
{
background: transparent url(/img/uiTabsArrow.png) no-repeat bottom center;
border: none;
}
#minimaltabbar .ui-tabs-nav .ui-state-default a
{
color: #c0c0c0;
}
#minimaltabbar .ui-tabs-nav .ui-state-active a
{
color: #000000;
}
#minimaltabbar .ui-tabs-panel
{
padding: 0 0 0 0;
}

View File

@ -53,7 +53,7 @@ puts {
<small><button id=switch>Switch to grid-style Now/Next</button></small>
<script type=text/javascript>
$('#switch').button().click(function() {
window.location = '/cgi-bin/xepg.jim'; });
window.location = '/xepg/'; });
</script>
<table class=borders>
<tr>

View File

@ -21,7 +21,7 @@ require epg_popup
set service [cgi_get service 4170]
set records [epg dbfetch dump \
-service $service \
-sort "strftime('%%H%%J', start, 'unixepoch'), strftime('%%M', start, 'unixepoch')" \
-sort "strftime('%%H%%J', start, 'unixepoch', 'localtime'), strftime('%%M', start, 'unixepoch')" \
-debug 0
]

View File

@ -12,7 +12,7 @@ if {[dict exists $env HTTP_COOKIE]} {
set settings [settings]
if {[$settings epg_style] eq "grid"} {
set epglink "/cgi-bin/xepg.jim"
set epglink "/xepg/"
} else {
set epglink "/epg/list.jim"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

View File

@ -3,9 +3,9 @@ $(function() {
{
$('#epginfo_extra').load('/cgi-bin/epg/schedule.jim?' +
'service=' +
encodeURIComponent($('#dialogue').attr('xs')) +
encodeURIComponent($('#epgpopup_dialogue').attr('xs')) +
'&event=' +
encodeURIComponent($('#dialogue').attr('xe')) +
encodeURIComponent($('#epgpopup_dialogue').attr('xe')) +
'&type=' + type, function() {
$('#restart_block').slideDown('slow');
});
@ -24,14 +24,14 @@ $(function() {
{"Record Series": function() { doschedule(2) }},
$buttons2);
var $dialog = $('#dialogue').dialog({
var $dialog = $('#epgpopup_dialogue').dialog({
title: "Programme Details",
modal: false, autoOpen: false,
height: 500, width: 700,
show: 'scale', hide: 'fade',
draggable: true, resizable: true,
buttons: $buttons1,
close: function(e,u) { $('#dialogue').empty().html(
close: function(e,u) { $('#epgpopup_dialogue').empty().html(
'<img src="/img/loading.gif" alt="loading">'); }
});
@ -51,15 +51,15 @@ $(function() {
var url = '/cgi-bin/epg/info.jim?service=' +
o.attr('xs') + '&event=' +
o.attr('xe') + '&bare=1';
$('#dialogue')
$('#epgpopup_dialogue')
.html('<img src=/img/loading.gif> Loading details...' +
' Please wait...')
.load(url, function() {
$('#dialogue a.event').click(function(e) {
$('#epgpopup_dialogue a.event').click(function(e) {
epgpopup(e, $(this));
});
});
$('#dialogue')
$('#epgpopup_dialogue')
.attr('xs', o.attr('xs'))
.attr('xe', o.attr('xe'));
$dialog.dialog('open');

32
webif/html/js/icons.js Normal file
View File

@ -0,0 +1,32 @@
function schedule_icons(reckind, rsvtype, repeat)
{
var icons = [];
switch (rsvtype)
{
case 1: icons.push("175_1_00_Reservation_Watch"); break;
case 2: icons.push("175_1_00_Reservation_Watch"); break;
case 3: icons.push("175_1_11_Reservation_Record"); break;
case 4: icons.push("175_1_11_Reservation_Record"); break;
case 5: icons.push("745_1_10_Video_2Live"); break;
case 6: icons.push("745_1_11_Video_1REC"); break;
case 7: icons.push("345_6_08_ST_Ad_Hoc"); break;
}
switch (reckind)
{
case 2: icons.push("178_1_26_Icon_Split"); break;
case 4: icons.push("175_1_11_Series_Record"); break;
default:
switch (repeat)
{
case 1: icons.push("521_1_00_RP_Daily_C"); break;
case 2: icons.push("521_1_00_RP_Weekly_C"); break;
case 3: icons.push("521_1_00_RP_Weekdays_C"); break;
case 4: icons.push("521_1_00_RP_Weekend_C"); break;
}
}
return icons;
}

View File

@ -5,10 +5,9 @@ puts {
<div class=footer>
}
if {![catch {
set rendertime [expr [expr [clock milliseconds] - $renderstart] / 1000.0]
}]} {
puts "<font class=footnote>Rendered in: $rendertime seconds</font>"
catch {
set rendertime [expr ([clock milliseconds] - $renderstart) / 1000.0]
puts "<span class=footnote>Rendered in: $rendertime seconds</span>"
}
puts {

View File

@ -0,0 +1,85 @@
/* jQuery Growl
* Copyright 2015 Kevin Sylvestre
* 1.3.1
*/
#growls {
z-index: 50000;
position: fixed; }
#growls.default {
top: 10px;
right: 10px; }
#growls.tl {
top: 10px;
left: 10px; }
#growls.tr {
top: 10px;
right: 10px; }
#growls.bl {
bottom: 10px;
left: 10px; }
#growls.br {
bottom: 10px;
right: 10px; }
#growls.tc {
top: 10px;
right: 10px;
left: 10px; }
#growls.bc {
bottom: 10px;
right: 10px;
left: 10px; }
#growls.tc .growl, #growls.bc .growl {
margin-left: auto;
margin-right: auto; }
.growl {
opacity: 0.8;
filter: alpha(opacity=80);
position: relative;
border-radius: 4px;
-webkit-transition: all 0.4s ease-in-out;
-moz-transition: all 0.4s ease-in-out;
transition: all 0.4s ease-in-out; }
.growl.growl-incoming {
opacity: 0;
filter: alpha(opacity=0); }
.growl.growl-outgoing {
opacity: 0;
filter: alpha(opacity=0); }
.growl.growl-small {
width: 200px;
padding: 5px;
margin: 5px; }
.growl.growl-medium {
width: 250px;
padding: 10px;
margin: 10px; }
.growl.growl-large {
width: 300px;
padding: 15px;
margin: 15px; }
.growl.growl-default {
color: #FFF;
background: #7f8c8d; }
.growl.growl-error {
color: #FFF;
background: #C0392B; }
.growl.growl-notice {
color: #FFF;
background: #2ECC71; }
.growl.growl-warning {
color: #FFF;
background: #F39C12; }
.growl .growl-close {
cursor: pointer;
float: right;
font-size: 14px;
line-height: 18px;
font-weight: normal;
font-family: helvetica, verdana, sans-serif; }
.growl .growl-title {
font-size: 18px;
line-height: 24px; }
.growl .growl-message {
font-size: 14px;
line-height: 18px; }

View File

@ -0,0 +1,244 @@
// Generated by CoffeeScript 1.9.3
/*
jQuery Growl
Copyright 2015 Kevin Sylvestre
1.3.1
*/
(function() {
"use strict";
var $, Animation, Growl,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
$ = jQuery;
Animation = (function() {
function Animation() {}
Animation.transitions = {
"webkitTransition": "webkitTransitionEnd",
"mozTransition": "mozTransitionEnd",
"oTransition": "oTransitionEnd",
"transition": "transitionend"
};
Animation.transition = function($el) {
var el, ref, result, type;
el = $el[0];
ref = this.transitions;
for (type in ref) {
result = ref[type];
if (el.style[type] != null) {
return result;
}
}
};
return Animation;
})();
Growl = (function() {
Growl.settings = {
namespace: 'growl',
duration: 3200,
close: "&#215;",
location: "default",
style: "default",
size: "medium"
};
Growl.growl = function(settings) {
if (settings == null) {
settings = {};
}
this.initialize();
return new Growl(settings);
};
Growl.initialize = function() {
return $("body:not(:has(#growls))").append('<div id="growls" />');
};
function Growl(settings) {
if (settings == null) {
settings = {};
}
this.container = bind(this.container, this);
this.content = bind(this.content, this);
this.html = bind(this.html, this);
this.$growl = bind(this.$growl, this);
this.$growls = bind(this.$growls, this);
this.animate = bind(this.animate, this);
this.remove = bind(this.remove, this);
this.dismiss = bind(this.dismiss, this);
this.present = bind(this.present, this);
this.cycle = bind(this.cycle, this);
this.close = bind(this.close, this);
this.click = bind(this.click, this);
this.unbind = bind(this.unbind, this);
this.bind = bind(this.bind, this);
this.render = bind(this.render, this);
this.settings = $.extend({}, Growl.settings, settings);
this.$growls().attr('class', this.settings.location);
this.render();
}
Growl.prototype.render = function() {
var $growl;
$growl = this.$growl();
this.$growls().append($growl);
if (this.settings.fixed) {
this.present();
} else {
this.cycle();
}
};
Growl.prototype.bind = function($growl) {
if ($growl == null) {
$growl = this.$growl();
}
$growl.on("click", this.click);
return $growl.on("contextmenu", this.close).find("." + this.settings.namespace + "-close").on("click", this.close);
};
Growl.prototype.unbind = function($growl) {
if ($growl == null) {
$growl = this.$growl();
}
$growl.off("click", this.click);
return $growl.off("contextmenu", this.close).find("." + this.settings.namespace + "-close").off("click", this.close);
};
Growl.prototype.click = function(event) {
if (this.settings.url != null) {
event.preventDefault();
event.stopPropagation();
return window.open(this.settings.url);
}
};
Growl.prototype.close = function(event) {
var $growl;
event.preventDefault();
event.stopPropagation();
$growl = this.$growl();
return $growl.stop().queue(this.dismiss).queue(this.remove);
};
Growl.prototype.cycle = function() {
var $growl;
$growl = this.$growl();
return $growl.queue(this.present).delay(this.settings.duration).queue(this.dismiss).queue(this.remove);
};
Growl.prototype.present = function(callback) {
var $growl;
$growl = this.$growl();
this.bind($growl);
return this.animate($growl, this.settings.namespace + "-incoming", 'out', callback);
};
Growl.prototype.dismiss = function(callback) {
var $growl;
$growl = this.$growl();
this.unbind($growl);
return this.animate($growl, this.settings.namespace + "-outgoing", 'in', callback);
};
Growl.prototype.remove = function(callback) {
this.$growl().remove();
return callback();
};
Growl.prototype.animate = function($element, name, direction, callback) {
var transition;
if (direction == null) {
direction = 'in';
}
transition = Animation.transition($element);
$element[direction === 'in' ? 'removeClass' : 'addClass'](name);
$element.offset().position;
$element[direction === 'in' ? 'addClass' : 'removeClass'](name);
if (callback == null) {
return;
}
if (transition != null) {
$element.one(transition, callback);
} else {
callback();
}
};
Growl.prototype.$growls = function() {
return this.$_growls != null ? this.$_growls : this.$_growls = $('#growls');
};
Growl.prototype.$growl = function() {
return this.$_growl != null ? this.$_growl : this.$_growl = $(this.html());
};
Growl.prototype.html = function() {
return this.container(this.content());
};
Growl.prototype.content = function() {
return "<div class='" + this.settings.namespace + "-close'>" + this.settings.close + "</div>\n<div class='" + this.settings.namespace + "-title'>" + this.settings.title + "</div>\n<div class='" + this.settings.namespace + "-message'>" + this.settings.message + "</div>";
};
Growl.prototype.container = function(content) {
return "<div class='" + this.settings.namespace + " " + this.settings.namespace + "-" + this.settings.style + " " + this.settings.namespace + "-" + this.settings.size + "'>\n " + content + "\n</div>";
};
return Growl;
})();
this.Growl = Growl;
$.growl = function(options) {
if (options == null) {
options = {};
}
return Growl.growl(options);
};
$.growl.error = function(options) {
var settings;
if (options == null) {
options = {};
}
settings = {
title: "Error!",
style: "error"
};
return $.growl($.extend(settings, options));
};
$.growl.notice = function(options) {
var settings;
if (options == null) {
options = {};
}
settings = {
title: "Notice!",
style: "notice"
};
return $.growl($.extend(settings, options));
};
$.growl.warning = function(options) {
var settings;
if (options == null) {
options = {};
}
settings = {
title: "Warning!",
style: "warning"
};
return $.growl($.extend(settings, options));
};
}).call(this);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

21
webif/html/lib/noheader.jim Executable file
View File

@ -0,0 +1,21 @@
#!/mod/bin/jimsh
source /mod/webif/lib/setup
require system.class
puts {
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1" />
}
incr ::mws::headerdone
::mws::emit_jscss
puts {
</head>
<body>
}

View File

@ -14,7 +14,7 @@ set file [file tail [cgi_get file \
[clock format $now -format "auto-%Y-%b-%d-%H:%M"]]]
if {[string match {auto-*} $file]} {
# Delete any automatic backups over 7 days old.
# Delete any automatic backups over 15 days old.
set mt $(15 * 86400)
foreach af [glob -nocomplain "$dir/auto-*"] {
set aft [file mtime $af]

View File

@ -1,13 +1,12 @@
#!/mod/bin/jimsh
source /mod/webif/lib/setup
require altrow rsv.class
require rsv.class
jscss backup.js
header
noheader
puts {
<h2>Backup/Restore</h2>
<h2>Recording Schedule Backup &amp; Restore</h2>
<div id=restore_warning class="hidden warningbox">
<div>
@ -60,7 +59,7 @@ puts {
</div>
<div id=results class=pre
<div id=backup_results class=pre
style="display: none; clear: both; float: left; padding: 1em;
margin-top: 1em; border: 1px solid grey; width: 70%">
</div>

View File

@ -0,0 +1,72 @@
function refresh_backup_files()
{
$('#backup_files').load('backup/files.jim', function() {
$('input.restore').change(function() {
$('#restore_button').removeAttr('disabled')
.button('option', 'disabled', false);
$('#delete_button').removeAttr('disabled')
.button('option', 'disabled', false);
$('#view_button').removeAttr('disabled')
.button('option', 'disabled', false);
});
});
}
function backup_loaded()
{
$('button').button();
refresh_backup_files();
$('#backup_button').click(function() {
$('#backup_working').slideDown();
$('#backup_results').load('backup/backup.jim?' +
$('#backup_name').serialize(), function() {
$('#backup_results').slideDown(function() {
$('#backup_working').slideUp();
refresh_backup_files();
});
});
});
$('#delete_button').click(function() {
var backup = $('input.restore:checked').val();
if (confirm('Confirm deletion of ' + backup))
{
$('#backup_results').load('backup/delete.jim?' +
$('input.restore').serialize(), function() {
$('#backup_results').slideDown(function() {
refresh_backup_files();
});
});
}
});
$('#view_button').click(function() {
var backup = $('input.restore:checked').val();
$('#backup_results').load('backup/view.jim?' +
$('input.restore').serialize(), function() {
$('#backup_results').slideDown(function() {
refresh_backup_files();
});
});
});
$('#restore_button').click(function() {
var backup = $('input.restore:checked').val();
if (confirm('!!!!!!!!!!!!!!!!!!!!!!!!! PLEASE CONFIRM !!!!!!!!!!!!!!!!!!!!!!!!!\n\nAre you sure you wish to erase all scheduled recordings and favourite channels and then restore them from\n' + backup + '?'))
{
$('#restore_working').slideDown();
$('#backup_results').load('backup/restore.jim?' +
$('input.restore').serialize(), function() {
$('#backup_results').slideDown(function() {
$('#restore_working').slideUp();
refresh_backup_files();
$('#restore_warning').slideDown();
$('#restart_block').slideDown('slow');
});
});
}
});
}

View File

@ -52,7 +52,8 @@ foreach line $data {
set rsv [rsv new $vars]
puts " [$rsv name] ([$rsv channel_name])"
puts " [$rsv name] ([$rsv channel_name] [\
clock format [$rsv start]] - [clock format [$rsv end]])"
}
close $fd

View File

@ -2,281 +2,31 @@
package require cgi
source /mod/webif/lib/setup
require altrow rsv.class progressbar epg.class system.class
jqplugin tablesorter2 contextMenu form blockui confirmAction \
timepicker datepair
jscss script.js
timepicker datepair growl qtip
jscss {
/js/icons.js
list/script.js
visual/script.js
backup/script.js
script.js
} visual/style.css
header
set svcmap {}
lmap i \
[$::channeldb query {select hSvc, usSvcid from TBL_SVC}] \
{
set svcmap([lindex $i 1]) [lindex $i 3]
}
set svckeys [array names svcmap]
require epg_popup
# Prevent render time being shown in footer
unset renderstart
puts {
<div id=output class=hidden></div>
<div id=sdialogue></div>
<div id=minimaltabbar>
<ul>
<li><a href=list/>Recording List</a></li>
<li><a href=visual/>Visual Schedule</a></li>
<li><a href=backup/>Backup/Restore</a></li>
</ul>
</div>
}
proc eventheader {{table TBL_RESERVATION} note} {
if {$table eq "pending"} {
set sortlist {[[6,0]]}
} else {
set sortlist {[[5,0]]}
}
puts {
<fieldset id=sched_fs_$table
style="display: inline; padding: 1em">
<legend>
}
puts "<h3>$note</h3>"
puts "
</legend>
<table class=\"schedule tablesorter\" data-sortlist=\"$sortlist\">
<thead>
<tr>
<th data-sorter=false>&nbsp;</th>
"
if {$table eq "pending"} { puts "<th>Action</th>" }
puts {
<th>&nbsp;</th>
<th data-sorter=false>&nbsp;</th>
<th>Channel</th>
<th data-sorter=programme>Programme</th>
<th data-sorter=xdate>Start Time</th>
<th>Duration</th>
<th data-sorter=false>Mode</th>
<th data-sorter=false>&nbsp;</th>
</tr>
</thead>
<tbody>
}
}
proc eventrow {event {table TBL_RESERVATION} {pending 0}} {
global svcmap svckeys num_ended
set name [$event name]
set s [$event start]
set d [$event get nduration]
set e $($s + $d)
set n [clock seconds]
if {$n > $e && [$event get ersvtype] <= 3} {
set ended 1
incr num_ended
} else {
set ended 0
}
set attrs "table=$table sid=[$event get ulslot] \
reckind=[$event get ucRecKind] rsvtype=[$event get ersvtype] \
ar=[expr ! [$event padded]] ended=$ended"
if {$pending} {
append attrs " class=\"blueshade strike\""
}
altrow $attrs
#puts "<tr $attrs>"
# Checkbox
puts "<td><input type=checkbox class=schedselect></td>";
if {$table eq "pending"} {
puts "<td align=center>[$event pendingicon]</td>"
}
puts "<td>[$event get ulslot]</td>"
if {[$event get usLcn] ne ""} {
puts "
<td>
[epg channelicon [$event channel_name] 50]
</td>
<td nowrap>[$event get usLcn]<br>
[$event channel_name]</td>
"
} else {
puts "<td>&nbsp;<br><br></td><td>&nbsp;</td>"
}
puts "<td nowrap>
<a slot=[$event get ulslot] table=$table
href=# class=schedule>$name</a>"
if {
([$event get ucRecKind] == 4 && $name ne [$event folder]) ||
([$event get ucRecKind] == 1 && [$event folder] ne "")
} {
puts "<br><span class=also>&nbsp;(Folder: [$event folder])</span>"
}
puts "</td>"
if {$ended} {
set ds "[clock format $s -format {%a %d %b %Y %H:%M}]"
puts "<td nowrap class=va>
--- -- --- ---- --:--<br>
<span class=footnote>(Last: $ds)</span>"
puts "</td><td>--:--:--</td>"
} else {
set elist [$event aul]
set ds "[clock format $s -format {%a %d %b %Y %H:%M}]"
puts "<td nowrap class=\"va"
if {$n > $e} { puts " blood" }
puts "\">"
if {[llength $elist] > 0} {
lassign [lindex $elist 0] service_id start end event_id
if {$start == $s && $service_id in $svckeys} {
puts "<a class=event href=#
xs=$svcmap($service_id) xe=$event_id>
<span class=ds>$ds</span></a>"
} else {
puts "<span class=ds>$ds</span>"
}
} else {
puts "<span class=ds>$ds</span>"
}
if {$d > 0 && $n > $s && $n < $e} {
puts "<br>"
set perc [expr [expr $n - $s] * 100 / $d]
puts "<img class=va
src=/images/745_1_11_Video_1REC.png>"
puts [progressbar $perc]
}
if {[llength $elist] > 1} {
puts "<div class=also style=\"margin-top: 0.5em\">"
foreach e $elist {
lassign $e service_id start end event_id
if {$start == $s} continue
if {$service_id in $svckeys} {
puts "<a class=event href=#
xs=$svcmap($service_id) xe=$event_id>
[clock format $start \
-format {%a %d %b %Y %H:%M}]</a>"
if {$service_id != [$event get hsvc]} {
set ch [system strip [\
get_channel_attr_byhsvc \
$service_id szSvcName]]
puts "<img width=15 src=\"[\
epg channeliconpath $ch]\""
puts "title=\"$ch\" alt=\"$ch\">"
}
puts "<br>"
} else {
puts "[clock format $start \
-format {%a %d %b %Y %H:%M}]
($service_id)<br>"
}
}
puts "</div>";
}
puts "</td><td align=center>"
set xndur [$event get nduration]
if {$xndur > 0} {
puts "[clock format $xndur -format %H:%M]"
} else {
puts "--:--:--"
}
if {[llength $elist] > 1} {
puts "<div class=also style=\"margin-top: 0.5em\">"
foreach e $elist {
lassign $e service_id start end event_id
if {$start == $s} continue
puts "[clock format $($end - $start) \
-format {%H:%M}]<br>"
}
puts "</div>";
}
puts "</td>"
}
puts "<td nowrap>[join [$event iconset] ""]</td>"
puts "<td><a href=# class=smenu sid=[$event get ulslot]>
<img border=0 src=/img/more_btn.gif></a></td>"
puts "</tr>"
}
proc eventfooter {rawlink} {
global num_ended
puts "</tbody></table>"
puts "
<br>
<button class=selall>Select All</button>
<button class=selnone>Select None</button>
"
if {$num_ended} {
puts "
<button class=selended>Select Finished</button>
"
}
puts "
<button class=\"delselected red\">Delete Selected
<span class=delselcnt></span>
</button>
<button class=\"manual_rsv blue\">Manual Event</button>
<button class=rawview
path=\"$rawlink\"
>Raw Database</button>
</fieldset>
"
}
proc ekey {event} {
set ret ""
foreach key {ersvtype nsttime nduration hsvc usevtid} {
append ret ".[$event get $key]"
}
return $ret
}
set pmap {}
set events [rsv list pending]
set num_ended 0
if {[llength $events] > 0} {
eventheader pending "Pending Actions"
foreach event $events {
eventrow $event pending
lappend pmap [ekey $event]
}
eventfooter "db=rsvp.db&tab=pending"
}
set events [rsv list]
set num_ended 0
eventheader live "Scheduled Events"
foreach event $events {
if {[ekey $event] in $pmap} {
eventrow $event TBL_RESERVATION 1
} else {
eventrow $event
}
}
eventfooter "db=rsv.db&tab=TBL_RESERVATION"
puts {
<br><br>
<button class=backup>
Backup/Restore Scheduled Recordings/Events
</button>
}
source assets.jim
footer

View File

@ -1,5 +1,7 @@
#!/mod/bin/jimsh
puts "<div id=sdialogue></div>"
lassign [system padding] pre post
proc padval {num str def} {
@ -14,12 +16,13 @@ puts {
<ul id=optmenu class=contextMenu style="width: 200px">
<li class=delete><a href=#delete>Delete</a></li>
<li class=separator><a href=#ar>Enable AR</a></li>
<li class=separator><a href=#refresh>Refresh Events</a></li>
<li class=separator><a href=#folder>Change Folder</a></li>
<li class=separator><a href=#mkfolder>Create Folder</a></li>
</ul>
<div id=padding style="display: none">
<form id=paddingf method=post action=pad.jim>
<form id=paddingf method=post action=rpc/pad.jim>
<input type=hidden id=paddingsid name=slot value=0>
<table class=keyval>
<tr>

353
webif/html/sched/list/index.jim Executable file
View File

@ -0,0 +1,353 @@
#!/mod/bin/jimsh
package require cgi
source /mod/webif/lib/setup
require altrow rsv.class progressbar epg.class system.class
noheader
set renderstart [clock milliseconds]
set svcmap {}
lmap i \
[$::channeldb query {select hSvc, usSvcid from TBL_SVC}] \
{
set svcmap([lindex $i 1]) [lindex $i 3]
}
set svckeys [array names svcmap]
set conflictstart [clock milliseconds]
set conflicts [rsv newconflicts [system tuners] "xlist"]
set conflicttime [expr ([clock milliseconds] - $conflictstart) / 1000.0]
if {[llength $conflicts] > 1} {
puts "<div class=warningbox><div><center>
Your recording schedule contains conflicts; shown in pink below.
</center></div></div>"
}
require epg_popup
set pkey_tags {
1 "Pending unschedule"
2 "Pending padding change"
3 "Pending padding change"
4 "Pending folder change"
5 "Pending episode skip"
6 "Pending refresh"
}
proc eventheader {{table TBL_RESERVATION} note} {
if {$table eq "pending"} {
set sortlist {[[6,0]]}
} else {
set sortlist {[[5,0]]}
}
puts "
<fieldset id=sched_fs_$table
style=\"display: inline; padding: 1em\">
<legend>
"
puts "<h3>$note</h3>"
puts "
</legend>
<table class=\"schedule tablesorter\" data-sortlist=\"$sortlist\">
<thead>
<tr>
<th data-sorter=false>&nbsp;</th>
"
if {$table eq "pending"} { puts "<th>Action</th>" }
puts {
<th>&nbsp;</th>
<th data-sorter=false>&nbsp;</th>
<th>Channel</th>
<th data-sorter=programme>Programme</th>
<th data-sorter=xdate>Start Time</th>
<th>Duration</th>
<th data-sorter=false>Mode</th>
<th data-sorter=false>&nbsp;</th>
</tr>
</thead>
<tbody>
}
}
proc eventrow {event {table TBL_RESERVATION}} {
global svcmap svckeys num_ended conflicts pmap pkey_tags
set name [$event name]
set s [$event start]
set d [$event get nduration]
set e $($s + $d)
set n [clock seconds]
if {$n > $e && [$event get ersvtype] <= 3} {
set ended 1
incr num_ended
} else {
set ended 0
}
set attrs "table=$table sid=[$event get ulslot] \
reckind=[$event get ucRecKind] rsvtype=[$event get ersvtype] \
ar=[expr ! [$event padded]] ended=$ended"
set ek [ekey $event]
set opts 1
set tag ""
if {[dict exists $pmap $ek]} {
set act $pmap($ek)
switch $act {
1 -
5 {
append attrs " class=\"blueshade strike\""
set opts 0
}
2 -
3 -
4 {
set opts 0
append attrs " class=orangeshade"
}
6 {
append attrs " class=orangeshade"
}
}
if {[dict exists $pkey_tags $act]} {
set tag $pkey_tags($act)
}
}
altrow $attrs
# Checkbox
puts "<td><input type=checkbox class=schedselect></td>";
if {$table eq "pending"} {
puts "<td align=center>[$event pendingicon]</td>"
}
puts "<td>[$event get ulslot]</td>"
if {[$event get usLcn] ne ""} {
puts "
<td>
[epg channelicon [$event channel_name] 50]
</td>
<td nowrap>[$event get usLcn]<br>
[$event channel_name]</td>
"
} else {
puts "<td>&nbsp;<br><br></td><td>&nbsp;</td>"
}
puts "<td nowrap>
<a slot=[$event get ulslot] table=$table
href=# class=schedule>$name</a>"
if {
([$event get ucRecKind] == 4 && $name ne [$event folder]) ||
([$event get ucRecKind] == 1 && [$event folder] ne "")
} {
puts "<br><span class=also>&nbsp;(Folder: [$event folder])</span>"
}
if {$tag ne ""} {
puts "<br><span class=blood>&nbsp;$tag</span>"
}
puts "</td>"
if {$ended} {
set ds "[clock format $s -format {%a %d %b %Y %H:%M}]"
puts "<td nowrap class=va>
--- -- --- ---- --:--<br>
<span class=footnote>(Last: $ds)</span>"
puts "</td><td>--:--:--</td>"
} else {
set elist [$event aul]
set ds "[clock format $s -format {%a %d %b %Y %H:%M}]"
puts "<td nowrap class=\"va"
if {$n > $e} { puts -nonewline " blood" }
if {"[$event get ulslot][$event end]" in $conflicts} {
puts -nonewline " pinkshade"
}
puts "\">"
if {[llength $elist] > 0} {
lassign [lindex $elist 0] service_id start end event_id
if {$start == $s && $service_id in $svckeys} {
puts "<a class=event href=#
xs=$svcmap($service_id) xe=$event_id>
<span class=ds>$ds</span></a>"
} else {
puts "<span class=ds>$ds</span>"
}
} else {
puts "<span class=ds>$ds</span>"
}
if {$d > 0 && $n > $s && $n < $e} {
puts "<br>"
set perc [expr [expr $n - $s] * 100 / $d]
puts "<img class=va
src=/images/745_1_11_Video_1REC.png>"
puts [progressbar $perc]
}
if {[llength $elist] > 1} {
set i 0
set c [llength $elist]
set max 5
foreach e $elist {
lassign $e service_id start end event_id
if {$start == $s} continue
if {[incr i] == $max} {
set remain $($c - $i)
puts "
<div class=\"moretoshow footnote\">
<center>
Click for $remain more...
</center>
</div>
<div class=\"hidden moretoshowc\">
"
} else {
puts "<br>"
}
set xcl "also"
if {"[$event get ulslot]${end}" in $conflicts} {
append xcl " pinkshade"
}
puts "<span class=\"$xcl\">"
if {$service_id in $svckeys} {
puts "<a class=event href=#
xs=$svcmap($service_id) xe=$event_id>
[clock format $start \
-format {%a %d %b %Y %H:%M}]</a>"
if {$service_id != [$event get hsvc]} {
set ch [system strip [\
get_channel_attr_byhsvc \
$service_id szSvcName]]
puts "<img width=15 src=\"[\
epg channeliconpath $ch]\""
puts "title=\"$ch\" alt=\"$ch\">"
}
} else {
puts "[clock format $start \
-format {%a %d %b %Y %H:%M}]
($service_id)<br>"
}
puts "</span>"
}
if {$i >= $max} {
puts "</div>"
}
}
puts "</td><td align=center>"
set xndur [$event get nduration]
if {$xndur > 0} {
puts "[clock format $xndur -format %H:%M]"
} else {
puts "--:--:--"
}
if {[llength $elist] > 1} {
set i 0
foreach e $elist {
lassign $e service_id start end event_id
if {$start == $s} continue
if {[incr i] == $max} {
puts "<div class=\"hidden moretoshowc\">"
} else {
puts "<br>"
}
puts "<span class=also>
[clock format $($end - $start) \
-format {%H:%M}]</span>"
}
if {$i >= $max} {
puts "</div>"
}
}
puts "</td>"
}
puts "<td nowrap>[join [$event iconset] ""]</td>"
puts "<td>"
if {$opts} {
puts "<a href=# class=smenu sid=[$event get ulslot]>
<img border=0 src=/img/more_btn.gif></a>"
}
puts "</td></tr>"
}
proc eventfooter {rawlink} {
global num_ended
puts "</tbody></table>"
puts "
<br>
<button class=selall>Select All</button>
<button class=selnone>Select None</button>
"
if {$num_ended} {
puts "
<button class=selended>Select Finished</button>
"
}
puts "
<button class=\"delselected red\">Delete Selected
<span class=delselcnt></span>
</button>
<button class=\"manual_rsv blue\">Manual Event</button>
<button class=rawview
path=\"$rawlink\"
>Raw Database</button>
</fieldset>
"
}
proc ekey {event} {
set ret ""
foreach key {ersvtype nsttime nduration hsvc usevtid} {
append ret ".[$event get $key]"
}
return $ret
}
set pmap {}
set events [rsv list pending]
set num_ended 0
if {[llength $events] > 0} {
eventheader pending "Pending Actions"
foreach event $events {
eventrow $event pending
set pmap([ekey $event]) [$event get action]
}
eventfooter "db=rsvp.db&tab=pending"
}
set events [rsv list]
set num_ended 0
eventheader live "Scheduled Events"
foreach event $events {
eventrow $event TBL_RESERVATION
}
eventfooter "db=rsv.db&tab=TBL_RESERVATION"
source assets.jim
puts "<div class=footnote>Conflict evaluation: $conflicttime seconds</div>"
footer

439
webif/html/sched/list/script.js Executable file
View File

@ -0,0 +1,439 @@
function list_loaded()
{
$('table.tablesorter')
.tablesorter({
theme: 'webif',
widthFixed: false,
widgets: ['zebra', 'stickyHeaders']
});
function docancel()
{
var table = $('#sdialogue').attr('table');
var slot = $('#sdialogue').attr('slot');
if (confirm('Really remove scheduled event?'))
{
$.get('rpc/cancel.jim?slot=' + slot +
'&table=' + table, function() {
page_refresh();
});
$('#sdialogue').dialog('close');
}
}
var $buttons1 = {
"Close" : function() {$(this).dialog('close');}
};
var $buttons2 = $.extend(
{"Cancel Event": function() { docancel() }},
$buttons1);
var $dialog = $('#sdialogue').dialog({
title: "Schedule Details",
modal: false, autoOpen: false,
height: 500, width: 700,
show: 'scale', hide: 'fade',
draggable: true, resizable: true,
buttons: $buttons2,
close: function(e,u) { $('#sdialogue').empty().html(
'<img src="/img/loading.gif" alt="loading">'); }
});
function schedpopup(e, o)
{
e.preventDefault();
var slot = o.attr('slot');
var table = o.attr('table');
$('#sdialogue').attr('slot', slot).attr('table', table);
var url = 'rpc/info.jim?slot=' + slot +
'&table=' + table;
$('#sdialogue').load(url);
$dialog.dialog('open');
}
$('a.schedule').click(function(e) { schedpopup(e, $(this)) });
$('.schedselect:checked').prop('checked', false);
$('button.delselected').button({icons:{primary:"ui-icon-trash"}})
.button('disable')
.on('click', function() {
$(this).dojConfirmAction({
question: 'Delete selected?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
$.blockUI({
message: '<h1><img src=/img/loading.gif> Deleting... </h1>'
});
var els = $(el).parent().find('.schedselect:checked');
var tab = $(els).first().closest('tr').attr('table');
var slots = $.map(els, function(v) {
return $(v).closest('tr').attr('sid');
});
$.get('rpc/cancel.jim?slot=' + slots.join(',') +
'&table=' + tab, function() {
page_refresh();
});
});
});
$('.schedselect').on('change', function() {
var num = $(this).closest('table').find('.schedselect:checked').size();
var but = $(this).closest('fieldset').find('button.delselected');
if (num)
$(but).button('enable').find('.delselcnt')
.text('(' + num + ')');
else
$(but).button('disable').find('.delselcnt').empty();
});
$('button.selall').button({icons:{primary:"ui-icon-check"}})
.on('click', function() {
$(this).parent().find('table .schedselect').prop('checked', true)
.first().trigger('change');
});
$('button.selnone').button({icons:{primary:"ui-icon-close"}})
.on('click', function() {
$(this).parent().find('table .schedselect').prop('checked', false)
.first().trigger('change');
});
$('button.selended').button({icons:{primary:"ui-icon-stop"}})
.on('click', function() {
$(this).parent().find('table .schedselect').prop('checked', false);
$(this).parent().find('table tr[ended="1"] .schedselect')
.prop('checked', true).first().trigger('change');
});
$('button.rawview').button({icons:{primary:"ui-icon-wrench"}})
.on('click', function() {
window.location = '/db/index.jim?' + $(this).attr('path');
});
// Menu
var $paddialog = $('#padding').dialog({
title: "Padding values",
modal: true, autoOpen: false,
height: 'auto', width: 'auto',
show: 'scale', hide: 'fade',
draggable: false, resizable: false,
buttons: {
"Confirm": function() {
$('#paddingf').ajaxSubmit({
dataType: 'text',
success: function(data) {
page_refresh();
}
});
},
"Cancel": function() { $(this).dialog('close'); }
}
});
$('#fchange').dialog({
autoOpen: false,
height: 'auto', width: 'auto',
modal: true,
buttons: {
"Update": function() {
var s = $('#fchangeform').serialize();
$.get('rpc/folder.jim?' + s,
function() { page_refresh(); });
},
"Close": function() {
$(this).dialog('close');
}
},
close: function() {
$('#changeslot').val(0);
$('#fchangename').val('');
}
});
function preparemenu(el, menu)
{
if (!$(el).is("tr"))
el = $(el).closest('tr');
table = $(el).attr('table');
rsvtype = $(el).attr('rsvtype');
reckind = $(el).attr('reckind');
if (table != 'pending' && rsvtype == 3)
{
if ($(el).attr('ar') == 1)
$('#optmenu').changeContextMenuItem('#ar',
'Disable AR');
else
$('#optmenu').changeContextMenuItem('#ar',
'Enable AR');
$('#optmenu').enableContextMenuItems('#ar');
}
else
$('#optmenu').disableContextMenuItems('#ar');
if (reckind == 4)
$('#optmenu').enableContextMenuItems('#mkfolder');
else
$('#optmenu').disableContextMenuItems('#mkfolder');
if (table != 'pending' && rsvtype == 3 && reckind == 4)
$('#optmenu').enableContextMenuItems('#refresh');
else
$('#optmenu').disableContextMenuItems('#refresh');
if (reckind == 4)
$('#optmenu').enableContextMenuItems('#folder');
else
$('#optmenu').disableContextMenuItems('#folder');
}
function menuclick(action, el, pos)
{
if (!$(el).is("tr"))
el = $(el).closest('tr');
var sid = $(el).attr('sid');
var table = $(el).attr('table');
//if (window.console)
//console.log("Got %s, el: %o, id: %d", action, el, sid);
switch (action)
{
case 'delete':
if (confirm('Are you sure you want to delete this entry?'))
$.get('rpc/cancel.jim?slot=' + sid +
'&table=' + table,
function() {
page_refresh();
});
break;
case 'ar':
if ($(el).attr('ar') == 1)
{
$('#paddingsid').val(sid);
$paddialog.dialog('open');
}
else
$.get('rpc/ar.jim?slot=' + sid,
function() {
page_refresh();
});
break;
case 'folder':
$('#fchangeslot').val(sid);
$('#fchangename').val($(el).find('a.schedule').text());
$('#fchangetable').val($(el).attr('table'));
$('#fchange').dialog('open');
break;
case 'mkfolder':
$('#list_output')
.empty()
.show('slow')
.load('rpc/mkdir.jim?slot=' + sid + '&table=' + table,
function() {
$('#list_output')
.css('font-style', 'italic')
.delay(5000).fadeOut('slow');
});
break;
case 'refresh':
if (confirm('Are you sure you want to refresh events ' +
'for this entry?'))
$.get('rpc/refresh.jim?slot=' + sid,
function() {
visual_reload_required = true;
$.growl.warning({ title: 'Success',
message: "The schedule entry will be " +
"refreshed." });
$epgpopup.dialog('close');
page_refresh();
});
break;
default:
alert('Unhandled menu event, ' + action);
}
}
$('table.schedule tbody tr').hover(
function() { $(this).addClass('hover'); },
function() { $(this).removeClass('hover'); })
.contextMenu({menu: 'optmenu', beforeShow: preparemenu}, menuclick);
$('a.smenu')
.contextMenu({menu: 'optmenu', leftButton: true, beforeShow: preparemenu}, menuclick);
$('#schedule_cleanup').bind('click', function(e) {
if (confirm('Are you sure you want to remove all finished recordings?'))
$.get('rpc/cleanup.jim',
function() { page_refresh(); }
);
});
// Manual reservation
$('#manrsv').dialog({
autoOpen: false,
height: 'auto', width: 'auto',
modal: true,
buttons: {
"Create event": function() {
var data = $('#mrform').serializeArray();
var s = $('#mrstime').timepicker('getTime',
$('#mrsdate').datepicker('getDate'));
if (s)
data.push({ name: "start", value: s.getTime() / 1000});
var e = $('#mretime').timepicker('getTime',
$('#mredate').datepicker('getDate'));
if (e)
data.push({ name: "end", value: e.getTime() / 1000});
if (s.getTime() >= e.getTime())
{
$('#mrerr').html('Invalid time range...');
return;
}
$('#mrerr')
.html('<img src=/img/loading.gif> Creating event...');
$.getJSON('rpc/manual.jim', data, function(d) {
if (d.status)
page_refresh();
else if (d.errfields)
{
d.errfields.forEach(function(item) {
$('#mrform input[name=' + item + ']')
.addClass('error');
});
$('#mrerr').html('The start and end times '
+ 'must be provided.');
}
else if (d.err)
$('#mrerr').html(d.err);
});
},
"Cancel": function() {
$(this).dialog('close');
}
}
});
$('button.manual_rsv').button({icons:{primary:"ui-icon-clock"}})
.on('click', function() {
$('#mrform').get(0).reset();
$('#mrform input.date').datepicker('setDate', null);
$('#mrform input.time').timepicker('setTime', null);
$('#manrsv').dialog('open');
// $("#manrsv .ui-dialog-buttonpane button:contains('Create')")
// .button('disable')
if ($('#mrlcn').hasClass('blood'))
{
var $s = $('#mrlcn');
$.getJSON('/cgi-bin/chanlist.jim', function(data) {
$s.find('option').remove();
$.each(data, function(lcn, name) {
$('<option>')
.val(lcn)
.text(lcn + ' - ' + name)
.appendTo($s);
});
$('#mrlcn').removeClass('blood');
});
}
});
$('#mrform input.time').timepicker({
showDuration: true,
timeFormat: 'g:ia',
step:5
});
function dayfilter(date)
{
var cur = $('#mrrepeat').val();
if (cur < 3) return [1, ""];
var day = date.getDay();
if (cur == 3) // Weekdays only
return [(day > 0 && day < 6), ""];
else // Weekends only
return [day == 0 || day >= 6, ""];
}
$('#mrsdate').datepicker({
firstDay: 1,
defaultDate: 0,
minDate: 0,
maxDate: "+1Y",
dateFormat: "D, dd/mm/yy",
autoclose: true,
beforeShowDay: dayfilter,
onClose: function(s) {
var dat = $(this).datepicker('getDate');
if (dat)
dat.setDate(dat.getDate() + 1);
$('#mredate')
.datepicker('setDate', s)
.datepicker('option', 'minDate', s)
.datepicker('option', 'maxDate', dat);
}
});
$('#mredate').datepicker({
firstDay: 1,
defaultDate: 0,
minDate: 0,
maxDate: "+1Y",
autoclose: true,
dateFormat: "D, dd/mm/yy",
beforeShowDay: dayfilter
});
$('#mrrepeat').on('change', function() {
$('#mrform input.date').datepicker('refresh');
});
$('#mrform').datepair({
defaultDateDelta: null,
defaultTimeDelta: 3600000,
parseDate: function (el) {
var utc = new Date($(el).datepicker('getDate'));
return utc && new Date(utc.getTime() +
(utc.getTimezoneOffset() * 60000));
},
updateDate: function (el, v) {
$(el).datepicker('setDate', v);
}
});
$('.moretoshow')
.hover(
function() { $(this).css('cursor', 'pointer'); },
function() { $(this).css('cursor', 'auto'); }
)
.on('click', function() {
el = $(this).closest('tr');
$(el).find('.moretoshow').hide();
$(el).find('.moretoshowc').slideDown('slow');
});
}

View File

@ -19,9 +19,7 @@ foreach event $events {
$event insert
system restartpending
}
}
footer

View File

@ -0,0 +1,23 @@
#!/mod/bin/jimsh
package require cgi
source /mod/webif/lib/setup
require rsv.class system.class
httpheader
set slots [cgi_get slot 0]
set table [cgi_get table TBL_RESERVATION]
if {$table eq "pending"} exit
foreach slot [split $slots ","] {
set event [rsv slot $table $slot]
$event clear_ulslot
$event set_refresh
$event insert
}
system restartpending

45
webif/html/sched/rpc/skip.jim Executable file
View File

@ -0,0 +1,45 @@
#!/mod/bin/jimsh
package require cgi
source /mod/webif/lib/setup
require rsv.class epg.class system.class
httpheader
set slots [cgi_get slot -]
set xservice [cgi_get service 0]
set xevent [cgi_get event 0]
if {$slots eq "-"} exit
foreach slot [split $slots ","] {
puts "SLOT: $slot"
set event [rsv slot $slot]
lassign [epg fetch dump -service $xservice -event $xevent] epg
if {$epg eq ""} {
puts "!Cannot find event in EPG."
break
}
$epg get_channel_info
# First check to see if there is already a pending skip for this
# event and, if so, update that one.
set crid "[$epg get channel_crid][$epg get series_crid]"
set ev [rsv fetch pending [$event get ersvtype] \
[$event get hsvc] 0 [$event get usevtid] $crid]
if {$ev eq "0"} {
# No pending event
puts "No pending event."
set ev $event
$ev clear_ulslot
}
puts "Set skip $xevent"
$ev set_skip $epg
$ev insert
}
system restartpending

View File

@ -1,4 +1,14 @@
var spinner = '<div class=spinner><img border=0 src=/img/loading.gif>' +
'&nbsp;Retrieving data... Please wait...</div>';
var LIST_INDEX = 0;
var VISUAL_INDEX = 1;
var BACKUP_INDEX = 3;
var list_reload_required = true;
var visual_reload_required = true;
$.tablesorter.addParser({
id: 'programme',
is: function () { return false; },
@ -20,410 +30,77 @@ $.tablesorter.addParser({
type: 'numeric'
});
function page_refresh(msg)
{
if (!msg)
msg = 'Refreshing page...';
$.blockUI({
message: '<h1><img src=/img/loading.gif> ' + msg + '</h1>'
});
window.location.reload(true);
}
$(function() {
$('table.tablesorter')
.tablesorter({
theme: 'webif',
widthFixed: false,
widgets: ['zebra', 'stickyHeaders']
});
// Retrieve the stored selected tab from the hash portion of the URL.
var curtab = ~~(window.location.hash.slice(1));
if (curtab < 0 || curtab > 2)
curtab = 0;
function docancel()
{
var table = $('#sdialogue').attr('table');
var slot = $('#sdialogue').attr('slot');
if (confirm('Really remove scheduled event?'))
{
$.get('cancel.jim?slot=' + slot +
'&table=' + table, function() {
window.location.reload(true);
});
$('#sdialogue').dialog('close');
}
}
var $buttons1 = {
"Close" : function() {$(this).dialog('close');}
};
var $buttons2 = $.extend(
{"Cancel Event": function() { docancel() }},
$buttons1);
var $dialog = $('#sdialogue').dialog({
title: "Schedule Details",
modal: false, autoOpen: false,
height: 500, width: 700,
show: 'scale', hide: 'fade',
draggable: true, resizable: true,
buttons: $buttons2,
close: function(e,u) { $('#sdialogue').empty().html(
'<img src="/img/loading.gif" alt="loading">'); }
});
function schedpopup(e, o)
{
e.preventDefault();
var slot = o.attr('slot');
var table = o.attr('table');
$('#sdialogue').attr('slot', slot).attr('table', table);
var url = 'info.jim?slot=' + slot +
'&table=' + table;
$('#sdialogue').load(url);
$dialog.dialog('open');
}
$('a.schedule').click(function(e) { schedpopup(e, $(this)) });
$('.schedselect:checked').prop('checked', false);
$('button.delselected').button({icons:{primary:"ui-icon-trash"}})
.button('disable')
.on('click', function() {
$(this).dojConfirmAction({
question: 'Delete selected?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
$.blockUI({
message: '<h1><img src=/img/loading.gif> Deleting... </h1>'
});
var els = $(el).parent().find('.schedselect:checked');
var tab = $(els).first().closest('tr').attr('table');
var slots = $.map(els, function(v) {
return $(v).closest('tr').attr('sid');
});
$.get('cancel.jim?slot=' + slots.join(',') +
'&table=' + tab, function() {
window.location.reload(true);
});
});
});
$('.schedselect').on('change', function() {
var num = $(this).closest('table').find('.schedselect:checked').size();
var but = $(this).closest('fieldset').find('button.delselected');
if (num)
$(but).button('enable').find('.delselcnt')
.text('(' + num + ')');
else
$(but).button('disable').find('.delselcnt').empty();
});
$('button.selall').button({icons:{primary:"ui-icon-check"}})
.on('click', function() {
$(this).parent().find('table .schedselect').prop('checked', true)
.first().trigger('change');
});
$('button.selnone').button({icons:{primary:"ui-icon-close"}})
.on('click', function() {
$(this).parent().find('table .schedselect').prop('checked', false)
.first().trigger('change');
});
$('button.selended').button({icons:{primary:"ui-icon-stop"}})
.on('click', function() {
$(this).parent().find('table .schedselect').prop('checked', false);
$(this).parent().find('table tr[ended="1"] .schedselect')
.prop('checked', true).first().trigger('change');
});
$('button.rawview').button({icons:{primary:"ui-icon-wrench"}})
.on('click', function() {
window.location = '/db/index.jim?' + $(this).attr('path');
});
$('button.backup').button({icons:{primary:"ui-icon-disk"}})
.on('click', function() {
window.location = '/backup/index.jim';
});
// Menu
var $paddialog = $('#padding').dialog({
title: "Padding values",
modal: true, autoOpen: false,
height: 'auto', width: 'auto',
show: 'scale', hide: 'fade',
draggable: false, resizable: false,
buttons: {
"Confirm": function() {
$('#paddingf').ajaxSubmit({
dataType: 'text',
success: function(data) {
window.location.reload(true);
}
});
},
"Cancel": function() { $(this).dialog('close'); }
}
});
$('#fchange').dialog({
autoOpen: false,
height: 'auto', width: 'auto',
modal: true,
buttons: {
"Update": function() {
var s = $('#fchangeform').serialize();
$.get('folder.jim?' + s,
function() { window.location.reload(true); });
},
"Close": function() {
$(this).dialog('close');
}
$('#minimaltabbar').tabs({
active: curtab,
heightStyle: "auto",
spinner: spinner,
create: function(event, ui) {
$(ui.panel).html(spinner);
},
close: function() {
$('#changeslot').val(0);
$('#fchangename').val('');
}
});
function preparemenu(el, menu)
{
if (!$(el).is("tr"))
el = $(el).closest('tr');
if ($(el).attr('table') != 'pending' && $(el).attr('rsvtype') == 3)
{
if ($(el).attr('ar') == 1)
$('#optmenu').changeContextMenuItem('#ar',
'Disable AR');
else
$('#optmenu').changeContextMenuItem('#ar',
'Enable AR');
$('#optmenu').enableContextMenuItems('#ar');
}
else
$('#optmenu').disableContextMenuItems('#ar');
if ($(el).attr('reckind') == 4)
$('#optmenu').enableContextMenuItems('#mkfolder');
else
$('#optmenu').disableContextMenuItems('#mkfolder');
// if ($(el).attr('table') != 'pending' && $(el).attr('reckind') == 4)
if ($(el).attr('reckind') == 4)
$('#optmenu').enableContextMenuItems('#folder');
else
$('#optmenu').disableContextMenuItems('#folder');
}
function menuclick(action, el, pos)
{
if (!$(el).is("tr"))
el = $(el).closest('tr');
var sid = $(el).attr('sid');
var table = $(el).attr('table');
//if (window.console)
//console.log("Got %s, el: %o, id: %d", action, el, sid);
switch (action)
{
case 'delete':
if (confirm('Are you sure you want to delete this entry?'))
$.get('cancel.jim?slot=' + sid +
'&table=' + table,
function() {
window.location.reload(true);
});
break;
case 'ar':
if ($(el).attr('ar') == 1)
{
$('#paddingsid').val(sid);
$paddialog.dialog('open');
}
else
$.get('ar.jim?slot=' + sid,
function() {
window.location.reload(true);
});
break;
case 'folder':
$('#fchangeslot').val(sid);
$('#fchangename').val($(el).find('a.schedule').text());
$('#fchangetable').val($(el).attr('table'));
$('#fchange').dialog('open');
break;
case 'mkfolder':
$('#output')
.empty()
.show('slow')
.load('mkdir.jim?slot=' + sid + '&table=' + table,
function() {
$(output)
.css('font-style', 'italic')
.delay(5000).fadeOut('slow');
});
break;
default:
alert('Unhandled menu event, ' + action);
}
}
$('table.schedule tbody tr').hover(
function() { $(this).addClass('hover'); },
function() { $(this).removeClass('hover'); })
.contextMenu({menu: 'optmenu', beforeShow: preparemenu}, menuclick);
$('a.smenu')
.contextMenu({menu: 'optmenu', leftButton: true, beforeShow: preparemenu}, menuclick);
$('#schedule_cleanup').bind('click', function(e) {
if (confirm('Are you sure you want to remove all finished recordings?'))
$.get('cleanup.jim',
function() { window.location.reload(true);
});
});
// Manual reservation
$('#manrsv').dialog({
autoOpen: false,
height: 'auto', width: 'auto',
modal: true,
buttons: {
"Create event": function() {
var data = $('#mrform').serializeArray();
var s = $('#mrstime').timepicker('getTime',
$('#mrsdate').datepicker('getDate'));
if (s)
data.push({ name: "start", value: s.getTime() / 1000});
var e = $('#mretime').timepicker('getTime',
$('#mredate').datepicker('getDate'));
if (e)
data.push({ name: "end", value: e.getTime() / 1000});
if (s.getTime() >= e.getTime())
{
$('#mrerr').html('Invalid time range...');
return;
}
$('#mrerr')
.html('<img src=/img/loading.gif> Creating event...');
$.getJSON('manual.jim', data, function(d) {
if (d.status)
window.location.reload(true);
else if (d.errfields)
{
d.errfields.forEach(function(item) {
$('#mrform input[name=' + item + ']')
.addClass('error');
});
$('#mrerr').html('The start and end times '
+ 'must be provided.');
}
else if (d.err)
$('#mrerr').html(d.err);
});
},
"Cancel": function() {
$(this).dialog('close');
}
}
});
$('button.manual_rsv').button({icons:{primary:"ui-icon-clock"}})
.on('click', function() {
$('#mrform').get(0).reset();
$('#mrform input.date').datepicker('setDate', null);
$('#mrform input.time').timepicker('setTime', null);
$('#manrsv').dialog('open');
// $("#manrsv .ui-dialog-buttonpane button:contains('Create')")
// .button('disable')
if ($('#mrlcn').hasClass('blood'))
{
var $s = $('#mrlcn');
$.getJSON('/cgi-bin/chanlist.jim', function(data) {
$s.find('option').remove();
$.each(data, function(lcn, name) {
$('<option>')
.val(lcn)
.text(lcn + ' - ' + name)
.appendTo($s);
});
$('#mrlcn').removeClass('blood');
});
}
});
$('#mrform input.time').timepicker({
showDuration: true,
timeFormat: 'g:ia',
step:5
});
function dayfilter(date)
{
var cur = $('#mrrepeat').val();
if (cur < 3) return [1, ""];
var day = date.getDay();
if (cur == 3) // Weekdays only
return [(day > 0 && day < 6), ""];
else // Weekends only
return [day == 0 || day >= 6, ""];
}
$('#mrsdate').datepicker({
firstDay: 1,
defaultDate: 0,
minDate: 0,
maxDate: "+1Y",
dateFormat: "D, dd/mm/yy",
autoclose: true,
beforeShowDay: dayfilter,
onClose: function(s) {
var dat = $(this).datepicker('getDate');
if (dat)
dat.setDate(dat.getDate() + 1);
$('#mredate')
.datepicker('setDate', s)
.datepicker('option', 'minDate', s)
.datepicker('option', 'maxDate', dat);
}
});
$('#mredate').datepicker({
firstDay: 1,
defaultDate: 0,
minDate: 0,
maxDate: "+1Y",
autoclose: true,
dateFormat: "D, dd/mm/yy",
beforeShowDay: dayfilter
});
$('#mrrepeat').on('change', function() {
$('#mrform input.date').datepicker('refresh');
});
$('#mrform').datepair({
defaultDateDelta: null,
defaultTimeDelta: 3600000,
parseDate: function (el) {
var utc = new Date($(el).datepicker('getDate'));
return utc && new Date(utc.getTime() +
(utc.getTimezoneOffset() * 60000));
activate: function(event, ui) {
window.location.hash = ui.newTab.index();
},
updateDate: function (el, v) {
$(el).datepicker('setDate', v);
beforeLoad: function (event, ui) {
var content = $(ui.panel).html();
switch (ui.tab.index())
{
case LIST_INDEX:
if (list_reload_required)
content = false;
break;
case VISUAL_INDEX:
if (visual_reload_required)
content = false;
break;
case BACKUP_INDEX:
break;
default:
alert('Unknown tab just loaded.');
}
// Don't reload if content exists.
if (content)
event.preventDefault();
else
{
list_reload_required = false;
visual_reload_required = false;
$(ui.panel).html(spinner);
}
},
load: function(event, ui) {
switch (ui.tab.index())
{
case LIST_INDEX:
list_loaded(ui.panel);
break;
case VISUAL_INDEX:
visual_loaded(ui.panel);
break;
case BACKUP_INDEX:
backup_loaded(ui.panel);
break;
default:
alert('Unknown tab just loaded.');
}
}
});

View File

@ -0,0 +1,10 @@
#!/mod/bin/jimsh
puts {
<div id=epgpopup></div>
<ul id=optmenu class=contextMenu style="width: 200px">
<li class=separator><a href=#refresh>Refresh Events</a></li>
</ul>
}

456
webif/html/sched/visual/index.jim Executable file
View File

@ -0,0 +1,456 @@
#!/mod/bin/jimsh
package require cgi
source /mod/webif/lib/setup
require rsv.class epg.class system.class classdump
set debug 0
noheader
set renderstart [clock milliseconds]
set events [rsv allevents]
set pending [rsv list pending]
# hSvc -> usSvcId map
lmap i \
[$::channeldb query {select hSvc, usSvcid from TBL_SVC}] \
{
set hmap([lindex $i 1]) [lindex $i 3]
}
# usSvcId -> hSvc map
#set svcmap [lreverse $hmap]
proc pop_event {{debug 0}} {
uplevel {
lassign [lindex $events $eindex] \
start end hsvc eid slotid kind sclass
incr eindex
if {$debug} {
puts "
--------------------------------------------------
EVENT$eindex
START: $start [clock format $start]
END: $end [clock format $end]
HSVC: $hsvc
EID: $eid
SID: $slotid
KIND: $kind
SCLASS: $sclass
"
}
}
}
set pendingclasses {
1 "pending-unschedule"
2 "pending-ar"
3 "pending-ar"
4 "pending-folder"
5 "pending-skip"
6 "pending-refresh"
}
foreach p $pending {
set action [$p get action]
# Add - already handled.
if {$action eq "0"} continue
set s [rsv fetch "TBL_RESERVATION" \
[$p get ersvtype] [$p get hsvc] 0 [$p get usevtid]]
if {$s eq "0"} continue
set slot [$s get ulslot]
set elen [llength $events]
set eindex 0
while {$eindex < $elen} {
pop_event 1
if {$slotid ne [$s get ulslot]} continue
if {$action eq "5" && $start != [$p start]} continue
lset events $($eindex - 1) 6 $pendingclasses($action)
}
}
#foreach ev $events {
# puts $ev
#}
# Event array keys:
# 0: start
# 1: end
# 2: hSvc
# 3: event_id
# 4: Schedule Slot ID
# 5: ucRecKind
# 6: class (live, pending)
# Sort final events array
set events [lsort -index 0 -integer $events]
######################################################################
# Visualisation functions
# Intend 40 pixels / hour for fully populated timeline.
set width $(40 * 24)
# This is the total padding/margin/border width.
set pad 4
set padadj 0
# Returns an absolute number of pixels from the left corresponding
# to a number of seconds into the day.
proc secstopx {daysecs hourpx usedhours} {
# No floor() in Humax jim
#set max [expr floor($daysecs / 3600)]
set max [expr int($daysecs / 3600)]
# Subtract any unused hours from $daysecs.
for {set i 0} {$i < $max} {incr i} {
if {$i >= 24} break
if {![lindex $usedhours $i]} {
incr daysecs -3600
}
}
#return [expr floor($daysecs / 3600 * $hourpx)]
return [expr int($daysecs * $hourpx / 3600)]
}
# daysecs - absolute number of seconds into the day that needs to be reached.
# pxpos - current pixel position.
proc showpad {daysecs hourpx &pxpos usedhours} {
global debug pad padadj
# Determine required pixel position.
set newpos [set p [secstopx $daysecs $hourpx $usedhours]]
# Calculate required width of spacer
incr p -$pxpos
# Account for padding and any pending adjustment
incr p $(-($pad + $padadj))
if {$p <= 0} {
# If too narrow, show bare skip at full required width.
incr p $($pad + $padadj)
# Bare skip
set class "ct_bskip"
} else {
set class "ct_skip"
if {$debug} { append class " ct_debug" }
}
if {$p > 0} {
puts -nonewline \
"<div class=\"$class\" style=\"width: ${p}px;\">"
if {$debug} {
puts [format "@%d-%d s=%d w=%dpx @%dpx" \
$pxpos $padadj $daysecs $p $newpos]
}
puts -nonewline "</div>"
set pxpos $newpos
set padadj 0
}
}
proc showevent {s class &overflow &tomorrow &pxpos start end usedhours daystart dayend hourpx service event sclass} {
global debug pad padadj dev
# Required pixel position of event start.
set p [secstopx $($start - $daystart) $hourpx $usedhours]
if {$p < $pxpos} {
# Backwards move => tuner conflict.
lappend overflow [list $s $start $end $service $event $sclass]
return
}
# Show padding up to event.
showpad $($start - $daystart) $hourpx pxpos $usedhours
# And now the event itself.
set name [set channel "Unknown"]
set slotid -1
if {$s ne "0"} {
set name [$s name]
set channel [$s channel_name]
set slotid [$s get ulslot]
}
if {$service > 0} {
set epgs [epg dbfetch dump -service $service -event $event]
if {[llength $epgs] == 1} {
lassign $epgs epg
set name [$epg get name]
$epg get_channel_info
set channel [$epg get channel_name]
}
}
if {[string match -nocase "New:*" $name]} {
set name [string trim [string range $name 4 end]]
}
set name [cgi_quote_html $name]
if {$sclass == "pending"} {
append class " purpleshade"
} elseif {$sclass == "pending-unschedule" ||
$sclass == "pending-skip"} {
append class " strike purpleshade"
} elseif {[string match "*pending*" $sclass]} {
append class " orangeshade"
}
if {$end > $dayend + 1} {
# Push event into tomorrow.
if {$s ne "0"} { $s setorigstart $start }
lappend tomorrow [list $s $($dayend + 1) $end \
$service $event $sclass]
set newpos [set p [secstopx 86400 $hourpx $usedhours]]
append name "..."
} else {
set newpos [set p [\
secstopx $($end - $daystart) $hourpx $usedhours]]
if {$start == $daystart && $s ne "0" &&
[$s get _origstart] > 0} {
set name "..$name"
set start [$s get _origstart]
$s setorigstart 0
}
}
# Calculate width
incr p -$pxpos
# Account for padding
incr p -$pad
if {$p < 8} {
# Event too narrow, force to 8 pixels.
set padadj $(8 - $p)
set p 8
} else {
set padadj 0
}
puts -nonewline "<div class=\"$class ct_event\" style=\"width: ${p}px;\" sid=\"${slotid}\" sclass=\"$sclass\" period=\"[clock format $start -format {%H:%M}] - [clock format $end -format {%H:%M}]\" xs=$service xe=$event"
if {$s ne "0"} {
puts -nonewline " rc=\"[$s get ucRecKind]\" et=\"[$s get ersvtype]\" rt=\"[$s get erepeat]\""
if {[$s get szCRID] ne ""} {
puts " crid=\"[$s get szCRID]\""
}
}
puts -nonewline " sz=\"[cgi_quote_html $channel]\""
puts -nonewline ">"
if {$debug} {
puts -nonewline [format "@%d s=%d w=%dpx @%dpx /=%d" \
$pxpos $($end - $start) $p $newpos $padadj]
}
puts -nonewline "$name</div>"
set pxpos $newpos
}
proc render_timeline {usedhours} {
global debug events width pad hmap padadj
puts "<div class=ct_visual>\n"
set tothours [ladd $usedhours]
if {!$tothours} {
puts "No schedule data found."
return
}
# calculate pixels per hour
#set hourpx [expr floor($width / $tothours)]
set hourpx [expr int($width / $tothours)]
if {$debug} {
puts "USEDHOURS: $usedhours<br>\n"
puts "HOURPX: $hourpx (tothours: $tothours)<br>\n"
}
# Show the hour bar.
puts "<div class=ct_header><div class=ct_row>"
puts "<div class=ct_dayname>&nbsp;</div>"
# Account for element padding and margin
set px $($hourpx - $pad)
for {set i 0} {$i < 25} {incr i} {
if {[lindex $usedhours $i] ne "1"} continue
puts -nonewline \
"<div class=ct_hour style=\"width:${px}px\"><center>[\
format "%02dh" $i]</center></div>"
}
puts "</div></div>\n"
# To store events which overflow into tomorrow.
set tomorrow {}
# Midnight today.
set day [clock scan \
[clock format [clock seconds] -format "%Y:%m:%d:0:0:0"] \
-format "%Y:%m:%d:%T"]
set elength [llength $events]
set eindex 0
set start 0
while {$start < $day} { pop_event }
for {set i 0} {$i < 8} {incr i} {
# To store things that need to move to the next row.
set rows {}
if {$i % 2} {
set class "odd"
} else {
set class "even"
}
set eclass "blueshade"
set dayend [set daystart $day]
incr dayend 86399
# Row 1
puts "<div class=\"ct_day $class\">"
puts "<div class=\"ct_row\">"
puts "<div class=ct_dayname>[clock format $day \
-format {%a %d/%m/%Y}]</div>"
set pxpos [set padadj 0]
if {[llength $tomorrow]} {
set dummy {}
foreach row $tomorrow {
lassign $row s xstart xend xs xe xsclass
showevent $s $eclass rows dummy \
pxpos $xstart $xend $usedhours \
$daystart $dayend $hourpx \
$xs $xe $xsclass
}
set tomorrow {}
}
while {$start >= $daystart && $start <= $dayend} {
if {$slotid ne "-1"} {
set tab "TBL_RESERVATION"
if {$sclass eq "pending"} {
set tab "pending"
}
set s [rsv slot $tab $slotid]
} else {
set s "0"
}
if {[dict exists $hmap $hsvc]} {
set service $hmap($hsvc)
} else {
set service 0
}
showevent $s $eclass rows tomorrow pxpos \
$start $end $usedhours \
$daystart $dayend $hourpx \
$service $eid $sclass
pop_event
}
# Close row
showpad 86400 $hourpx pxpos $usedhours
puts "</div>"
# Output other rows if necessary
while {[llength $rows]} {
set nrows {}
puts "<div class=\"ct_row $class\"><div class=ct_dayname></div>"
set pxpos [set padadj 0]
foreach row $rows {
lassign $row s xstart xend xs xe xsclass
showevent $s $eclass nrows tomorrow \
pxpos $xstart $xend $usedhours \
$daystart $dayend $hourpx \
$xs $xe $xsclass
}
showpad 86400 $hourpx pxpos $usedhours
puts "</div>"
set rows $nrows
set eclass "pinkshade"
}
incr day 86400
# Close ct_day
puts "</div>"
}
puts "\n</div>\n"
}
######################################################################
# Compressed Day Schedule Visualisation
set usedhours [lrepeat 24 0]
# Determine which hours are usde.
foreach e $events {
lassign $e start end
set shour [clock format $start -format "%H"]
set ehour [clock format $end -format "%H"]
if {$ehour < $shour} {
incr ehour 24
}
for {set i $shour} {$i <= $ehour} {incr i} {
set index $i
if {$i > 23} { incr index -24 }
lset usedhours $index 1
}
}
# To show 24 hours
# set usedhours [lrepeat 24 1]
puts "
<fieldset id=sched_timeline class=visual>
<legend>
<h3>Schedule</h3>
</legend>
"
render_timeline $usedhours
puts "</fieldset>"
puts {
<div id=legend>
<h3 style="display: inline-block">Key:</h3>
<div class="ct_key blueshade">
Scheduled event
</div>
<div class="ct_key pinkshade">
Conflict
</div>
<div class="ct_key orangeshade">
Pending change
</div>
<div class="ct_key purpleshade">
Pending <b>schedule</b>
</div>
<div class="ct_key purpleshade strike">
Pending <b>unschedule</b>
</div>
</div>
}
source assets.jim
footer

View File

@ -0,0 +1,414 @@
var visual_notes = {
'pending': 'Pending schedule command',
'pending-unschedule': 'Pending unschedule',
'pending-folder': 'Pending folder change',
'pending-ar': 'Pending padding change',
'pending-refresh': 'Pending refresh',
'pending-skip': 'Pending episode skip'
};
function visual_loaded()
{
$epgpopup = $('#epgpopup').dialog({
title: "Programme Details",
modal: false, autoOpen: false,
height: 500, width: 700,
show: 'scale', hide: 'fade',
draggable: true, resizable: true,
buttons: [
{
text: 'Cancel Entire Recording',
id: 'b_cancel',
class: 'ep_button',
icons: { primary: "ui-icon-trash" },
click: cancel_recording
},
{
text: 'Skip This Episode',
id: 'b_skip',
class: 'ep_button',
icons: { primary: "ui-icon-seek-next" },
click: skip_episode
},
{
text: 'Record This Showing Instead',
id: 'b_alternate',
class: 'ep_button',
icons: { primary: "ui-icon-transfer-e-w" },
click: alternate_recording
},
{
text: 'Switch to This Series',
id: 'b_alternateseries',
class: 'ep_button',
icons: { primary: "ui-icon-shuffle" },
click: alternate_series
},
{
text: 'More',
id: 'b_more',
class: 'ep_button',
icons: { primary: "ui-icon-arrowthick-1-s" },
click: function(e) { e.preventDefault(); }
},
{
text: 'Close',
icons: { primary: "ui-icon-close" },
click: function() { $(this).dialog('close'); }
},
]
});
function cancel_recording()
{
$('#b_cancel').dojConfirmAction({
question: 'Cancel Recording?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
var sid = $epgpopup.attr('sid');
$.get('rpc/cancel.jim?slot=' + sid,
function() {
$('.ct_event[sid=' + sid + ']')
.attr('sclass', 'pending-unschedule')
.addClass('purpleshade strike');
$epgpopup.dialog('close');
$.growl.error({ title: 'Success',
message:
"The recording has been cancelled." });
list_reload_required = true;
});
});
}
function skip_episode()
{
$('#b_skip').dojConfirmAction({
question: 'Skip Episode?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
var sid = $epgpopup.attr('sid');
var xs = $epgpopup.attr('xs');
var xe = $epgpopup.attr('xe');
$.get('rpc/skip.jim?slot=' + sid +
'&service=' + xs + '&event=' + xe,
function() {
$('.ct_event[sid=' + sid + ']')
.filter('[xe=' + xe + ']')
.attr('sclass', 'pending-skip')
.addClass('purpleshade strike');
$epgpopup.dialog('close');
$.growl.warning({ title: 'Success',
message: "The episode will be skipped." });
list_reload_required = true;
});
});
}
function alternate_series()
{
$('#b_alternateseries').dojConfirmAction({
question: 'Switch Series?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
//////////////////////////////////////////////////////////////////////
var sid = $epgpopup.attr('sid');
var xs = $epgpopup.attr('xs');
var xe = $epgpopup.attr('xe');
var reckind = $epgpopup.attr('reckind');
if (reckind != 4)
{
alert('Not a series!');
return;
}
$.blockUI({message: '<h1><img src=/img/loading.gif> ' +
'Re-scheduling series... </h1>'});
$.get('/cgi-bin/epg/schedule.jim?type=2&service=' + xs + '&event=' + xe,
function() {
$.growl.notice({ title: 'Success', message: "Scheduled new series." });
$.get('rpc/cancel.jim?slot=' + sid, function() {
$epgpopup.dialog('close');
$('.ct_event[sid=' + sid + ']')
.addClass('purpleshade strike');
$.unblockUI();
$.growl.error({ title: 'Success',
message: "Unscheduled old series." });
page_refresh();
});
});
//////////////////////////////////////////////////////////////////////
});
}
function alternate_recording()
{
$('#b_alternate').dojConfirmAction({
question: 'Move Recording?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
//////////////////////////////////////////////////////////////////////
var sid = $epgpopup.attr('sid');
var xs = $epgpopup.attr('xs');
var xe = $epgpopup.attr('xe');
var oxs = $epgpopup.attr('oxs');
var oxe = $epgpopup.attr('oxe');
var reckind = $epgpopup.attr('reckind');
// If it's a one-off event, schedule the new one and then cancel the old.
if (reckind != 4)
{
$.blockUI({message: '<h1><img src=/img/loading.gif> ' +
'Re-scheduling one-off event... </h1>'});
$.get('/cgi-bin/epg/schedule.jim?type=1&service=' + xs + '&event=' + xe,
function() {
$.growl.notice({ title: 'Success',
message: "Scheduled replacement event." });
$.get('rpc/cancel.jim?slot=' + sid, function() {
$epgpopup.dialog('close');
$('.ct_event[sid=' + sid + ']')
.attr('sclass', 'pending-unschedule')
.addClass('purpleshade strike');
$.unblockUI();
$.growl.error({ title: 'Success',
message: "Unscheduled original event." });
page_refresh();
});
});
}
else
{
$.blockUI({message: '<h1><img src=/img/loading.gif> ' +
'Re-scheduling series event... </h1>'});
$.get('/cgi-bin/epg/schedule.jim?type=1&service=' + xs + '&event=' + xe,
function() {
$.growl.notice({ title: 'Success',
message: "Scheduled replacement event." });
$.get('rpc/skip.jim?slot=' + sid +
'&service=' + oxs + '&event=' + oxe, function() {
$epgpopup.dialog('close');
$('.ct_event[sid=' + sid + ']')
.attr('sclass', 'pending-skip')
.addClass('purpleshade strike');
$.unblockUI();
$.growl.warning({ title: 'Success',
message: "Skipped unwanted episode." });
page_refresh();
});
});
}
//////////////////////////////////////////////////////////////////////
});
}
function menuclick(action, el, pos)
{
var sid = $epgpopup.attr('sid');
switch (action)
{
case 'refresh':
if (confirm('Are you sure you want to refresh events ' +
'for this entry?'))
$.get('rpc/refresh.jim?slot=' + sid,
function() {
$('.ct_event[sid=' + sid + ']')
.attr('sclass', 'pending-refresh')
.addClass('orangeshade');
$.growl.warning({ title: 'Success',
message: "The schedule entry will be " +
"refreshed." });
$epgpopup.dialog('close');
list_reload_required = true;
});
break;
}
}
function update_buttons(xs, xe)
{
var oxs = $epgpopup.attr('oxs');
var oxe = $epgpopup.attr('oxe');
var sid = $epgpopup.attr('sid');
var reckind = $epgpopup.attr('reckind');
var ocrid = $epgpopup.attr('crid');
var sclass = $epgpopup.attr('sclass');
var crid = $.trim($epgpopup.find('span.scrid').text());
if (sid == 0 || sclass != 'tbl_reservation')
{
$('.ep_button').hide();
return;
}
$('#b_more').show().contextMenu({
menu: 'optmenu',
leftButton: true,
position: {
my: "center top",
at: "center bottom+5",
of: $('#b_more'),
collision: "fit"
}
}, menuclick);
if (oxs == xs && oxe == xe)
{
// Viewing original event.
$('#b_alternate,#b_alternateseries').hide();
$('#b_cancel').show();
if (reckind == 4)
$('#b_skip').show();
else
$('#b_skip').hide();
}
else
{
// Viewing alternate event.
$('#b_alternate').show();
$('#b_cancel,#b_skip').hide();
if (reckind == 4 &&crid.toLowerCase() != ocrid.toLowerCase())
$('#b_alternateseries').show();
else
$('#b_alternateseries').hide();
}
}
function epgpopup(e, o, first)
{
if (e)
e.preventDefault();
var xs = o.attr('xs');
var xe = o.attr('xe');
if (!xs || xs == 0 || !xe || xe == 0)
return;
if (first)
$epgpopup.attr('oxs', xs).attr('oxe', xe);
var url = '/cgi-bin/epg/info.jim?bare&service=' + xs + '&event=' + xe +
'&bare=1';
$epgpopup
.empty().html('<img src="/img/loading.gif" alt="loading"> ' +
'Retrieving details...')
.load(url, function() {
update_buttons(xs, xe);
$epgpopup.find('a.event').on('click', function(e) {
epgpopup(e, $(this));
});
})
.attr('xs', xs)
.attr('xe', xe)
.dialog('open');
}
$('.ct_event, .ct_debug')
.hover(
function() { $(this).css('cursor', 'pointer'); },
function() { $(this).css('cursor', 'auto'); }
)
.qtip({
position: {
my: 'bottom center',
at: 'top center',
viewport: $(window)
},
style: {
classes: 'qtip-rounded qtip-shadow'
},
content: {
title: function() {
var reckind = $(this).attr('rc');
var rsvtype = $(this).attr('et');
var repeat = $(this).attr('rt');
var channel = $(this).attr('sz');
var period = $(this).attr('period');
var s = '';
schedule_icons(~~reckind, ~~rsvtype, ~~repeat)
.forEach(function(item, index) {
s += '<img class=visualicon '
+ 'src=/images/'
+ item + '.png>';
});
return "<span>" + s +
'<img class=visualicon ' +
'src="/img/channels/out/' +
channel + '.png"></span> ' +
"<span>" + channel +
' (' + period + ')</span>';
},
text: function() {
var t = '<center>' + $(this).html();
var s = $(this).attr('sclass');
x = visual_notes[s];
if (x)
t += '<div class=blood>' + x + '</div>';
else if ($(this).hasClass('strike') ||
$(this).hasClass('orangeshade'))
t += '<div class=blood>' +
'Changes pending</div>';
t += '</center>';
return t;
}
},
events: {
show: function(event, api) {
var sid = api.target.attr('sid');
$('.ct_event[sid=' + sid + ']')
.addClass('visual_hover');
},
hide: function(event, api) {
var sid = api.target.attr('sid');
$('.ct_event[sid=' + sid + ']')
.removeClass('visual_hover');
}
}
})
.on('click', function(e) {
var reckind = ~~$(this).attr('rc');
if (reckind == 4) // Series record
{
$('#b_skip').show();
$('#b_cancel .ui-button-text')
.text('Cancel Entire Series');
}
else
{
$('#b_skip').hide();
$('#b_cancel .ui-button-text')
.text('Cancel Recording');
}
$('.ep_button').hide();
$epgpopup.attr('reckind', reckind);
$epgpopup.attr('sid', $(this).attr('sid'));
$epgpopup.attr('crid', $(this).attr('crid'));
$epgpopup.attr('sclass', $(this).attr('sclass'));
epgpopup(e, $(this), true);
});
}

View File

@ -0,0 +1,115 @@
fieldset.visual
{
margin: 1em 0;
}
.ct_visual
{
xborder: 1px solid red;
min-width: 1100px;
}
.ct_header
{
padding: 2px 6px;
}
.ct_day
{
padding: 2px 5px;
border-width: 2px 1px 0 1px;
border-color: #999;
border-style: solid;
}
.ct_row
{
width: 100%;
height: 40px;
}
.ct_dayname
{
xborder: 1px solid blue;
width: 120px;
display: inline-block;
}
.ct_hour
{
display: inline-block;
overflow: hidden;
white-space: nowrap;
vertical-align: middle;
height: 25px;
padding: 4px 0 3px 3px;
margin: 3px 0 3px 0;
border-right: 1px solid #ccc;
}
.ct_skip, .ct_event, .ct_key
{
display: inline-block;
overflow: hidden;
white-space: nowrap;
vertical-align: middle;
height: 25px;
padding: 4px 0 3px 0;
margin: 3px 2px 3px 0;
border: 1px solid #ccc;
border-radius: 10px;
-moz-border-radius: 10px;
}
.ct_bskip
{
display: inline-block;
overflow: hidden;
white-space: nowrap;
vertical-align: middle;
height: 25px;
padding: 0;
margin: 0;
border: 0;
}
.visual_schedule
{
position: absolute;
width: 400px;
padding: 3px;
margin: 5px;
border: black solid 1px;
}
img.visualicon
{
height: 20px;
vertical-align: middle;
}
#optprogname
{
color: #999;
font-style: italic;
text-align: center;
border-bottom: 1px solid #999;
}
.qtip
{
max-width: 350px;
}
.qtip span
{
white-space: nowrap;
}
.visual_hover
{
background: #f6ff5b !important;
color: black;
}

View File

@ -4,8 +4,8 @@ package require cgi
source /mod/webif/lib/setup
require settings.class
jqplugin enadis
jscss xepg.js /css/xepg.css
jqplugin enadis qtip
jscss script.js style.css
header
require epg.class spinner.class altrow epg_search totop system.class
@ -46,8 +46,15 @@ if {$stt <= $now} {
set favgroup [$s channel_group]
set hours [expr 1.0 * [[settings] xepghours]]
set hours [cgi_get hours -]
if {$hours eq "-"} {
set hours [expr 1.0 * [[settings] xepghours]]
}
if {$hours == 0} { set hours 4.0 }
if {$hours < 1} { set hours 1.0 }
if {$hours > 8} { set hours 8.0 }
set seconds $($hours * 3600)
set ostt $stt
@ -83,49 +90,119 @@ set got [clock milliseconds]
proc but {label time {active 1}} {
puts -nonewline "<button style=\"margin-right: 2em\" tt=$time"
if {$active} {
puts -nonewline " class=nav"
puts -nonewline " class=\"nav delayshow\""
} else {
puts -nonewline " disabled class=\"nav ui-state-disabled\""
puts -nonewline " disabled class=\"nav delayshow ui-state-disabled\""
}
puts " active=$active>$label</button>"
}
puts "<!-- Hours $hours ($seconds) -->"
puts "<div id=buttons style=\"width: $eventpx; margin-left: ${chanpx}px;\">"
but "&lt&lt; -1 Week" $($stt - 3600 * 24 * 7) \
$($stt - $now + 3600 >= 3600 * 24 * 7)
but "&lt&lt; -1 Day" $($stt - 3600 * 24) $($stt - $now >= 3600 * 23)
but "&lt;&lt; Earlier" $($stt - 3600 * 4) $($stt > $now)
#but "&lt; Now &gt;" $now $($current == 0)
but "&lt; Now &gt;" $now
but "&gt;&gt; Later" $ett;
but "&gt;&gt; +1 Day" $($stt + 3600 * 24) $($stt < $now + 8 * 3600 * 24)
but "&gt;&gt; +1 Week" $($stt + 3600 * 24 * 7) $($stt < $now + 8 * 3600 * 24)
puts "<div id=timeline style=\"min-width: ${contwidth}px;\">"
puts "</div>"
######################################################################
# Time-jump bar
puts "
<script type=text/javascript>
\$('button.nav').button();
</script>
<div id=xepg style=\"min-width: ${contwidth}px; overflow: auto\">
<div id=xepgnow></div>
<div id=xegrid>
puts "<div class=timejump_outer>
<div class=timejump_label style=\"width: ${chanpx}px;\">
Jump to:&nbsp;
</div>
<div class=timejump_bar>
"
puts "<div id=epgdate style=\"width: ${eventpx}px; margin-left: ${chanpx}px;\">"
puts [clock format $stt -format {%a %d/%m/%Y}]
puts "<input type=hidden id=xepg_dp stt=$stt>"
set starthour [clock format $stt -format "%H"]
for {set i 0} {$i < 24} {incr i} {
if {$i < 13} { set disp $i } else { set disp $($i - 12) }
if {!$disp} { set disp 12 }
set xclass ""
if {$i == $starthour} {
append xclass " tjstart"
} elseif {$i > $starthour && $i < [expr $starthour + $hours]} {
append xclass " tjother"
}
set tt [expr $starthour + $stt + 3600 * ($i - $starthour)]
# Back to start of hour (no floor() in Humax Jim)
set tt $(3600 * round(1 + $tt / 3600))
if {$xclass ne "" || $i == 0 || $i == 12} {
append disp "<br>[clock format $tt -format %p]"
}
puts "<div tt=$tt class=\"timejump$xclass\">$disp</div>\n"
}
puts "</div>&nbsp;"
but "Now" $now
puts "</div>"
puts "<div class=xechan style=\"width: ${chanpx}px;\">&nbsp;</div>"
######################################################################
# Day-jump bar
puts "<div class=dayjump_outer>
<div class=dayjump_label style=\"width: ${chanpx}px;\"></div>
<div class=dayjump_bar>
"
set stt_time [clock format $stt -format "%H:%M"]
for {set i 0} {$i < 8} {incr i} {
set day $($now + $i * 86400)
if {!$i} {
set s "Today"
} else {
set s [clock format $day -format "%A"]
}
set xclass ""
if {[clock format $day -format "%j"] == \
[clock format $stt -format "%j"]} {
append xclass " dayjump_current"
}
set tt [clock scan \
[clock format $day -format "%Y:%m:%d:$stt_time"] \
-format "%Y:%m:%d:%H:%M"]
set alt [clock format $tt -format "%e %B"]
puts "<div tt=$tt title=\"$alt\" class=\"dayjump$xclass\">$s</div>\n"
}
puts "</div>"
puts "<span class=\"footnote delayshow\">&nbsp;
(Right-Click to select end hour)</span>"
puts "</div>"
######################################################################
# Time-line bar
# Displays the top border above the timeline.
puts "
<div id=epgdate style=\"width: ${eventpx}px; margin-left: ${chanpx}px;\">
</div>
<div class=xedate style=\"width: ${chanpx}px;\">
[clock format $stt -format "%A %e %B"]&nbsp;&nbsp;</div>
"
for {set i 0} {$i < $hours * 2} {incr i} {
puts "<div class=xetime id=xe$i
style=\"width: [expr $minpx * 30]px\">"
puts "[clock format $($stt + 1800 * $i) -format "%H:%M"]</div>"
}
# Close timeline div
puts "</div>"
######################################################################
# Events
puts "
<div id=xepgnow></div>
<div id=xepg style=\"min-width: ${contwidth}px;\">
<div id=xegrid>
"
set lim 0
set lcn 0
set bg "odd"
@ -218,6 +295,10 @@ puts "
Switch to standard now/next display.
</button></small>
</div>
<script type=text/javascript>
var hours = $hours;
var stt = [expr $stt + 0];
</script>
"
if {$current} {
@ -231,13 +312,14 @@ var kt = $('#xe0');
var ktpos = kt.position();
var height = $('#keypos').position().top - ktpos.top;
var top = ktpos.top - 25;
}
puts "var nowpos = ktpos.left + (kt.width() * $offset);"
puts {
$('#xepgnow')
.css('left', nowpos + 'px')
.css('top', ktpos.top)
.css('top', top)
.height(height)
.show('slow');
}

78
webif/html/xepg/script.js Normal file
View File

@ -0,0 +1,78 @@
$(function() {
$('#epgswitch').button().click(function() {
window.location = '/epg/list.jim';
});
$('button.nav').button().click(function() {
window.location = '/xepg/?stt=' + $(this).attr('tt') +
'&pos=' + $('#xegrid').scrollTop();
});
$('div.xeprog').qtip({
position: {
my: 'bottom center',
at: 'top center',
viewport: $(window)
},
style: {
classes: 'qtip-rounded qtip-shadow'
}
});
$('.timejump').hover(
function() {
$(this).css('cursor', 'pointer');
$(this).addClass('timejump_hover')
$(this).nextAll('.timejump').slice(0,hours-1)
.addClass('timejump_hover');
},
function() {
$(this).css('cursor', 'auto');
$(this).removeClass('timejump_hover')
$(this).nextAll('.timejump').slice(0,hours-1)
.removeClass('timejump_hover');
}
).on('click', function(e) {
e.preventDefault();
tt = $(this).attr('tt');
window.location = '/xepg/?stt=' + tt +
'&hours=' + hours +
'&pos=' + $('#xegrid').scrollTop();
}).on('contextmenu', function(e) {
e.preventDefault();
tt = $(this).attr('tt');
// Change the end hour.
nhours = Math.ceil((tt - stt) / 3600) + 1;
if (nhours > 1)
hours = nhours;
window.location = '/xepg/?stt=' + stt +
'&hours=' + hours +
'&pos=' + $('#xegrid').scrollTop();
});
$('.dayjump').hover(
function() {
$(this).css('cursor', 'pointer');
$(this).addClass('dayjump_hover')
},
function() {
$(this).css('cursor', 'auto');
$(this).removeClass('dayjump_hover')
}
).on('click', function(e) {
e.preventDefault();
window.location = '/xepg/?stt=' + $(this).attr('tt') +
'&hours=' + hours +
'&pos=' + $('#xegrid').scrollTop();
});
$('.delayshow').show();
});

View File

@ -58,6 +58,15 @@ div.xerow
{
}
div.xedate
{
float: left;
clear: left;
white-space: nowrap;
overflow: hidden;
text-align: right;
}
div.xechan
{
vertical-align: middle;
@ -116,3 +125,74 @@ button#later
margin-left: 0px !important;
}
.timejump_outer, .dayjump_outer
{
}
.timejump_bar, .dayjump_bar
{
padding: 4px;
background: #e6e6e6;
display: inline-block;
}
.dayjump_bar
{
padding-top: 0;
margin-bottom: 5px;
}
.timejump_label, .dayjump_label
{
text-align: right;
font-style: italic;
display: inline-block;
}
.timejump
{
display: inline-block;
width: 25px;
text-align: center;
vertical-align: middle;
line-height: 1em;
border-radius: 5px;
padding: 2px 0;
}
.dayjump
{
display: inline-block;
width: 85px;
text-align: center;
vertical-align: middle;
line-height: 1em;
padding: 2px 0 5px 0;
border-top: 1px solid #73e600;
}
.timejump_hover, .dayjump_hover
{
background: #ffffcc !important;
}
.dayjump_current
{
border-radius: 10px;
}
.tjstart, .dayjump_current
{
background: #73e600;
}
.tjother
{
background: #ccff99;
}
.delayshow
{
display: none;
}

View File

@ -813,7 +813,7 @@ if {[lindex $argv 0] eq "-singledir"} {
}
}
release_lock webif_auto
if {!$prelocked} { release_lock webif_auto }
log "Media scan completed in [elapsed $scanstart] seconds."

View File

@ -1,5 +1,5 @@
puts {
<div id=dialogue></div>
<div id=epgpopup_dialogue></div>
<script type=text/javascript src=/js/epg_popup.js></script>
}

View File

@ -17,9 +17,8 @@ if {![file exists /var/lib/humaxtv/rsvp.db]} {
$tdb close
}
$rsvdb query {attach database '/var/lib/humaxtv/rsvp.db' as pending}
# This is required to upgrade old tables and will be removed in a future
# version.
catch { $rsvdb query {alter table pending add column action int} }
# These are required to upgrade old tables.
catch { $rsvdb query { alter table pending add column action int} }
set binaryfields aulEventToRecordInfo
@ -54,6 +53,8 @@ class rsv {
usLcn 0
sort 0
action 0
_table ""
_origstart 0
}
require findhsvc
@ -156,6 +157,10 @@ rsv method start {} {
return $nsttime
}
rsv method end {} {
return $([$self start] + $nduration)
}
rsv method icon {} {
set rsvicon ""
switch $ersvtype {
@ -224,6 +229,10 @@ rsv method iconset {{height 20}} {
return $iconlist
}
rsv method setorigstart {o} {
set _origstart $o
}
rsv method set_delete {} {
set action 1
}
@ -319,7 +328,10 @@ rsv method insert {{table pending} {force 0}} {
if {$ulslot < 0} { set ulslot 0 }
}
set fields [lsort [$self vars]]
set fields [lsort [lmap i [$self vars] {
if {[string index $i 0] eq "_"} continue
concat "" $i
}]]
foreach field {szSvcName usLcn sort} {
set df [lsearch $fields $field]
@ -379,6 +391,7 @@ proc {rsv list} {{table tbl_reservation} {extra ""}} {
set res [$::rsvdb query $qstring]
set records {}
foreach rec $res {
lappend rec _table $table
lappend records [rsv new $rec]
}
@ -872,31 +885,214 @@ proc {rsv restore} {file} {
close $fd
}
# Check for scheduled events at the same time
proc {rsv conflicts} {s d} {
global rsvdb
set query "
select count(*) as num
from TBL_RESERVATION
where ersvtype <= 3
and (
(
nsttime >= $s
and nsttime < $s + $d
) or (
nsttime + nduration > $s
and nsttime + nduration <= $s + $d
) or (
nsttime <= $s
and nsttime + nduration >= $s + $d
))
"
set ret [$rsvdb query $query]
# Returns an array of expanded events from the schedule.
# Array keys:
# 0: start
# 1: end
# 2: hSvc
# 3: event_id
# 4: Schedule ID (sid)
# 5: ucRecKind
# 6: class (live, pending)
set num 0
if {[llength $ret] > 0} {
lassign [lindex $ret 0] x num
proc {rsv allevents} {{xota 0}} {
set events [rsv list]
set pending [rsv list pending]
if {[llength $pending]} {
lappend events {*}$pending
}
return $num
set xevents {}
foreach e $events {
set seen 0
if {$xota && [$e get szevtname] eq "Disable OTA"} continue
if {[$e get action] ne "0"} continue
set trailer [list \
[$e get ulslot] \
[$e get ucRecKind] \
[$e get _table] \
]
# Expand the events encoded in the AUL data.
foreach a [$e aul] {
lassign $a service start end event_id
if {$start == [$e start] && \
$end == [$e end]} {
incr seen
}
lappend xevents [list $start $end $service $event_id \
{*}$trailer]
}
if {$seen} continue
set start [$e start]
set end [$e end]
lappend xevents [list $start $end \
[$e get hsvc] [$e get usevtid] {*}$trailer]
set repeat [$e get erepeat]
switch $repeat {
1 -
3 -
4 {
# 1 Daily
# 3 Weekends
# 4 Weekdays
for {set i 0} {$i < 8} {incr i} {
incr start 86400
incr end 86400
# Sun == 0
set day [clock format [$e start] \
-format "%w"]
# Weekdays Only
if {$repeat == 3 && ($day == 0 || $day == 6)} {
continue
}
# Weekends Only
if {$repeat == 4 && $day != 0 && $day != 6} {
continue
}
lappend xevents [list \
$start $end \
[$e get hsvc] [$e get usevtid] \
{*}$trailer]
}
}
2 {
# Weekly
lappend xevents [list \
$($start + 7 * 86400) \
$($end + 7 * 86400) \
[$e get hsvc] [$e get usevtid] \
{*}$trailer]
}
}
}
return $xevents
}
proc {rsv evaluate_conflicts} {events type thresh {debug 0}} {
set conflicts {}
set slots {0 0}
foreach ev $events {
lassign $ev start end hsvc eid sid
if {$debug} {
puts "\nSLOTS: $slots"
puts $ev
}
# Close off any open slots that have now finished.
for {set i 0} {$i < 2} {incr i} {
set v [lindex $slots $i]
if {$v eq "0"} {
if {$debug} { puts "\[$i] empty." }
continue
}
lassign $v xsid xend
if {$start >= $xend} {
lset slots $i 0
if {$debug} { puts "\[$i] finished $xend." }
} else {
if {$debug} { puts "\[$i] running $xend." }
}
}
# Find slot for recording
if {[lindex $slots 0] eq "0"} {
set uslot 0
} elseif {$thresh > 1 && [lindex $slots 1] eq "0"} {
set uslot 1
} else {
# Conflict detected
if {$debug} { puts " Conflict." }
if {$type eq "xlist"} {
set c "$sid$end"
for {set i 0} {$i < 2} {incr i} {
set v [lindex $slots $i]
if {$v eq "0"} continue
lassign $v xsid xend
lappend c "$xsid$xend"
}
} else {
set c "$sid"
for {set i 0} {$i < 2} {incr i} {
set v [lindex $slots $i]
if {$v eq "0"} continue
lassign $v xsid xend
lappend c "$xsid"
}
}
foreach x $c {
if {![dict exists $conflicts $x]} {
set conflicts($x) $c
}
}
# If this events ends later than the
# existing one in slot 0, then replace that
# with this one.
lassign [lindex $slots 0] xsid xend
if {$xend >= $end} continue
if {$debug} { puts " Replacing slot 0.\n" }
set uslot 0
}
if {$debug} {
puts " -> into slot $uslot"
}
# Insert event into slot
lset slots $uslot [list $sid $end]
}
return $conflicts
}
proc {rsv newconflicts} {{thresh 1} {type "list"} {debug 0}} {
set events [lsort -index 0 -integer [rsv allevents]]
set conflicts [rsv evaluate_conflicts $events $type $thresh $debug]
if {$type eq "map"} { return $conflicts }
return [dict keys $conflicts]
}
proc {rsv checkconflict} {s d thresh {debug 0}} {
set events [rsv allevents]
lappend events [list $s $($s + $d) 0 0 0]
set events [lsort -index 0 -integer $events]
set conflicts [rsv evaluate_conflicts $events 'list' $thresh $debug]
set ret {}
if {![dict exists $conflicts "0"]} { return $ret }
foreach c [dict get $conflicts 0] {
if {$c eq "0"} continue
set s [rsv slot $c]
set s [$s name]
lappend ret $s
}
return $ret
}

View File

@ -54,9 +54,10 @@ if {![exists -proc require]} {
incr ::mws::headerdone
}
proc noheader {} {
proc noheader {{type "text/html"} {cache 0}} {
httpheader $type $cache
uplevel source /mod/webif/html/lib/noheader.jim
incr ::mws::headerdone
::mws::emit_jscss
}
proc footer {} {

View File

@ -12,3 +12,7 @@ proc lremove {var val} {
set v [lsearch -all -inline -not -exact $v $val]
}
proc ladd {l} {
expr [join $l +] + 0
}