summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDieter Hametner <dh (plus) vdr (at) gekrumbel (dot) de>2007-07-12 19:10:34 +0000
committerDieter Hametner <dh (plus) vdr (at) gekrumbel (dot) de>2007-07-12 19:10:34 +0000
commit7b003f8aaafc2d95dcf7c9dfc5cbc6288b37915c (patch)
tree35ba447699c1fd1c1f41dd672fcc1e127d6ea3cc
parent9f65a960ca7d4cc3819e1434de05b9428acc23ad (diff)
downloadvdr-plugin-live-7b003f8aaafc2d95dcf7c9dfc5cbc6288b37915c.tar.gz
vdr-plugin-live-7b003f8aaafc2d95dcf7c9dfc5cbc6288b37915c.tar.bz2
- Update to the mootools framework.
- New more XHTML compliant tips. - Optional AJAX enabled infoboxes for epg information. - Major speed enhancement for the single pages, due to less data to transfer to the browser. - See doc/ChangeLog for more detailed changes description. - See doc/dev-conventions.txt for how we benefit from mootools package on the ECMAScript side of live.
-rw-r--r--css/styles.css343
-rw-r--r--doc/ChangeLog21
-rw-r--r--doc/dev-conventions.txt34
-rw-r--r--epg_events.cpp293
-rw-r--r--epg_events.h176
-rw-r--r--javascript/Makefile3
-rw-r--r--live/img/close_red.pngbin0 -> 587 bytes
-rw-r--r--live/img/info-win-b-l.pngbin0 -> 844 bytes
-rw-r--r--live/img/info-win-b-r.pngbin0 -> 466 bytes
-rw-r--r--live/img/info-win-m-l.pngbin0 -> 180 bytes
-rw-r--r--live/img/info-win-m-r.pngbin0 -> 176 bytes
-rw-r--r--live/img/info-win-t-l.pngbin0 -> 1131 bytes
-rw-r--r--live/img/info-win-t-r.pngbin0 -> 716 bytes
-rw-r--r--live/img/tip-hint-bl.pngbin0 -> 528 bytes
-rw-r--r--live/img/tip-hint-br.pngbin0 -> 336 bytes
-rw-r--r--live/img/tip-hint-ml.pngbin0 -> 110 bytes
-rw-r--r--live/img/tip-hint-mr.pngbin0 -> 112 bytes
-rw-r--r--live/img/tip-hint-tl.pngbin0 -> 470 bytes
-rw-r--r--live/img/tip-hint-tr.pngbin0 -> 346 bytes
-rw-r--r--live/js/live/hinttips.js34
-rw-r--r--live/js/live/infowin.js296
-rw-r--r--live/js/mootools/mootools.v1.11.js1101
-rw-r--r--pages/Makefile3
-rw-r--r--pages/channels_widget.ecpp3
-rw-r--r--pages/edit_searchtimer.ecpp1
-rw-r--r--pages/edit_timer.ecpp1
-rw-r--r--pages/epginfo.ecpp114
-rw-r--r--pages/ibox.ecpp97
-rw-r--r--pages/login.ecpp1
-rw-r--r--pages/menu.ecpp21
-rw-r--r--pages/pageelems.ecpp18
-rw-r--r--pages/recordings.ecpp37
-rw-r--r--pages/remote.ecpp1
-rw-r--r--pages/schedule.ecpp41
-rw-r--r--pages/searchepg.ecpp1
-rw-r--r--pages/searchresults.ecpp21
-rw-r--r--pages/searchtimers.ecpp1
-rw-r--r--pages/setup.ecpp1
-rw-r--r--pages/timers.ecpp1
-rw-r--r--pages/tooltip.ecpp41
-rw-r--r--pages/whats_on.ecpp38
-rw-r--r--recordings.cpp111
-rw-r--r--recordings.h31
-rw-r--r--tntconfig.cpp1
44 files changed, 2270 insertions, 616 deletions
diff --git a/css/styles.css b/css/styles.css
index 69e0fa7..0e56603 100644
--- a/css/styles.css
+++ b/css/styles.css
@@ -89,68 +89,147 @@ img {
######################
*/
-div.domTThint {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- border: 1px solid #EBC94C;
- background-color: #F4FFC3;
- max-width: 35em;
+.hint-tip {
+ margin: 0px auto;
+ max-width: 480px; /* depends on the tip backround image width */
+ color: #fff;
}
-div.domTThint .caption {
- font-weight: bold;
+.hint-tip .hint-tip-top .hint-tip-c,
+.hint-tip .hint-tip-bot .hint-tip-c {
+ font-size: 1px; /* ensure minimum height */
+ height: 17px;
+}
+
+.hint-tip .hint-tip-top {
+ background: transparent url(img/tip-hint-tl.png) no-repeat 0px 0px;
+ margin-right: 17px; /* space for right corner */
+}
+
+.hint-tip .hint-tip-top .hint-tip-c {
+ background: transparent url(img/tip-hint-tr.png) no-repeat right 0px;
+ margin-right: -17px; /* pull right corner back over "empty" space (from above margin) */
+}
+
+.hint-tip .hint-tip-bdy {
+ background: transparent url(img/tip-hint-ml.png) repeat-y 0px 0px;
+ margin-right: 17px;
+}
+
+.hint-tip .hint-tip-bdy .hint-tip-c {
+ background: transparent url(img/tip-hint-mr.png) repeat-y right 0px;
+ margin-right: -17px;
}
-div.domTThint .contents {
- padding: 2px;
+.hint-tip .hint-tip-bdy .hint-tip-c .hint-tip-s { /* optional gradient overlay */
+ /* background: transparent url(img/tip-hint-ms.jpg) repeat-x 0px 0px; */
+ padding: 0px 17px 0px 17px;
}
+.hint-tip .hint-tip-bot {
+ background: transparent url(img/tip-hint-bl.png) no-repeat 0px 0px;
+ margin-right: 17px;
+}
+
+.hint-tip .hint-tip-bot .hint-tip-c {
+ background: transparent url(img/tip-hint-br.png) no-repeat right 0px;
+ margin-right: -17px;
+}
+
+.hint-title {
+ display: none;
+}
+
+
/* ##############################
- # Tooltip style for epg infos
+ # Infowin styles for epg infos
##############################
*/
-div.domTTepg {
- width: 66%;
+div.info-win {
+ width: 560px;
+ max-width: 2048px;
border: none;
+ margin: 0px auto;
}
-.domTTepg div.epg_description {
+.info-win .info-win-top .info-win-c {
+ /*font-size: 1px;*/ /* ensure minimum height */
+ height: 37px;
}
-.domTTepg div.epg_content {
- padding: 0;
- margin: 0;
+.info-win .info-win-bot .info-win-c {
+ font-size: 1px; /* ensure minimum height */
+ height: 21px;
+}
- border-left: 1px solid #000000;
- border-right: 1px solid #000000;
- border-bottom: 1px solid #000000;
- background: white url(bg_tools.png) top left repeat-y;
+.info-win .info-win-top {
+ background: transparent url(img/info-win-t-l.png) no-repeat 0px 0px;
+ margin-right: 26px; /* space for right corner */
+}
+
+.info-win .info-win-top .info-win-c {
+ background: transparent url(img/info-win-t-r.png) no-repeat right 0px;
+ margin-right: -26px; /* pull right corner back over "empty" space (from above margin) */
+ overflow: hidden;
}
-.domTTepg div.epg_content div.epg_tools {
+.info-win .info-win-top .info-win-c .info-win-t {
+ color: #FFF;
+ font-weight: bold;
+ margin-top: 14px;
+ margin-left: 15px;
float: left;
- width: 26px;
- margin: 0;
- padding: 0;
+}
- text-align: center;
- vertical-align: top;
+.info-win .info-win-top .info-win-c .info-win-b {
+ margin-top: 17px;
+ margin-right: 26px;
+ float: right;
}
-.domTTepg div.epg_content div div.progress div {
- padding-left: 0px;
+.info-win .info-win-top .info-win-c .info-win-b .close {
+ width: 16px;
+ height: 16px;
+ background: transparent url(close.png) no-repeat top right;
}
-.domTTepg div.epg_content div div {
- padding-left: 35px;
+.info-win .info-win-top .info-win-c .info-win-b .close:hover {
+ width: 16px;
+ height: 16px;
+ background: transparent url(img/close_red.png) no-repeat top right;
}
-.domTTepg div.epg_content div.epg_tools img {
- margin-top: 5px;
+.info-win .info-win-body {
+ background: transparent url(img/info-win-m-l.png) repeat-y 0px 0px;
+ margin-right: 26px;
+}
+
+.info-win .info-win-body .info-win-c {
+ background: transparent url(img/info-win-m-r.png) repeat-y right 0px;
+ margin-right: -26px;
+}
+
+.info-win .info-win-body .info-win-c .info-win-s {
+ padding: 0px 26px 0px 14px;
+}
+
+.info-win .info-win-bot {
+ background: transparent url(img/info-win-b-l.png) no-repeat 0px 0px;
+ margin-right: 26px;
+}
+
+.info-win .info-win-bot .info-win-c {
+ background: transparent url(img/info-win-b-r.png) no-repeat right 0px;
+ margin-right: -26px;
}
-.domTTepg div.boxheader div div a {
+.info-win .description {
+ margin-bottom: 0px;
+}
+
+.hint-title {
+ display: none;
}
/* #######################
@@ -556,7 +635,6 @@ div.__progress div.__elapsed {
background-color: #E9EFFF;
}
-
/* ##################################
# table listing
# (this is used in listing tables)
@@ -616,99 +694,6 @@ table.listing a:hover {
text-decoration: underline;
}
-
-/* #############
- # Timers
- #############
-*/
-/* NOT USED
-table.timers {
- padding: 0;
- margin: 0;
- margin-top: 10px;
-}
-
-table.timers tr td {
- padding: 3px 7px 3px 3px;
- background: url(bg_line.png) bottom repeat-x;
- border-bottom: 1px solid #C0C1DA;
-}
-
-table.timers td.border {
- padding: 0;
- margin: 0;
- width: 1px;
-}
-
-table.timers tr.head td {
- color: white;
- font-weight: bold;
- margin: 0;
- padding: 0;
- border-bottom: 1px solid black;
-}
-
-table.timers tr.description td {
- font-weight: bold;
- background: #E9EFFF;
-}
-
-table.timers a {
- text-decoration: none;
- color: black;
- font-weight: bold;
-}
-
-table.timers a:hover {
- text-decoration: underline;
-}
-*/
-
-/*
- ##############################
- # Schedule
- ##############################
-*/
-
-/* NOT USED:
-table.schedule {
- margin: 10px 0 0 0 ;
- padding: 0;
-}
-
-table.schedule tr td.head {
- background: #6D96A9;
- color: white;
- font-weight: bold;
- margin: 0;
- padding: 3px;
-}
-
-table.schedule tr td {
- vertical-align: top;
- padding: 3px 7px 3px 3px;
- background: url(bg_line.png) bottom repeat-x;
- border-bottom: 1px solid #C0C1DA;
-}
-
-table.schedule tr td.day {
- vertical-align: top;
- padding: 0;
- margin: 0;
- border:none;
-}
-
-table.schedule tr.active {
- background: #DEE6EE;
-}
-
-table.schedule div.more {
- margin: 0px;
- font-weight: bold;
- cursor: pointer;
-}
-*/
-
/*
##############################
# Blue Background Thingy
@@ -853,14 +838,12 @@ table.error td.border {
width: 1px;
}
-
/*
##############################
# Formular Tables
# (are used in forms to group input elements)
##############################
*/
-/* 'formular' replaces 'edit' */
table.formular {
margin-top: 10px;
@@ -913,77 +896,57 @@ table.dependent tr td {
vertical-align: middle;
}
-
/*
##############################
- # Search results
+ # Login
##############################
*/
-/* NOT USED:
-table.searchresults {
- margin: 10px 0 0 0;
- padding: 0;
+table.login {
+ margin: 0 auto;
}
-table.searchresults tr td.head {
- background: #6D96A9;
- color: white;
- font-weight: bold;
- margin: 0;
- padding: 3px;
+table.login tr td {
+ padding: 3px 5px;
+ text-align: right;
}
-table.searchresults tr td {
- vertical-align: top;
- padding: 3px 7px 7px 3px;
- background: url(bg_line.png) bottom repeat-x;
- border-bottom: 1px solid #C0C1DA;
-}
+/*
+ ##############################
+ # Infowin support styles for EPG-Boxes
+ ##############################
+*/
-table.searchresults tr td.day {
- vertical-align: top;
+.info-win div.epg_content {
padding: 0;
margin: 0;
- border:none;
+ background: transparent url(bg_tools.png) top left repeat-y;
}
-table.searchresults td.border {
- padding: 0;
+.info-win div.epg_content div.epg_tools {
+ float: left;
+ width: 26px;
margin: 0;
- width: 1px;
-}
+ padding: 0;
-table.searchresults div.more {
- margin: 0px;
- font-weight: bold;
- cursor: pointer;
+ text-align: center;
+ vertical-align: top;
}
-table.searchresults a {
- text-decoration: none;
- color: black;
- font-weight: bold;
+.info-win div.epg_content div div.progress div {
+ padding-left: 0px;
}
-table.searchresults a:hover {
- text-decoration: underline;
+.info-win div.epg_content div div {
+ padding-left: 35px;
}
-*/
-/*
- ##############################
- # Login
- ##############################
-*/
-
-table.login {
- margin: 0 auto;
+.info-win div.epg_content div.epg_tools img {
+ margin-top: 5px;
}
-table.login tr td {
- padding: 3px 5px;
- text-align: right;
+.info-win div.boxheader {
+ display: none;
}
/* ##############################
@@ -1005,14 +968,7 @@ div.about_box a:hover {
text-decoration: underline;
}
-div#aboutBox_tip {
- width: 45%;
-}
-
-.about_box div.about_description {
-}
-
-.about_box div.about_content {
+.info-win div.about_content {
padding: 0;
margin: 0;
@@ -1021,29 +977,26 @@ div#aboutBox_tip {
border-bottom: 1px solid #000000;
}
-.about_box div.about_content div {
+.info-win div.about_content div {
background-color: white;
padding-bottom: 6px;
}
-.about_box div.boxheader div div a {
-}
-
-.about_box div.about_content div.about_left {
+.info-win div.about_content div.about_left {
text-align: right;
float: left;
width: 175px;
}
-.about_box div.about_content div.about_right {
+.info-win div.about_content div.about_right {
padding-left: 200px;
}
-.about_box div.about_content div.about_line {
+.info-win div.about_content div.about_line {
padding-left: 10px;
}
-.about_box div.about_content div.about_head {
+.info-win div.about_content div.about_head {
font-weight: bold;
margin-top: 0px;
padding-top: 6px;
@@ -1051,12 +1004,12 @@ div#aboutBox_tip {
background: #FFFFFF url(bg_line.png) top repeat-x;
}
-.about_box div.about_content div.about_head div {
+.info-win div.about_content div.about_head div {
padding-bottom: 6px;
background: #FFFFFF url(bg_line_top.png) bottom repeat-x;
}
-.about_box div.about_content div.about_head div div {
+.info-win div.about_content div.about_head div div {
padding: 2px 0px 2px 10px;
background: #E9EFFF;
border-top: 1px solid #C0C1DA;
diff --git a/doc/ChangeLog b/doc/ChangeLog
index bd5cb7b..1eb4787 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,3 +1,24 @@
+2007-07-12 Dieter Hametner <dh+vdr at gekrumbel dot de>
+
+ Changed the javascript base of live. We now use the 'mootools'
+ framework (see http://www.mootools.net for infos) to handle
+ javascript in a browser independend fashion and for nifty Web 2.0
+ features.
+
+ Based on this framework we have now tooltips that use the XHTML
+ standard 'title' attribute and Web-2.0 popup windows for epg
+ information. This Epg information is loaded on demand and once
+ loaded, they are cached in the page for further viewing.
+
+ On the other hand this also provides us with a solution to have
+ live functioning without javascript at all. When done right, the
+ same functionality can be achieved with or without enabled
+ javascript in the browser. Currently there still are javascript
+ only features, which will be resolved in the next weeks.
+
+ This is a rather big change on many files, so they are not all
+ mentioned here.
+
2007-06-22 Dieter Hametner <dh+vdr at gekrumbel dot de>
Start of new 'standalone' javascript source directory
diff --git a/doc/dev-conventions.txt b/doc/dev-conventions.txt
index 07d026d..2b80eab 100644
--- a/doc/dev-conventions.txt
+++ b/doc/dev-conventions.txt
@@ -18,16 +18,30 @@ Here WEB 2.0 features can improve the users experience.
With or without ECMAScript
--------------------------
-Since not all browsers support ECMAScript, we suggest the following
-rule to activate functionality with and without ECMAScript support:
-
-Use anchors analog to this example
-
- <a href="show_content.html?ref=additional_content_ref"
- onclick="makefalse(requestbox('additional_content_ref'));">link</a>
-
-to retrieve and display extra information either in a WEB 2.0 fashion
-in an popup box or on a separte page in a html fashion.
+Since not all browsers support ECMAScript, we need to make sure all
+functions live wants to provide need to be accessible through links.
+
+With the mootools framework and its selection functions we can enhance
+the user experience through ECMAScript by selecting the relevant
+elements in the DOM and attaching event handlers from the loaded
+script files. Thus when the user disables ECMAScript in his browser
+(or the browser does not support it) the traditional web technique of
+jumping between pages provides the functions. With enabled ECMAScript
+the event handlers can take over and provide a nifty Web 2.0 technique
+solution to the user.
+
+To enable a tooltip just add a 'title' attribute on the element and
+load 'hinttips.js' in your pages (Actually this will be allready done
+for you if you use the live page-framework).
+
+For popup windows that asynchronously load its contents you need to
+use normal links like <a href="epginfo.html?eventid=evnt_identifier">
+your link text here </a>. If 'infowin.js' is loaded it will enhance
+these links with AJAX functionality. If not the link will change to a
+new page with the requested information.
+
+This means that both users with and without ECMAScript support will
+benefit from the functions in live.
Themeing
diff --git a/epg_events.cpp b/epg_events.cpp
index 2d5237c..e9524b3 100644
--- a/epg_events.cpp
+++ b/epg_events.cpp
@@ -5,54 +5,67 @@
#include "epg_events.h"
+using namespace std;
+
namespace vdrlive
{
- EpgEvent::EpgEvent(const std::string& id,
- const std::string& caption,
- const std::string& title,
- const std::string& short_descr,
- const std::string& long_descr,
- time_t start_time,
- time_t end_time) :
+
+ /*
+ * -------------------------------------------------------------------------
+ * EpgInfo
+ * -------------------------------------------------------------------------
+ */
+
+ EpgInfo::EpgInfo(const std::string& id, const std::string& caption) :
m_eventId(id),
- m_caption(caption),
- m_title(title),
- m_short_descr(short_descr),
- m_long_descr(long_descr),
- m_archived(),
- m_start_time(start_time),
- m_end_time(end_time)
+ m_caption(caption)
+ {
+ }
+
+ EpgInfo::~EpgInfo()
+ {
+ }
+
+ const std::string EpgInfo::CurrentTime(const char* format) const
+ {
+ return FormatDateTime(format, time(0));
+ }
+
+ const string EpgInfo::StartTime(const char* format) const
{
+ time_t start = GetStartTime();
+ return start ? FormatDateTime(format, start) : "";
}
+ const string EpgInfo::EndTime(const char* format) const
+ {
+ time_t end = GetEndTime();
+ return end ? FormatDateTime(format, end) : "";
+ }
+
+ int EpgInfo::Elapsed() const
+ {
+ time_t end_time = GetEndTime();
+ time_t start_time = GetStartTime();
+
+ if (end_time > start_time) {
+ time_t now = time(0);
+ if ((start_time <= now) && (now <= end_time)) {
+ return 100 * (now - start_time) / (end_time - start_time);
+ }
+ }
+ return -1;
+ }
+
+ /*
+ * -------------------------------------------------------------------------
+ * EpgEvent
+ * -------------------------------------------------------------------------
+ */
+
EpgEvent::EpgEvent(const std::string& id, const cEvent* event, const char* channelName) :
- m_eventId(id),
- m_caption(channelName),
- m_title(event->Title() ? event->Title() : ""),
- m_short_descr(event->ShortText() ? event->ShortText() : ""),
- m_long_descr(event->Description() ? event->Description() : ""),
- m_archived(),
- m_start_time(event->StartTime()),
- m_end_time(event->EndTime())
- {
- }
-
- EpgEvent::EpgEvent(const std::string& id,
- const std::string& caption,
- const std::string& title,
- const std::string& short_descr,
- const std::string& long_descr,
- const std::string& archived,
- time_t start_time,
- time_t end_time) :
- m_eventId(id),
- m_caption(caption),
- m_title(title),
- m_short_descr(short_descr),
- m_long_descr(long_descr),
- m_archived(archived),
- m_start_time(start_time),
- m_end_time(end_time)
+ EpgInfo(id, channelName),
+ m_event(event)
{
}
@@ -60,50 +73,206 @@ namespace vdrlive
{
}
- const std::string EpgEvent::StartTime(const char* format) const
+ /*
+ * -------------------------------------------------------------------------
+ * EpgString
+ * -------------------------------------------------------------------------
+ */
+
+ EpgString::EpgString(const string& id, const string& caption, const string& info) :
+ EpgInfo(id, caption),
+ m_info(info)
{
- return FormatDateTime(format, m_start_time);
}
- const std::string EpgEvent::EndTime(const char* format) const
+ EpgString::~EpgString()
{
- return FormatDateTime(format, m_end_time);
}
- const std::string EpgEvent::CurrentTime(const char* format) const
+ const string EpgString::Title() const
{
- return FormatDateTime(format, time(0));
+ return m_info;
}
- int EpgEvent::Elapsed() const
+ const string EpgString::ShortDescr() const
{
- if (m_end_time > m_start_time) {
- time_t now = time(0);
- if ((m_start_time <= now) && (now <= m_end_time)) {
- return 100 * (now - m_start_time) / (m_end_time - m_start_time);
+ return "";
+ }
+
+ const string EpgString::LongDescr() const
+ {
+ return "";
+ }
+
+ // virtual const std::string Archived() const { return std::string(); }
+
+ time_t EpgString::GetStartTime() const
+ {
+ return time(0);
+ }
+
+ time_t EpgString::GetEndTime() const
+ {
+ return time(0);
+ }
+
+ /*
+ * -------------------------------------------------------------------------
+ * EpgRecording
+ * -------------------------------------------------------------------------
+ */
+
+ EpgRecording::EpgRecording(const string& recid, const cRecording* recording, const char* caption) :
+ EpgInfo(recid, (caption != 0) ? caption : ""),
+ m_recording(recording),
+ m_ownCaption(caption != 0),
+ m_checkedArchived(false),
+ m_archived()
+ {
+ }
+
+ EpgRecording::~EpgRecording()
+ {
+ m_recording = 0;
+ }
+
+ const string EpgRecording::Caption() const
+ {
+ if (!m_ownCaption) {
+ return EpgInfo::Caption();
+ }
+ if (!m_recording) {
+ return "";
+ }
+
+ return Name();
+ }
+
+ const string EpgRecording::Title() const
+ {
+ if (!m_recording) {
+ return "";
+ }
+
+ const cRecordingInfo* info = m_recording->Info();
+ return (info && info->Title()) ? info->Title() : Name();
+ }
+
+ const string EpgRecording::ShortDescr() const
+ {
+ const cRecordingInfo* info = m_recording ? m_recording->Info() : 0;
+ return (info && info->ShortText()) ? info->ShortText() : "";
+ }
+
+ const string EpgRecording::LongDescr() const
+ {
+ const cRecordingInfo* info = m_recording ? m_recording->Info() : 0;
+ return (info && info->Description()) ? info->Description() : "";
+ }
+
+ const string EpgRecording::Archived() const
+ {
+ if (!m_checkedArchived) {
+ if (m_recording) {
+ m_archived = RecordingsManager::GetArchiveDescr(m_recording);
+ m_checkedArchived = true;
}
}
- return -1;
+ return m_archived;
+ }
+
+ time_t EpgRecording::GetStartTime() const
+ {
+ return m_recording ? m_recording->start : 0;
}
- const cTimer* EpgEvent::GetTimer() const
+ time_t EpgRecording::GetEndTime() const
{
- return NULL;
+ return m_recording ? m_recording->start : 0;
}
- EpgEvents::EpgEvents() :
- std::vector<EpgEventPtr>()
+ const string EpgRecording::Name() const
+ {
+ string name(m_recording->Name());
+ size_t index = name.find_last_of('~');
+ if (index != string::npos) {
+ name = name.substr(index, name.length());
+ }
+ return name;
+ }
+
+ /*
+ * -------------------------------------------------------------------------
+ * EpgEvents
+ * -------------------------------------------------------------------------
+ */
+
+ EpgEvents::EpgEvents()
{
}
EpgEvents::~EpgEvents()
{
}
-#ifdef never
- EpgEventsPtr EpgEvents::dim(size_t count)
+
+ string EpgEvents::GetDomId(const tChannelID& chanId, const tEventID& eId)
+ {
+ string channelId(chanId.ToString());
+ string eventId("event_");
+
+ replace(channelId.begin(), channelId.end(), '.', 'p');
+ replace(channelId.begin(), channelId.end(), '-', 'm');
+
+ eventId += channelId;
+ eventId += '_';
+ eventId += lexical_cast<std::string>(eId);
+ return eventId;
+ }
+
+ EpgInfoPtr EpgEvents::CreateEpgInfo(const std::string& epgid, const cSchedules* schedules)
+ {
+ const string eventStr("event_");
+
+ size_t delimPos = epgid.find_last_of('_');
+ string cIdStr = epgid.substr(eventStr.length(), delimPos - eventStr.length());
+
+ replace(cIdStr.begin(), cIdStr.end(), 'm', '-');
+ replace(cIdStr.begin(), cIdStr.end(), 'p', '.');
+
+ const string eIdStr = epgid.substr(delimPos+1);
+ const string errorInfo(tr("Epg error"));
+
+
+ tEventID eventId = lexical_cast<tEventID>(eIdStr);
+ tChannelID channelId = tChannelID::FromString(cIdStr.c_str());
+ const cChannel* channel = Channels.GetByChannelID(channelId);
+ if (!channel) {
+ return CreateEpgInfo(epgid, errorInfo, tr("Wrong channel id"));
+ }
+ const cSchedule* schedule = schedules->GetSchedule(channel);
+ if (!schedule) {
+ return CreateEpgInfo(epgid, errorInfo, tr("Channel has no schedule"));
+ }
+ const cEvent* event = schedule->GetEvent(eventId);
+ if (!event) {
+ return CreateEpgInfo(epgid, errorInfo, tr("wrong event id"));
+ }
+ return CreateEpgInfo(channel, event, epgid.c_str());
+ }
+
+ EpgInfoPtr EpgEvents::CreateEpgInfo(const cChannel* chan, const cEvent* event, const char* idOverride)
+ {
+ string domId(idOverride ? idOverride : GetDomId(chan->GetChannelID(), event->EventID()));
+ return EpgInfoPtr(new EpgEvent(domId, event, chan->Name()));
+ }
+
+ EpgInfoPtr EpgEvents::CreateEpgInfo(const string& recid, const cRecording* recording, const char* caption)
+ {
+ return EpgInfoPtr(new EpgRecording(recid, recording, caption));
+ }
+
+ EpgInfoPtr EpgEvents::CreateEpgInfo(const std::string& id, const std::string& caption, const std::string& info)
{
- EpgEventsPtr ePtr(new EpgEvents(count));
- return ePtr;
+ return EpgInfoPtr(new EpgString(id, caption, info));
}
-#endif
}; // namespace vdrlive
diff --git a/epg_events.h b/epg_events.h
index 68eb0a4..8536463 100644
--- a/epg_events.h
+++ b/epg_events.h
@@ -2,7 +2,6 @@
#define VDR_LIVE_WHATS_ON_H
#include <ctime>
-#include <vector>
#include <vdr/plugin.h>
#include <vdr/channels.h>
@@ -15,72 +14,171 @@
namespace vdrlive
{
- class EpgEvent
+
+ class EpgInfo
{
+ protected:
+ EpgInfo(const std::string& id,
+ const std::string& caption);
+
public:
- EpgEvent(const std::string& id,
- const std::string& caption,
- const std::string& title,
- const std::string& short_descr,
- const std::string& long_descr,
- time_t start_time,
- time_t end_time);
+ virtual ~EpgInfo();
+
+ virtual const std::string Id() const { return m_eventId; }
+
+ virtual const std::string Caption() const { return m_caption; }
+
+ virtual const std::string Title() const = 0;
+
+ virtual const std::string ShortDescr() const = 0;
+
+ virtual const std::string LongDescr() const = 0;
+
+ virtual const std::string Archived() const { return ""; }
+
+ virtual const std::string StartTime(const char* format) const;
+
+ virtual const std::string EndTime(const char* format) const;
+
+ virtual const std::string CurrentTime(const char* format) const;
+
+ virtual int Elapsed() const;
+
+ // virtual const cTimer* GetTimer() const = 0;
+
+ virtual time_t GetStartTime() const = 0;
+
+ virtual time_t GetEndTime() const = 0;
+
+ private:
+ std::string m_eventId;
+ std::string m_caption;
+ };
+
+ typedef std::tr1::shared_ptr<EpgInfo> EpgInfoPtr;
+
+ // -------------------------------------------------------------------------
+
+ class EpgString : public EpgInfo
+ {
+ friend class EpgEvents;
+
+ protected:
+ EpgString(const std::string& id,
+ const std::string& caption,
+ const std::string& info);
+
+ public:
+ virtual ~EpgString();
+ virtual const std::string Title() const;
+
+ virtual const std::string ShortDescr() const;
+
+ virtual const std::string LongDescr() const;
+
+ virtual time_t GetStartTime() const;
+
+ virtual time_t GetEndTime() const;
+
+ private:
+ const std::string m_info;
+ };
+
+ // -------------------------------------------------------------------------
+
+ class EpgEvent : public EpgInfo
+ {
+ friend class EpgEvents;
+
+ protected:
EpgEvent(const std::string& id,
const cEvent* event,
const char* channelName = "");
- EpgEvent(const std::string& id,
- const std::string& caption,
- const std::string& title,
- const std::string& short_descr,
- const std::string& long_descr,
- const std::string& archived,
- time_t start_time,
- time_t end_time);
-
+ public:
virtual ~EpgEvent();
- const std::string& Id() const { return m_eventId; }
+ virtual const std::string Title() const { return std::string(m_event->Title() ? m_event->Title() : ""); }
- const std::string& Title() const { return m_title; }
+ virtual const std::string ShortDescr() const { return std::string(m_event->ShortText() ? m_event->ShortText() : ""); }
- const std::string& Caption() const { return m_caption; }
+ virtual const std::string LongDescr() const { return std::string(m_event->Description() ? m_event->Description() : ""); }
- const std::string& ShortDescr() const { return m_short_descr; }
+ virtual time_t GetStartTime() const { return m_event->StartTime(); }
- const std::string& LongDescr() const { return m_long_descr; }
+ virtual time_t GetEndTime() const { return m_event->EndTime(); }
- const std::string& Archived() const { return m_archived; }
+ private:
+ const cEvent* m_event;
+ };
- const std::string StartTime(const char* format) const;
+ // -------------------------------------------------------------------------
+
+ class EpgRecording : public EpgInfo
+ {
+ friend class EpgEvents;
- const std::string EndTime(const char* format) const;
+ protected:
+ EpgRecording(const std::string& recid, const cRecording* recording, const char* caption);
+
+ const std::string Name() const;
+
+ public:
+ virtual ~EpgRecording();
- const std::string CurrentTime(const char* format) const;
+ virtual const std::string Caption() const;
- int Elapsed() const;
+ virtual const std::string Title() const;
- const cTimer* GetTimer() const;
+ virtual const std::string ShortDescr() const;
+
+ virtual const std::string LongDescr() const;
+
+ virtual const std::string Archived() const;
+
+ virtual time_t GetStartTime() const;
+
+ virtual time_t GetEndTime() const;
private:
- std::string m_eventId;
- std::string m_caption;
- std::string m_title;
- std::string m_short_descr;
- std::string m_long_descr;
- std::string m_archived;
- time_t m_start_time;
- time_t m_end_time;
+ const cRecording* m_recording;
+ bool m_ownCaption;
+ mutable bool m_checkedArchived;
+ mutable std::string m_archived;
};
- typedef std::tr1::shared_ptr<EpgEvent> EpgEventPtr;
+ // -------------------------------------------------------------------------
- class EpgEvents : public std::vector<EpgEventPtr> {
+ class EpgEvents {
public:
EpgEvents();
virtual ~EpgEvents();
+ static std::string GetDomId(const tChannelID& chanId, const tEventID& eventId);
+
+ /**
+ * Allocate and initalize an epgEvent instance with the
+ * passed channel and event information.
+ */
+ static EpgInfoPtr CreateEpgInfo(const cChannel* chan, const cEvent* event, const char* idOverride = 0);
+
+ /**
+ * This is the inverse creator for epgInfos to the creator above.
+ */
+ static EpgInfoPtr CreateEpgInfo(const std::string& epgid, const cSchedules* schedules);
+
+ /**
+ * Allocate and initalize an epgEvent instance with the
+ * passed recording information.
+ */
+ static EpgInfoPtr CreateEpgInfo(const std::string& recid, const cRecording* recording, const char* caption = 0);
+
+ /**
+ * Allocate and initalize an epgEvent instance with the
+ * passed string informations
+ */
+ static EpgInfoPtr CreateEpgInfo(const std::string& id, const std::string& caption, const std::string& info);
private:
};
}; // namespace vdrlive
diff --git a/javascript/Makefile b/javascript/Makefile
index 648361c..c86afe2 100644
--- a/javascript/Makefile
+++ b/javascript/Makefile
@@ -15,8 +15,7 @@ VDRDIR ?= ../../../..
### The object files (add further files here):
-OBJS = alphaAPI.o domLib.o domTT_drag.o domTT.o fadomatic.o \
- treeview.o
+OBJS = treeview.o
### Default rules:
diff --git a/live/img/close_red.png b/live/img/close_red.png
new file mode 100644
index 0000000..05e02b2
--- /dev/null
+++ b/live/img/close_red.png
Binary files differ
diff --git a/live/img/info-win-b-l.png b/live/img/info-win-b-l.png
new file mode 100644
index 0000000..5fccc68
--- /dev/null
+++ b/live/img/info-win-b-l.png
Binary files differ
diff --git a/live/img/info-win-b-r.png b/live/img/info-win-b-r.png
new file mode 100644
index 0000000..29a09e9
--- /dev/null
+++ b/live/img/info-win-b-r.png
Binary files differ
diff --git a/live/img/info-win-m-l.png b/live/img/info-win-m-l.png
new file mode 100644
index 0000000..9fb5356
--- /dev/null
+++ b/live/img/info-win-m-l.png
Binary files differ
diff --git a/live/img/info-win-m-r.png b/live/img/info-win-m-r.png
new file mode 100644
index 0000000..f097081
--- /dev/null
+++ b/live/img/info-win-m-r.png
Binary files differ
diff --git a/live/img/info-win-t-l.png b/live/img/info-win-t-l.png
new file mode 100644
index 0000000..f916543
--- /dev/null
+++ b/live/img/info-win-t-l.png
Binary files differ
diff --git a/live/img/info-win-t-r.png b/live/img/info-win-t-r.png
new file mode 100644
index 0000000..854bde2
--- /dev/null
+++ b/live/img/info-win-t-r.png
Binary files differ
diff --git a/live/img/tip-hint-bl.png b/live/img/tip-hint-bl.png
new file mode 100644
index 0000000..790a602
--- /dev/null
+++ b/live/img/tip-hint-bl.png
Binary files differ
diff --git a/live/img/tip-hint-br.png b/live/img/tip-hint-br.png
new file mode 100644
index 0000000..61eafcf
--- /dev/null
+++ b/live/img/tip-hint-br.png
Binary files differ
diff --git a/live/img/tip-hint-ml.png b/live/img/tip-hint-ml.png
new file mode 100644
index 0000000..b730b57
--- /dev/null
+++ b/live/img/tip-hint-ml.png
Binary files differ
diff --git a/live/img/tip-hint-mr.png b/live/img/tip-hint-mr.png
new file mode 100644
index 0000000..bf2465c
--- /dev/null
+++ b/live/img/tip-hint-mr.png
Binary files differ
diff --git a/live/img/tip-hint-tl.png b/live/img/tip-hint-tl.png
new file mode 100644
index 0000000..467ecec
--- /dev/null
+++ b/live/img/tip-hint-tl.png
Binary files differ
diff --git a/live/img/tip-hint-tr.png b/live/img/tip-hint-tr.png
new file mode 100644
index 0000000..a4c9b3f
--- /dev/null
+++ b/live/img/tip-hint-tr.png
Binary files differ
diff --git a/live/js/live/hinttips.js b/live/js/live/hinttips.js
new file mode 100644
index 0000000..5cf899a
--- /dev/null
+++ b/live/js/live/hinttips.js
@@ -0,0 +1,34 @@
+/*
+ * Extension of mootools Tips class for rounded corner
+ * tooltips of variable size up to some maximum.
+ */
+
+var HintTips = Tips.extend({
+ initialize: function(elements, options){
+ this.parent(elements, options);
+ this.toolTip.empty();
+ /* top border of tip */
+ var hd = new Element('div', {'class': this.options.className + '-tip-top'}).inject(this.toolTip);
+ hd = new Element('div', {'class': this.options.className + '-tip-c'}).inject(hd);
+
+ /* body of tip: some helper divs and content */
+ this.wrapper = new Element('div', {'class': this.options.className + '-tip-bdy'}).inject(this.toolTip);
+ this.wrapper = new Element('div', {'class': this.options.className + '-tip-c'}).inject(this.wrapper);
+ this.wrapper = new Element('div', {'class': this.options.className + '-tip-s'}).inject(this.wrapper);
+
+ /* bottom border of tip */
+ var bt = new Element('div', {'class': this.options.className + '-tip-bot'}).inject(this.toolTip);
+ bt = new Element('div', {'class': this.options.className + '-tip-c'}).inject(bt);
+ }
+ });
+
+window.addEvent('domready', function(){
+ var tips = new HintTips($$('*[title]'), {
+ maxTitleChars: 100,
+ className: 'hint'
+ });
+ });
+
+window.addEvent('mousedown', function(){
+ $$('.hint-tip').setStyle('visibility', 'hidden');
+ }); \ No newline at end of file
diff --git a/live/js/live/infowin.js b/live/js/live/infowin.js
new file mode 100644
index 0000000..c142700
--- /dev/null
+++ b/live/js/live/infowin.js
@@ -0,0 +1,296 @@
+/*
+ * Extension of mootools to display a popup window with
+ * some html code.
+ */
+
+/*
+Class: InfoWin
+ Create an information window as overlay to current page.
+
+Arguments:
+
+Options:
+
+Note:
+ A window consists of a frame-element. This is the overall
+ containing element used to control the display and size of the
+ window. It is accesable through the 'winFrame' property.
+
+ The InfoWin class provides the followin properties to fill the
+ window with content:
+ - titleBox: the element meant to place the title of the window into.
+ - buttonBox: here the default window buttons are created. You might
+ clear this and create your own kind of window controls.
+ - winBody: this is where your window contents goes.
+ */
+var InfoWin = new Class({
+ options: {
+ timeout: 0,
+ onShow: Class.empty,
+ onHide: Class.empty,
+ className: 'info',
+ wm: false, // overide default window manager.
+ draggable: true,
+ resizable: true,
+ buttonimg: 'transparent.png',
+ bodyselect: 'div.epg_content',
+ titleselect: 'div.caption',
+ offsets: {'x': -16, 'y': -16}
+ },
+
+ initialize: function(id, options){
+ this.setOptions(options);
+ this.wm = this.options.wm || InfoWin.$wm;
+ this.winFrame = $(id + '-win-id');
+ if (!$defined(this.winFrame)){
+ this.build(id);
+ this.wm.register(this);
+ }
+ },
+
+ // internal: build new window element.
+ //
+ // build sets up a frame for a new InfoWin. The parent element
+ // of the window frame has the id '<id>-win-id'. The function
+ // must return true if the body of the InfoWin has been filled
+ // with the user data, false otherwise.
+ build: function(id){
+ this.winFrame = new Element('div', {
+ 'id': id + '-win-id',
+ 'class': this.options.className + '-win',
+ 'styles': {
+ 'position': 'absolute',
+ 'top': '0',
+ 'left': '0'
+ }
+ });
+
+ // header of window: upper shadows, corners title and controls
+ var top = new Element('div', {
+ 'class': this.options.className + '-win-top'
+ }).inject(this.winFrame);
+ if (this.options.draggable) this.winFrame.makeDraggable({'handle': top});
+ top = new Element('div', {
+ 'class': this.options.className + '-win-c'
+ }).inject(top);
+ this.titleBox = new Element('div', {
+ 'class': this.options.className + '-win-t'
+ }).inject(top);
+
+ this.buttonBox = new Element('div', {
+ 'class': this.options.className + '-win-b'
+ }).inject(top);
+ var cls = new Element('div', {
+ 'class': 'close'
+ }).inject(this.buttonBox);
+ var self = this;
+ cls.addEvent('click', function(event){
+ var event = new Event(event);
+ event.stop();
+ return self.hide();
+ });
+ cls = new Element('img', {
+ 'src': this.options.buttonimg,
+ 'alt': 'close',
+ 'width': '16px',
+ 'height': '16px'
+ }).inject(cls);
+
+ // body of window: user content.
+ var bdy = new Element('div', {
+ 'class': this.options.className + '-win-body'
+ }).inject(this.winFrame);
+ bdy = new Element('div', {
+ 'class': this.options.className + '-win-c'
+ }).inject(bdy);
+ this.winBody = new Element('div', {
+ 'class': this.options.className + '-win-s'
+ }).inject(bdy);
+
+ // bottom border of window: lower shadows and corners, optional
+ // resize handle.
+ var bot = new Element('div', {
+ 'class': this.options.className + '-win-bot'
+ }).inject(this.winFrame);
+ bot = new Element('div', {
+ 'class': this.options.className + '-win-c'
+ }).inject(bot);
+
+ if (this.options.resizable) {
+ this.winFrame.makeResizable({'handle': bot});
+ }
+
+ if (!this.fillTitle(id)) {
+ // todo: add generic title
+ }
+ return this.fillBody(id);
+ },
+
+ show: function(event){
+ if (this.options.timeout)
+ this.timer = this.hide.delay(this.options.timeout, this);
+ this.position(event);
+ this.fireEvent('onShow', [this.winFrame]);
+ this.wm.raise(this);
+ return false;
+ },
+
+ hide: function(){
+ this.fireEvent('onHide', [this.winFrame]);
+ this.wm.bury(this);
+ return false;
+ },
+
+ fillBody: function(id){
+ var bodyElems = $$('#'+ id + ' ' + this.options.bodyselect);
+ if ($defined(bodyElems) && bodyElems.length > 0) {
+ this.winBody.empty().adopt(bodyElems);
+ return true;
+ }
+ return false;
+ },
+
+ fillTitle: function(id){
+ var titleElems = $$('#' + id + ' ' + this.options.titleselect);
+ if ($defined(titleElems) && titleElems.length > 0) {
+ this.titleBox.empty().adopt(titleElems);
+ return true;
+ }
+ return false;
+ },
+
+ position: function(event){
+ var prop = {'x': 'left', 'y': 'top'};
+ for (var z in prop) {
+ var pos = event.page[z] + this.options.offsets[z];
+ this.winFrame.setStyle(prop[z], pos);
+ }
+ }
+ });
+
+InfoWin.implement(new Events, new Options);
+
+InfoWin.Manager = new Class({
+ options: {
+ zIndex: 100,
+ closedContainer: 'infowin-closed',
+ openedContainer: 'infowin-opened',
+ onRegister: Class.empty,
+ onRaise: Class.empty,
+ onBury: Class.empty
+ },
+
+ initialize: function(options){
+ this.setOptions(options);
+ // initialize properties this.closedWins and this.openedWins:
+ ['closed', 'opened'].each(function(kind){
+ var wins = kind + 'Wins';
+ var opts = this.options[kind + 'Container'];
+ this[wins] = $(opts);
+ if (!$defined(this[wins])){
+ this[wins] = new Element('div', {
+ 'id': opts,
+ 'styles' : {
+ 'display' : (kind == 'closed') ? 'none' : 'block'
+ }
+ });
+ this[wins].inject(document.body);
+ }
+ }, this);
+ },
+
+ register: function(infoWin){
+ var self = this;
+ this.fireEvent('onRegister', [infoWin]);
+ infoWin.winFrame.addEvent('click', function(){
+ self.raise(infoWin);
+ });
+ infoWin.winFrame.inject(this.closedWins);
+ },
+
+ raise: function(infoWin){
+ this.fireEvent('onRaise', [infoWin]);
+ infoWin.winFrame.remove();
+ infoWin.winFrame.inject(this.openedWins);
+ },
+
+ bury: function(infoWin){
+ this.fireEvent('onBury', [infoWin]);
+ infoWin.winFrame.remove();
+ infoWin.winFrame.inject(this.closedWins);
+ }
+ });
+
+InfoWin.Manager.implement(new Events, new Options);
+
+InfoWin.$wm = null;
+window.addEvent('domready', function(){
+ InfoWin.$wm = new InfoWin.Manager();
+ });
+
+
+InfoWin.Ajax = InfoWin.extend({
+ options: {
+ loadingMsg: 'loading',
+ errorMsg: 'an error occured!',
+ onError: Class.empty
+ },
+
+ initialize: function(id, url, options){
+ this.parent(id, options);
+ if ($defined(this.ajaxResponse)) {
+ var self = this;
+ this.addEvent('onError', function(){
+ self.hide.delay(1000, self);
+ });
+ var ajax = new Ajax(url, {
+ update: this.ajaxResponse,
+ onComplete: function(text, xmldoc){
+ self.fillTitle(id);
+ self.fillBody(id);
+ },
+ onFailure: function(transport){
+ self.titleBox.setHTML(self.options.errorMsg);
+ self.fireEvent('onError', [id, url]);
+ }
+ }).request('async=1');
+ }
+ },
+
+ // this function gets called when no previous instance for 'id'
+ // created a dom subtree for an infowin.
+ build: function(id){
+ if (!this.parent(id)) {
+ this.titleBox.setHTML(this.options.loadingMsg);
+ this.ajaxResponse = new Element('div', {
+ 'styles' : {
+ 'display': 'none'
+ }
+ }).inject(this.winFrame);
+ }
+ }
+ });
+
+InfoWin.Ajax.implement(new Events, new Options);
+
+
+window.addEvent('domready', function(){
+ $$('a[href^="epginfo.html?epgid"]').each(function(el){
+ var href = el.href;
+ var epgid = $pick(href, "");
+ if (epgid != "") {
+ var extractId = /epgid=(\w+)/;
+ var found = extractId.exec(epgid);
+ if ($defined(found) && found.length > 1) {
+ epgid = found[1];
+ el.addEvent('click', function(event){
+ var event = new Event(event);
+ new InfoWin.Ajax(epgid, href).show(event);
+ event.stop();
+ return false;
+ });
+ }
+ }
+ });
+ });
+
diff --git a/live/js/mootools/mootools.v1.11.js b/live/js/mootools/mootools.v1.11.js
index 9c8be88..ac754b9 100644
--- a/live/js/mootools/mootools.v1.11.js
+++ b/live/js/mootools/mootools.v1.11.js
@@ -3017,6 +3017,325 @@ Function.extend({
/*
+Script: Element.Filters.js
+ add Filters capability to <Elements>.
+
+License:
+ MIT-style license.
+*/
+
+/*
+Class: Elements
+ A collection of methods to be used with <$$> elements collections.
+*/
+
+Elements.extend({
+
+ /*
+ Property: filterByTag
+ Filters the collection by a specified tag name.
+ Returns a new Elements collection, while the original remains untouched.
+ */
+
+ filterByTag: function(tag){
+ return new Elements(this.filter(function(el){
+ return (Element.getTag(el) == tag);
+ }));
+ },
+
+ /*
+ Property: filterByClass
+ Filters the collection by a specified class name.
+ Returns a new Elements collection, while the original remains untouched.
+ */
+
+ filterByClass: function(className, nocash){
+ var elements = this.filter(function(el){
+ return (el.className && el.className.contains(className, ' '));
+ });
+ return (nocash) ? elements : new Elements(elements);
+ },
+
+ /*
+ Property: filterById
+ Filters the collection by a specified ID.
+ Returns a new Elements collection, while the original remains untouched.
+ */
+
+ filterById: function(id, nocash){
+ var elements = this.filter(function(el){
+ return (el.id == id);
+ });
+ return (nocash) ? elements : new Elements(elements);
+ },
+
+ /*
+ Property: filterByAttribute
+ Filters the collection by a specified attribute.
+ Returns a new Elements collection, while the original remains untouched.
+
+ Arguments:
+ name - the attribute name.
+ operator - optional, the attribute operator.
+ value - optional, the attribute value, only valid if the operator is specified.
+ */
+
+ filterByAttribute: function(name, operator, value, nocash){
+ var elements = this.filter(function(el){
+ var current = Element.getProperty(el, name);
+ if (!current) return false;
+ if (!operator) return true;
+ switch(operator){
+ case '=': return (current == value);
+ case '*=': return (current.contains(value));
+ case '^=': return (current.substr(0, value.length) == value);
+ case '$=': return (current.substr(current.length - value.length) == value);
+ case '!=': return (current != value);
+ case '~=': return current.contains(value, ' ');
+ }
+ return false;
+ });
+ return (nocash) ? elements : new Elements(elements);
+ }
+
+});
+
+/*
+Script: Element.Selectors.js
+ Css Query related functions and <Element> extensions
+
+License:
+ MIT-style license.
+*/
+
+/* Section: Utility Functions */
+
+/*
+Function: $E
+ Selects a single (i.e. the first found) Element based on the selector passed in and an optional filter element.
+ Returns as <Element>.
+
+Arguments:
+ selector - string; the css selector to match
+ filter - optional; a DOM element to limit the scope of the selector match; defaults to document.
+
+Example:
+ >$E('a', 'myElement') //find the first anchor tag inside the DOM element with id 'myElement'
+
+Returns:
+ a DOM element - the first element that matches the selector
+*/
+
+function $E(selector, filter){
+ return ($(filter) || document).getElement(selector);
+};
+
+/*
+Function: $ES
+ Returns a collection of Elements that match the selector passed in limited to the scope of the optional filter.
+ See Also: <Element.getElements> for an alternate syntax.
+ Returns as <Elements>.
+
+Returns:
+ an array of dom elements that match the selector within the filter
+
+Arguments:
+ selector - string; css selector to match
+ filter - optional; a DOM element to limit the scope of the selector match; defaults to document.
+
+Examples:
+ >$ES("a") //gets all the anchor tags; synonymous with $$("a")
+ >$ES('a','myElement') //get all the anchor tags within $('myElement')
+*/
+
+function $ES(selector, filter){
+ return ($(filter) || document).getElementsBySelector(selector);
+};
+
+$$.shared = {
+
+ 'regexp': /^(\w*|\*)(?:#([\w-]+)|\.([\w-]+))?(?:\[(\w+)(?:([!*^$]?=)["']?([^"'\]]*)["']?)?])?$/,
+
+ 'xpath': {
+
+ getParam: function(items, context, param, i){
+ var temp = [context.namespaceURI ? 'xhtml:' : '', param[1]];
+ if (param[2]) temp.push('[@id="', param[2], '"]');
+ if (param[3]) temp.push('[contains(concat(" ", @class, " "), " ', param[3], ' ")]');
+ if (param[4]){
+ if (param[5] && param[6]){
+ switch(param[5]){
+ case '*=': temp.push('[contains(@', param[4], ', "', param[6], '")]'); break;
+ case '^=': temp.push('[starts-with(@', param[4], ', "', param[6], '")]'); break;
+ case '$=': temp.push('[substring(@', param[4], ', string-length(@', param[4], ') - ', param[6].length, ' + 1) = "', param[6], '"]'); break;
+ case '=': temp.push('[@', param[4], '="', param[6], '"]'); break;
+ case '!=': temp.push('[@', param[4], '!="', param[6], '"]');
+ }
+ } else {
+ temp.push('[@', param[4], ']');
+ }
+ }
+ items.push(temp.join(''));
+ return items;
+ },
+
+ getItems: function(items, context, nocash){
+ var elements = [];
+ var xpath = document.evaluate('.//' + items.join('//'), context, $$.shared.resolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (var i = 0, j = xpath.snapshotLength; i < j; i++) elements.push(xpath.snapshotItem(i));
+ return (nocash) ? elements : new Elements(elements.map($));
+ }
+
+ },
+
+ 'normal': {
+
+ getParam: function(items, context, param, i){
+ if (i == 0){
+ if (param[2]){
+ var el = context.getElementById(param[2]);
+ if (!el || ((param[1] != '*') && (Element.getTag(el) != param[1]))) return false;
+ items = [el];
+ } else {
+ items = $A(context.getElementsByTagName(param[1]));
+ }
+ } else {
+ items = $$.shared.getElementsByTagName(items, param[1]);
+ if (param[2]) items = Elements.filterById(items, param[2], true);
+ }
+ if (param[3]) items = Elements.filterByClass(items, param[3], true);
+ if (param[4]) items = Elements.filterByAttribute(items, param[4], param[5], param[6], true);
+ return items;
+ },
+
+ getItems: function(items, context, nocash){
+ return (nocash) ? items : $$.unique(items);
+ }
+
+ },
+
+ resolver: function(prefix){
+ return (prefix == 'xhtml') ? 'http://www.w3.org/1999/xhtml' : false;
+ },
+
+ getElementsByTagName: function(context, tagName){
+ var found = [];
+ for (var i = 0, j = context.length; i < j; i++) found.extend(context[i].getElementsByTagName(tagName));
+ return found;
+ }
+
+};
+
+$$.shared.method = (window.xpath) ? 'xpath' : 'normal';
+
+/*
+Class: Element
+ Custom class to allow all of its methods to be used with any DOM element via the dollar function <$>.
+*/
+
+Element.Methods.Dom = {
+
+ /*
+ Property: getElements
+ Gets all the elements within an element that match the given (single) selector.
+ Returns as <Elements>.
+
+ Arguments:
+ selector - string; the css selector to match
+
+ Examples:
+ >$('myElement').getElements('a'); // get all anchors within myElement
+ >$('myElement').getElements('input[name=dialog]') //get all input tags with name 'dialog'
+ >$('myElement').getElements('input[name$=log]') //get all input tags with names ending with 'log'
+
+ Notes:
+ Supports these operators in attribute selectors:
+
+ - = : is equal to
+ - ^= : starts-with
+ - $= : ends-with
+ - != : is not equal to
+
+ Xpath is used automatically for compliant browsers.
+ */
+
+ getElements: function(selector, nocash){
+ var items = [];
+ selector = selector.trim().split(' ');
+ for (var i = 0, j = selector.length; i < j; i++){
+ var sel = selector[i];
+ var param = sel.match($$.shared.regexp);
+ if (!param) break;
+ param[1] = param[1] || '*';
+ var temp = $$.shared[$$.shared.method].getParam(items, this, param, i);
+ if (!temp) break;
+ items = temp;
+ }
+ return $$.shared[$$.shared.method].getItems(items, this, nocash);
+ },
+
+ /*
+ Property: getElement
+ Same as <Element.getElements>, but returns only the first. Alternate syntax for <$E>, where filter is the Element.
+ Returns as <Element>.
+
+ Arguments:
+ selector - string; css selector
+ */
+
+ getElement: function(selector){
+ return $(this.getElements(selector, true)[0] || false);
+ },
+
+ /*
+ Property: getElementsBySelector
+ Same as <Element.getElements>, but allows for comma separated selectors, as in css. Alternate syntax for <$$>, where filter is the Element.
+ Returns as <Elements>.
+
+ Arguments:
+ selector - string; css selector
+ */
+
+ getElementsBySelector: function(selector, nocash){
+ var elements = [];
+ selector = selector.split(',');
+ for (var i = 0, j = selector.length; i < j; i++) elements = elements.concat(this.getElements(selector[i], true));
+ return (nocash) ? elements : $$.unique(elements);
+ }
+
+};
+
+Element.extend({
+
+ /*
+ Property: getElementById
+ Targets an element with the specified id found inside the Element. Does not overwrite document.getElementById.
+
+ Arguments:
+ id - string; the id of the element to find.
+ */
+
+ getElementById: function(id){
+ var el = document.getElementById(id);
+ if (!el) return false;
+ for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
+ if (!parent) return false;
+ }
+ return el;
+ }/*compatibility*/,
+
+ getElementsByClassName: function(className){
+ return this.getElements('.' + className);
+ }
+
+ /*end compatibility*/
+
+});
+
+document.extend(Element.Methods.Dom);
+Element.extend(Element.Methods.Dom);
+
+/*
Script: Element.Form.js
Contains Element prototypes to deal with Forms and their elements.
@@ -3092,6 +3411,161 @@ Element.extend({
});
/*
+Script: Element.Dimensions.js
+ Contains Element prototypes to deal with Element size and position in space.
+
+Note:
+ The functions in this script require n XHTML doctype.
+
+License:
+ MIT-style license.
+*/
+
+/*
+Class: Element
+ Custom class to allow all of its methods to be used with any DOM element via the dollar function <$>.
+*/
+
+Element.extend({
+
+ /*
+ Property: scrollTo
+ Scrolls the element to the specified coordinated (if the element has an overflow)
+
+ Arguments:
+ x - the x coordinate
+ y - the y coordinate
+
+ Example:
+ >$('myElement').scrollTo(0, 100)
+ */
+
+ scrollTo: function(x, y){
+ this.scrollLeft = x;
+ this.scrollTop = y;
+ },
+
+ /*
+ Property: getSize
+ Return an Object representing the size/scroll values of the element.
+
+ Example:
+ (start code)
+ $('myElement').getSize();
+ (end)
+
+ Returns:
+ (start code)
+ {
+ 'scroll': {'x': 100, 'y': 100},
+ 'size': {'x': 200, 'y': 400},
+ 'scrollSize': {'x': 300, 'y': 500}
+ }
+ (end)
+ */
+
+ getSize: function(){
+ return {
+ 'scroll': {'x': this.scrollLeft, 'y': this.scrollTop},
+ 'size': {'x': this.offsetWidth, 'y': this.offsetHeight},
+ 'scrollSize': {'x': this.scrollWidth, 'y': this.scrollHeight}
+ };
+ },
+
+ /*
+ Property: getPosition
+ Returns the real offsets of the element.
+
+ Arguments:
+ overflown - optional, an array of nested scrolling containers for scroll offset calculation, use this if your element is inside any element containing scrollbars
+
+ Example:
+ >$('element').getPosition();
+
+ Returns:
+ >{x: 100, y:500};
+ */
+
+ getPosition: function(overflown){
+ overflown = overflown || [];
+ var el = this, left = 0, top = 0;
+ do {
+ left += el.offsetLeft || 0;
+ top += el.offsetTop || 0;
+ el = el.offsetParent;
+ } while (el);
+ overflown.each(function(element){
+ left -= element.scrollLeft || 0;
+ top -= element.scrollTop || 0;
+ });
+ return {'x': left, 'y': top};
+ },
+
+ /*
+ Property: getTop
+ Returns the distance from the top of the window to the Element.
+
+ Arguments:
+ overflown - optional, an array of nested scrolling containers, see Element::getPosition
+ */
+
+ getTop: function(overflown){
+ return this.getPosition(overflown).y;
+ },
+
+ /*
+ Property: getLeft
+ Returns the distance from the left of the window to the Element.
+
+ Arguments:
+ overflown - optional, an array of nested scrolling containers, see Element::getPosition
+ */
+
+ getLeft: function(overflown){
+ return this.getPosition(overflown).x;
+ },
+
+ /*
+ Property: getCoordinates
+ Returns an object with width, height, left, right, top, and bottom, representing the values of the Element
+
+ Arguments:
+ overflown - optional, an array of nested scrolling containers, see Element::getPosition
+
+ Example:
+ (start code)
+ var myValues = $('myElement').getCoordinates();
+ (end)
+
+ Returns:
+ (start code)
+ {
+ width: 200,
+ height: 300,
+ left: 100,
+ top: 50,
+ right: 300,
+ bottom: 350
+ }
+ (end)
+ */
+
+ getCoordinates: function(overflown){
+ var position = this.getPosition(overflown);
+ var obj = {
+ 'width': this.offsetWidth,
+ 'height': this.offsetHeight,
+ 'left': position.x,
+ 'top': position.y
+ };
+ obj.right = obj.left + obj.width;
+ obj.bottom = obj.top + obj.height;
+ return obj;
+ }
+
+});
+
+/*
Script: Window.DomReady.js
Contains the custom event domready, for window.
@@ -3272,6 +3746,633 @@ window.extend({
});
/*
+Script: Fx.Base.js
+ Contains <Fx.Base>, the foundamentals of the MooTools Effects.
+
+License:
+ MIT-style license.
+*/
+
+var Fx = {};
+
+/*
+Class: Fx.Base
+ Base class for the Effects.
+
+Options:
+ transition - the equation to use for the effect see <Fx.Transitions>; default is <Fx.Transitions.Sine.easeInOut>
+ duration - the duration of the effect in ms; 500 is the default.
+ unit - the unit is 'px' by default (other values include things like 'em' for fonts or '%').
+ wait - boolean: to wait or not to wait for a current transition to end before running another of the same instance. defaults to true.
+ fps - the frames per second for the transition; default is 50
+
+Events:
+ onStart - the function to execute as the effect begins; nothing (<Class.empty>) by default.
+ onComplete - the function to execute after the effect has processed; nothing (<Class.empty>) by default.
+ onCancel - the function to execute when you manually stop the effect.
+*/
+
+Fx.Base = new Class({
+
+ options: {
+ onStart: Class.empty,
+ onComplete: Class.empty,
+ onCancel: Class.empty,
+ transition: function(p){
+ return -(Math.cos(Math.PI * p) - 1) / 2;
+ },
+ duration: 500,
+ unit: 'px',
+ wait: true,
+ fps: 50
+ },
+
+ initialize: function(options){
+ this.element = this.element || null;
+ this.setOptions(options);
+ if (this.options.initialize) this.options.initialize.call(this);
+ },
+
+ step: function(){
+ var time = $time();
+ if (time < this.time + this.options.duration){
+ this.delta = this.options.transition((time - this.time) / this.options.duration);
+ this.setNow();
+ this.increase();
+ } else {
+ this.stop(true);
+ this.set(this.to);
+ this.fireEvent('onComplete', this.element, 10);
+ this.callChain();
+ }
+ },
+
+ /*
+ Property: set
+ Immediately sets the value with no transition.
+
+ Arguments:
+ to - the point to jump to
+
+ Example:
+ >var myFx = new Fx.Style('myElement', 'opacity').set(0); //will make it immediately transparent
+ */
+
+ set: function(to){
+ this.now = to;
+ this.increase();
+ return this;
+ },
+
+ setNow: function(){
+ this.now = this.compute(this.from, this.to);
+ },
+
+ compute: function(from, to){
+ return (to - from) * this.delta + from;
+ },
+
+ /*
+ Property: start
+ Executes an effect from one position to the other.
+
+ Arguments:
+ from - integer: staring value
+ to - integer: the ending value
+
+ Examples:
+ >var myFx = new Fx.Style('myElement', 'opacity').start(0,1); //display a transition from transparent to opaque.
+ */
+
+ start: function(from, to){
+ if (!this.options.wait) this.stop();
+ else if (this.timer) return this;
+ this.from = from;
+ this.to = to;
+ this.change = this.to - this.from;
+ this.time = $time();
+ this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
+ this.fireEvent('onStart', this.element);
+ return this;
+ },
+
+ /*
+ Property: stop
+ Stops the transition.
+ */
+
+ stop: function(end){
+ if (!this.timer) return this;
+ this.timer = $clear(this.timer);
+ if (!end) this.fireEvent('onCancel', this.element);
+ return this;
+ }/*compatibility*/,
+
+ custom: function(from, to){
+ return this.start(from, to);
+ },
+
+ clearTimer: function(end){
+ return this.stop(end);
+ }
+
+ /*end compatibility*/
+
+});
+
+Fx.Base.implement(new Chain, new Events, new Options);
+
+/*
+Script: Fx.CSS.js
+ Css parsing class for effects. Required by <Fx.Style>, <Fx.Styles>, <Fx.Elements>. No documentation needed, as its used internally.
+
+License:
+ MIT-style license.
+*/
+
+Fx.CSS = {
+
+ select: function(property, to){
+ if (property.test(/color/i)) return this.Color;
+ var type = $type(to);
+ if ((type == 'array') || (type == 'string' && to.contains(' '))) return this.Multi;
+ return this.Single;
+ },
+
+ parse: function(el, property, fromTo){
+ if (!fromTo.push) fromTo = [fromTo];
+ var from = fromTo[0], to = fromTo[1];
+ if (!$chk(to)){
+ to = from;
+ from = el.getStyle(property);
+ }
+ var css = this.select(property, to);
+ return {'from': css.parse(from), 'to': css.parse(to), 'css': css};
+ }
+
+};
+
+Fx.CSS.Single = {
+
+ parse: function(value){
+ return parseFloat(value);
+ },
+
+ getNow: function(from, to, fx){
+ return fx.compute(from, to);
+ },
+
+ getValue: function(value, unit, property){
+ if (unit == 'px' && property != 'opacity') value = Math.round(value);
+ return value + unit;
+ }
+
+};
+
+Fx.CSS.Multi = {
+
+ parse: function(value){
+ return value.push ? value : value.split(' ').map(function(v){
+ return parseFloat(v);
+ });
+ },
+
+ getNow: function(from, to, fx){
+ var now = [];
+ for (var i = 0; i < from.length; i++) now[i] = fx.compute(from[i], to[i]);
+ return now;
+ },
+
+ getValue: function(value, unit, property){
+ if (unit == 'px' && property != 'opacity') value = value.map(Math.round);
+ return value.join(unit + ' ') + unit;
+ }
+
+};
+
+Fx.CSS.Color = {
+
+ parse: function(value){
+ return value.push ? value : value.hexToRgb(true);
+ },
+
+ getNow: function(from, to, fx){
+ var now = [];
+ for (var i = 0; i < from.length; i++) now[i] = Math.round(fx.compute(from[i], to[i]));
+ return now;
+ },
+
+ getValue: function(value){
+ return 'rgb(' + value.join(',') + ')';
+ }
+
+};
+
+/*
+Script: Fx.Styles.js
+ Contains <Fx.Styles>
+
+License:
+ MIT-style license.
+*/
+
+/*
+Class: Fx.Styles
+ Allows you to animate multiple css properties at once;
+ Colors must be in hex format.
+ Inherits methods, properties, options and events from <Fx.Base>.
+
+Arguments:
+ el - the $(element) to apply the styles transition to
+ options - the fx options (see: <Fx.Base>)
+
+Example:
+ (start code)
+ var myEffects = new Fx.Styles('myElement', {duration: 1000, transition: Fx.Transitions.linear});
+
+ //height from 10 to 100 and width from 900 to 300
+ myEffects.start({
+ 'height': [10, 100],
+ 'width': [900, 300]
+ });
+
+ //or height from current height to 100 and width from current width to 300
+ myEffects.start({
+ 'height': 100,
+ 'width': 300
+ });
+ (end)
+*/
+
+Fx.Styles = Fx.Base.extend({
+
+ initialize: function(el, options){
+ this.element = $(el);
+ this.parent(options);
+ },
+
+ setNow: function(){
+ for (var p in this.from) this.now[p] = this.css[p].getNow(this.from[p], this.to[p], this);
+ },
+
+ set: function(to){
+ var parsed = {};
+ this.css = {};
+ for (var p in to){
+ this.css[p] = Fx.CSS.select(p, to[p]);
+ parsed[p] = this.css[p].parse(to[p]);
+ }
+ return this.parent(parsed);
+ },
+
+ /*
+ Property: start
+ Executes a transition for any number of css properties in tandem.
+
+ Arguments:
+ obj - an object containing keys that specify css properties to alter and values that specify either the from/to values (as an array) or just the end value (an integer).
+
+ Example:
+ see <Fx.Styles>
+ */
+
+ start: function(obj){
+ if (this.timer && this.options.wait) return this;
+ this.now = {};
+ this.css = {};
+ var from = {}, to = {};
+ for (var p in obj){
+ var parsed = Fx.CSS.parse(this.element, p, obj[p]);
+ from[p] = parsed.from;
+ to[p] = parsed.to;
+ this.css[p] = parsed.css;
+ }
+ return this.parent(from, to);
+ },
+
+ increase: function(){
+ for (var p in this.now) this.element.setStyle(p, this.css[p].getValue(this.now[p], this.options.unit, p));
+ }
+
+});
+
+/*
+Class: Element
+ Custom class to allow all of its methods to be used with any DOM element via the dollar function <$>.
+*/
+
+Element.extend({
+
+ /*
+ Property: effects
+ Applies an <Fx.Styles> to the Element; This a shortcut for <Fx.Styles>.
+
+ Example:
+ >var myEffects = $(myElement).effects({duration: 1000, transition: Fx.Transitions.Sine.easeInOut});
+ >myEffects.start({'height': [10, 100], 'width': [900, 300]});
+ */
+
+ effects: function(options){
+ return new Fx.Styles(this, options);
+ }
+
+});
+
+/*
+Script: Drag.Base.js
+ Contains <Drag.Base>, <Element.makeResizable>
+
+License:
+ MIT-style license.
+*/
+
+var Drag = {};
+
+/*
+Class: Drag.Base
+ Modify two css properties of an element based on the position of the mouse.
+
+Note:
+ Drag.Base requires an XHTML doctype.
+
+Arguments:
+ el - the $(element) to apply the transformations to.
+ options - optional. The options object.
+
+Options:
+ handle - the $(element) to act as the handle for the draggable element. defaults to the $(element) itself.
+ modifiers - an object. see Modifiers Below.
+ limit - an object, see Limit below.
+ grid - optional, distance in px for snap-to-grid dragging
+ snap - optional, the distance you have to drag before the element starts to respond to the drag. defaults to false
+
+ modifiers:
+ x - string, the style you want to modify when the mouse moves in an horizontal direction. defaults to 'left'
+ y - string, the style you want to modify when the mouse moves in a vertical direction. defaults to 'top'
+
+ limit:
+ x - array with start and end limit relative to modifiers.x
+ y - array with start and end limit relative to modifiers.y
+
+Events:
+ onStart - optional, function to execute when the user starts to drag (on mousedown);
+ onComplete - optional, function to execute when the user completes the drag.
+ onDrag - optional, function to execute at every step of the drag
+*/
+
+Drag.Base = new Class({
+
+ options: {
+ handle: false,
+ unit: 'px',
+ onStart: Class.empty,
+ onBeforeStart: Class.empty,
+ onComplete: Class.empty,
+ onSnap: Class.empty,
+ onDrag: Class.empty,
+ limit: false,
+ modifiers: {x: 'left', y: 'top'},
+ grid: false,
+ snap: 6
+ },
+
+ initialize: function(el, options){
+ this.setOptions(options);
+ this.element = $(el);
+ this.handle = $(this.options.handle) || this.element;
+ this.mouse = {'now': {}, 'pos': {}};
+ this.value = {'start': {}, 'now': {}};
+ this.bound = {
+ 'start': this.start.bindWithEvent(this),
+ 'check': this.check.bindWithEvent(this),
+ 'drag': this.drag.bindWithEvent(this),
+ 'stop': this.stop.bind(this)
+ };
+ this.attach();
+ if (this.options.initialize) this.options.initialize.call(this);
+ },
+
+ attach: function(){
+ this.handle.addEvent('mousedown', this.bound.start);
+ return this;
+ },
+
+ detach: function(){
+ this.handle.removeEvent('mousedown', this.bound.start);
+ return this;
+ },
+
+ start: function(event){
+ this.fireEvent('onBeforeStart', this.element);
+ this.mouse.start = event.page;
+ var limit = this.options.limit;
+ this.limit = {'x': [], 'y': []};
+ for (var z in this.options.modifiers){
+ if (!this.options.modifiers[z]) continue;
+ this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
+ this.mouse.pos[z] = event.page[z] - this.value.now[z];
+ if (limit && limit[z]){
+ for (var i = 0; i < 2; i++){
+ if ($chk(limit[z][i])) this.limit[z][i] = ($type(limit[z][i]) == 'function') ? limit[z][i]() : limit[z][i];
+ }
+ }
+ }
+ if ($type(this.options.grid) == 'number') this.options.grid = {'x': this.options.grid, 'y': this.options.grid};
+ document.addListener('mousemove', this.bound.check);
+ document.addListener('mouseup', this.bound.stop);
+ this.fireEvent('onStart', this.element);
+ event.stop();
+ },
+
+ check: function(event){
+ var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
+ if (distance > this.options.snap){
+ document.removeListener('mousemove', this.bound.check);
+ document.addListener('mousemove', this.bound.drag);
+ this.drag(event);
+ this.fireEvent('onSnap', this.element);
+ }
+ event.stop();
+ },
+
+ drag: function(event){
+ this.out = false;
+ this.mouse.now = event.page;
+ for (var z in this.options.modifiers){
+ if (!this.options.modifiers[z]) continue;
+ this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
+ if (this.limit[z]){
+ if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
+ this.value.now[z] = this.limit[z][1];
+ this.out = true;
+ } else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
+ this.value.now[z] = this.limit[z][0];
+ this.out = true;
+ }
+ }
+ if (this.options.grid[z]) this.value.now[z] -= (this.value.now[z] % this.options.grid[z]);
+ this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
+ }
+ this.fireEvent('onDrag', this.element);
+ event.stop();
+ },
+
+ stop: function(){
+ document.removeListener('mousemove', this.bound.check);
+ document.removeListener('mousemove', this.bound.drag);
+ document.removeListener('mouseup', this.bound.stop);
+ this.fireEvent('onComplete', this.element);
+ }
+
+});
+
+Drag.Base.implement(new Events, new Options);
+
+/*
+Class: Element
+ Custom class to allow all of its methods to be used with any DOM element via the dollar function <$>.
+*/
+
+Element.extend({
+
+ /*
+ Property: makeResizable
+ Makes an element resizable (by dragging) with the supplied options.
+
+ Arguments:
+ options - see <Drag.Base> for acceptable options.
+ */
+
+ makeResizable: function(options){
+ return new Drag.Base(this, $merge({modifiers: {x: 'width', y: 'height'}}, options));
+ }
+
+});
+
+/*
+Script: Drag.Move.js
+ Contains <Drag.Move>, <Element.makeDraggable>
+
+License:
+ MIT-style license.
+*/
+
+/*
+Class: Drag.Move
+ Extends <Drag.Base>, has additional functionality for dragging an element, support snapping and droppables.
+ Drag.move supports either position absolute or relative. If no position is found, absolute will be set.
+ Inherits methods, properties, options and events from <Drag.Base>.
+
+Note:
+ Drag.Move requires an XHTML doctype.
+
+Arguments:
+ el - the $(element) to apply the drag to.
+ options - optional. see Options below.
+
+Options:
+ all the drag.Base options, plus:
+ container - an element, will fill automatically limiting options based on the $(element) size and position. defaults to false (no limiting)
+ droppables - an array of elements you can drop your draggable to.
+ overflown - an array of nested scrolling containers, see Element::getPosition
+*/
+
+Drag.Move = Drag.Base.extend({
+
+ options: {
+ droppables: [],
+ container: false,
+ overflown: []
+ },
+
+ initialize: function(el, options){
+ this.setOptions(options);
+ this.element = $(el);
+ this.droppables = $$(this.options.droppables);
+ this.container = $(this.options.container);
+ this.position = {'element': this.element.getStyle('position'), 'container': false};
+ if (this.container) this.position.container = this.container.getStyle('position');
+ if (!['relative', 'absolute', 'fixed'].contains(this.position.element)) this.position.element = 'absolute';
+ var top = this.element.getStyle('top').toInt();
+ var left = this.element.getStyle('left').toInt();
+ if (this.position.element == 'absolute' && !['relative', 'absolute', 'fixed'].contains(this.position.container)){
+ top = $chk(top) ? top : this.element.getTop(this.options.overflown);
+ left = $chk(left) ? left : this.element.getLeft(this.options.overflown);
+ } else {
+ top = $chk(top) ? top : 0;
+ left = $chk(left) ? left : 0;
+ }
+ this.element.setStyles({'top': top, 'left': left, 'position': this.position.element});
+ this.parent(this.element);
+ },
+
+ start: function(event){
+ this.overed = null;
+ if (this.container){
+ var cont = this.container.getCoordinates();
+ var el = this.element.getCoordinates();
+ if (this.position.element == 'absolute' && !['relative', 'absolute', 'fixed'].contains(this.position.container)){
+ this.options.limit = {
+ 'x': [cont.left, cont.right - el.width],
+ 'y': [cont.top, cont.bottom - el.height]
+ };
+ } else {
+ this.options.limit = {
+ 'y': [0, cont.height - el.height],
+ 'x': [0, cont.width - el.width]
+ };
+ }
+ }
+ this.parent(event);
+ },
+
+ drag: function(event){
+ this.parent(event);
+ var overed = this.out ? false : this.droppables.filter(this.checkAgainst, this).getLast();
+ if (this.overed != overed){
+ if (this.overed) this.overed.fireEvent('leave', [this.element, this]);
+ this.overed = overed ? overed.fireEvent('over', [this.element, this]) : null;
+ }
+ return this;
+ },
+
+ checkAgainst: function(el){
+ el = el.getCoordinates(this.options.overflown);
+ var now = this.mouse.now;
+ return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
+ },
+
+ stop: function(){
+ if (this.overed && !this.out) this.overed.fireEvent('drop', [this.element, this]);
+ else this.element.fireEvent('emptydrop', this);
+ this.parent();
+ return this;
+ }
+
+});
+
+/*
+Class: Element
+ Custom class to allow all of its methods to be used with any DOM element via the dollar function <$>.
+*/
+
+Element.extend({
+
+ /*
+ Property: makeDraggable
+ Makes an element draggable with the supplied options.
+
+ Arguments:
+ options - see <Drag.Move> and <Drag.Base> for acceptable options.
+ */
+
+ makeDraggable: function(options){
+ return new Drag.Move(this, options);
+ }
+
+});
+
+/*
Script: XHR.js
Contains the basic XMLHttpRequest Class Wrapper.
diff --git a/pages/Makefile b/pages/Makefile
index 09818e5..fde3974 100644
--- a/pages/Makefile
+++ b/pages/Makefile
@@ -22,7 +22,8 @@ OBJS = menu.o channels.o recordings.o schedule.o \
searchtimers.o edit_searchtimer.o searchresults.o \
searchepg.o login.o ibox.o xmlresponse.o \
play_recording.o pause_recording.o stop_recording.o \
- ffw_recording.o rwd_recording.o setup.o content.o
+ ffw_recording.o rwd_recording.o setup.o content.o \
+ epginfo.o
### Default rules:
diff --git a/pages/channels_widget.ecpp b/pages/channels_widget.ecpp
index ed6d30e..fcf8321 100644
--- a/pages/channels_widget.ecpp
+++ b/pages/channels_widget.ecpp
@@ -27,8 +27,7 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
int lastChannel = LiveSetup().GetLastChannel();
</%cpp>
<select name="<$ name $>" <{ reply.out() << ( !onchange.empty() ? "onchange=\""+onchange+"\"" : "" ); }>>
-% for ( cChannel *listChannel = Channels.First(); listChannel && listChannel->Number() <= lastChannel;
-% listChannel = Channels.Next( listChannel ) ) {
+% for ( cChannel *listChannel = Channels.First(); listChannel && listChannel->Number() <= lastChannel; listChannel = Channels.Next( listChannel ) ) {
% if ( listChannel->GroupSep() || *listChannel->Name() == '\0' )
% continue;
%
diff --git a/pages/edit_searchtimer.ecpp b/pages/edit_searchtimer.ecpp
index c541ede..4d27b59 100644
--- a/pages/edit_searchtimer.ecpp
+++ b/pages/edit_searchtimer.ecpp
@@ -331,7 +331,6 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
<head>
<title>VDR Live - <$ editsearchtimer ? tr("Edit search timer") : tr("New search timer") $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
<script type="text/javascript"><!--
diff --git a/pages/edit_timer.ecpp b/pages/edit_timer.ecpp
index 73e2852..6bf3b9c 100644
--- a/pages/edit_timer.ecpp
+++ b/pages/edit_timer.ecpp
@@ -126,7 +126,6 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
<head>
<title>VDR Live - <$ timer ? tr("Edit timer") : tr("New timer") $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
</head>
<body>
diff --git a/pages/epginfo.ecpp b/pages/epginfo.ecpp
new file mode 100644
index 0000000..9487040
--- /dev/null
+++ b/pages/epginfo.ecpp
@@ -0,0 +1,114 @@
+<%pre>
+#include <sys/stat.h>
+#include <vdr/tools.h>
+#include "exception.h"
+#include "setup.h"
+#include "tools.h"
+#include "epg_events.h"
+#include "recordings.h"
+
+using namespace vdrlive;
+using namespace std;
+
+</%pre>
+<%args>
+ string epgid;
+ string async;
+</%args>
+<%session scope="global">
+bool logged_in(false);
+</%session>
+<%include>page_init.eh</%include>
+<%cpp>
+if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
+</%cpp>
+<%cpp>
+ pageTitle = tr("Electronic program guide information");
+
+ bool ajaxReq = !async.empty() && (lexical_cast<int>(async) != 0);
+
+ EpgInfoPtr epgEvent;
+ bool aboutBox(false);
+
+ // These get initialized when needed. When freed by getting out
+ // out of scope they will release (if initialized) important
+ // Semaphores/Locks.
+ cSchedulesLock schedulesLock;
+ RecordingsManagerPtr recordings;
+
+ if (!epgid.empty()) {
+
+ const string recording("recording_");
+ const string event("event_");
+ const string aboutbox("aboutBox");
+
+ // check for recording:
+ if (epgid.compare(0, recording.length(), recording) == 0) {
+ recordings = LiveRecordingsManager();
+ const cRecording* recording = recordings->GetByMd5Hash(epgid);
+ if (recording == 0) {
+ throw HtmlError(tr("Couldn't find recording or no recordings available"));
+ }
+ epgEvent = EpgEvents::CreateEpgInfo(epgid, recording);
+ }
+ // check for event:
+ else if (epgid.compare(0, event.length(), event) == 0) {
+ const cSchedules* schedules = cSchedules::Schedules(schedulesLock);
+ if (!schedules) {
+ throw HtmlError(tr("Error aquiring schedules"));
+ }
+ epgEvent = EpgEvents::CreateEpgInfo(epgid, schedules);
+ }
+ // check for aboutbox:
+ else if (epgid.compare(0, aboutbox.length(), aboutbox) == 0) {
+ aboutBox = true;
+ }
+ }
+</%cpp>
+<& pageelems.doc_type &>
+<html>
+ <head>
+ <title>VDR-Live - <$ pageTitle $></title>
+<%cpp>
+ if (!ajaxReq) {
+</%cpp>
+ <& pageelems.stylesheets &>
+ <& pageelems.ajax_js &>
+<%cpp>
+ }
+</%cpp>
+ </head>
+ <body>
+<%cpp>
+ if (!ajaxReq) {
+</%cpp>
+ <& pageelems.logo &>
+ <& menu &>
+<%cpp>
+ }
+</%cpp>
+ <div class="inhalt">
+<%cpp>
+ if (epgEvent) {
+ string start(epgEvent->StartTime("%a,") + string(" ")
+ + epgEvent->StartTime(tr("%b %d %y")) + string(" ")
+ + epgEvent->StartTime(tr("%I:%M %p")));
+ string tools_component;
+ if (recordings) {
+ tools_component = epgEvent->Archived().empty() ? "recordings.rec_tools" : "recordings.archived_disc" ;
+ }
+</%cpp>
+ <& pageelems.epg_tt_box boxId=(epgEvent->Id()) caption=(epgEvent->Caption()) tools_comp=(tools_component) time=(start) title=(epgEvent->Title()) short_descr=(epgEvent->ShortDescr()) long_descr=(epgEvent->LongDescr()) archived=(epgEvent->Archived()) elapsed=(epgEvent->Elapsed()) &>
+<%cpp>
+ }
+ if (aboutBox) {
+</%cpp>
+ <& pageelems.about_tt_box &>
+<%cpp>
+ }
+</%cpp>
+ </div>
+ </body>
+</html>
+
+<%include>page_exit.eh</%include>
diff --git a/pages/ibox.ecpp b/pages/ibox.ecpp
index 4d819f5..0118c07 100644
--- a/pages/ibox.ecpp
+++ b/pages/ibox.ecpp
@@ -25,7 +25,6 @@ int update_status(1);
</%session>
<%cpp>
if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
- EpgEvents epgEvents;
string EMPTY_STR;
tChannelID prev_chan;
tChannelID next_chan;
@@ -39,25 +38,16 @@ int update_status(1);
#else
const char* NowReplaying = cControl::Control()?cReplayControl::LastReplayed():NULL;
#endif
+
+ EpgInfoPtr epgEvent;
+
if (NowReplaying) {
RecordingsManagerPtr recManager = LiveRecordingsManager();
cRecording *recording = Recordings.GetByName(NowReplaying);
if (recording) {
- string name(recording->Name());
- size_t index = name.find_last_of('~');
- if (index != string::npos)
- name = name.substr(index, name.length());
- const cRecordingInfo* info = recording->Info();
- if (info) {
- EpgEventPtr epgEvent(new EpgEvent(recManager->Md5Hash(recording),
- tr("playing recording"),
- info->Title() ? info->Title() : name,
- info->ShortText() ? info->ShortText() : "",
- info->Description() ? info->Description() : "",
- recording->start,
- recording->start));
- epgEvents.push_back(epgEvent);
- }
+ epgEvent = EpgEvents::CreateEpgInfo(recManager->Md5Hash(recording),
+ recording,
+ tr("playing recording"));
}
}
else {
@@ -75,7 +65,7 @@ int update_status(1);
if (tmp)
next_chan = tmp->GetChannelID();
- string chanName(Channel->Name());
+ const string chanName(Channel->Name());
cSchedulesLock schedulesLock;
const cSchedules* Schedules = cSchedules::Schedules(schedulesLock);
const cSchedule *Schedule = Schedules->GetSchedule(Channel);
@@ -83,80 +73,61 @@ int update_status(1);
if (Schedule) {
const cEvent *Event = Schedule->GetPresentEvent();
if (Event) {
- EpgEventPtr epgEvent(new EpgEvent(CHANNEL_STR,
- Event,
- Channel->Name()));
- epgEvents.push_back(epgEvent);
+ epgEvent = EpgEvents::CreateEpgInfo(Channel,
+ Event,
+ CHANNEL_STR.c_str());
}
else {
- string noInfo(tr("no epg info for current event!"));
- EpgEventPtr epgEvent(new EpgEvent(CHANNEL_STR,
- chanName,
- noInfo,
- EMPTY_STR, EMPTY_STR,
- time(0),
- time(0)));
- epgEvents.push_back(epgEvent);
+ const string noInfo(tr("no epg info for current event!"));
+ epgEvent = EpgEvents::CreateEpgInfo(CHANNEL_STR,
+ chanName,
+ noInfo);
}
}
else {
- string noInfo(tr("no epg info for current channel!"));
- EpgEventPtr epgEvent(new EpgEvent(CHANNEL_STR,
- Channel->Name(),
- noInfo,
- EMPTY_STR, EMPTY_STR,
- time(0),
- time(0)));
- epgEvents.push_back(epgEvent);
+ const string noInfo(tr("no epg info for current channel!"));
+ epgEvent = EpgEvents::CreateEpgInfo(CHANNEL_STR,
+ Channel->Name(),
+ noInfo);
}
}
else {
- string chanName(tr("no current channel!"));
- EpgEventPtr epgEvent(new EpgEvent(CHANNEL_STR,
- chanName,
- chanName,
- EMPTY_STR, EMPTY_STR,
- time(0),
- time(0)));
- epgEvents.push_back(epgEvent);
+ const string chanName(tr("no current channel!"));
+ epgEvent = EpgEvents::CreateEpgInfo(CHANNEL_STR,
+ chanName,
+ chanName);
}
}
- if (epgEvents.size() == 0) {
- string ERROR_STR("error");
- string noInfo(tr("error retrieving status info!"));
- string chanName(tr("no current channel!"));
- EpgEventPtr epgEvent(new EpgEvent(ERROR_STR,
- chanName,
- noInfo,
- EMPTY_STR, EMPTY_STR,
- time(0),
- time(0)));
- epgEvents.push_back(epgEvent);
+ if (!epgEvent) {
+ const string ERROR_STR("error");
+ const string noInfo(tr("error retrieving status info!"));
+ const string chanName(tr("no current channel!"));
+ epgEvent = EpgEvents::CreateEpgInfo(ERROR_STR,
+ chanName,
+ noInfo);
}
- for (vector<EpgEventPtr>::iterator i = epgEvents.begin(); i != epgEvents.end(); ++i) {
- EpgEventPtr epg = *i;
+ else {
if (prev_chan.Valid() && next_chan.Valid())
{
</%cpp>
-<& xmlresponse.ibox update=(update_status) type=(epg->Id()) caption=(epg->Caption()) currentTime=(epg->CurrentTime(tr("%I:%M:%S %p"))) duration=(epg->StartTime(tr("%I:%M %p")) + string(" - ") + epg->EndTime(tr("%I:%M %p"))) title=(epg->Title()) elapsed=(epg->Elapsed()) prev_chan=(prev_chan) next_chan=(next_chan) &>
+<& xmlresponse.ibox update=(update_status) type=(epgEvent->Id()) caption=(epgEvent->Caption()) currentTime=(epgEvent->CurrentTime(tr("%I:%M:%S %p"))) duration=(epgEvent->StartTime(tr("%I:%M %p")) + string(" - ") + epgEvent->EndTime(tr("%I:%M %p"))) title=(epgEvent->Title()) elapsed=(epgEvent->Elapsed()) prev_chan=(prev_chan) next_chan=(next_chan) &>
<%cpp>
}
else if (prev_chan.Valid()) {
</%cpp>
-<& xmlresponse.ibox update=(update_status) type=(epg->Id()) caption=(epg->Caption()) currentTime=(epg->CurrentTime(tr("%I:%M:%S %p"))) duration=(epg->StartTime(tr("%I:%M %p")) + string(" - ") + epg->EndTime(tr("%I:%M %p"))) title=(epg->Title()) elapsed=(epg->Elapsed()) prev_chan=(prev_chan) &>
+<& xmlresponse.ibox update=(update_status) type=(epgEvent->Id()) caption=(epgEvent->Caption()) currentTime=(epgEvent->CurrentTime(tr("%I:%M:%S %p"))) duration=(epgEvent->StartTime(tr("%I:%M %p")) + string(" - ") + epgEvent->EndTime(tr("%I:%M %p"))) title=(epgEvent->Title()) elapsed=(epgEvent->Elapsed()) prev_chan=(prev_chan) &>
<%cpp>
}
else if (next_chan.Valid()) {
</%cpp>
-<& xmlresponse.ibox update=(update_status) type=(epg->Id()) caption=(epg->Caption()) currentTime=(epg->CurrentTime(tr("%I:%M:%S %p"))) duration=(epg->StartTime(tr("%I:%M %p")) + string(" - ") + epg->EndTime(tr("%I:%M %p"))) title=(epg->Title()) elapsed=(epg->Elapsed()) next_chan=(next_chan) &>
+<& xmlresponse.ibox update=(update_status) type=(epgEvent->Id()) caption=(epgEvent->Caption()) currentTime=(epgEvent->CurrentTime(tr("%I:%M:%S %p"))) duration=(epgEvent->StartTime(tr("%I:%M %p")) + string(" - ") + epgEvent->EndTime(tr("%I:%M %p"))) title=(epgEvent->Title()) elapsed=(epgEvent->Elapsed()) next_chan=(next_chan) &>
<%cpp>
}
else {
</%cpp>
-<& xmlresponse.ibox update=(update_status) type=(epg->Id()) caption=(epg->Caption()) currentTime=(epg->CurrentTime(tr("%I:%M:%S %p"))) duration=(epg->StartTime(tr("%I:%M %p")) + string(" - ") + epg->EndTime(tr("%I:%M %p"))) title=(epg->Title()) elapsed=(epg->Elapsed()) &>
+<& xmlresponse.ibox update=(update_status) type=(epgEvent->Id()) caption=(epgEvent->Caption()) currentTime=(epgEvent->CurrentTime(tr("%I:%M:%S %p"))) duration=(epgEvent->StartTime(tr("%I:%M %p")) + string(" - ") + epgEvent->EndTime(tr("%I:%M %p"))) title=(epgEvent->Title()) elapsed=(epgEvent->Elapsed()) &>
<%cpp>
}
- break;
}
</%cpp>
diff --git a/pages/login.ecpp b/pages/login.ecpp
index 70fe7d8..754ec82 100644
--- a/pages/login.ecpp
+++ b/pages/login.ecpp
@@ -38,7 +38,6 @@ if (logged_in || !LiveSetup().UseAuth()) return reply.redirect(LiveSetup().GetSt
<head>
<title>VDR-Live - <$ tr("Login") $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
</head>
<body onload="document.auth.login.focus()">
diff --git a/pages/menu.ecpp b/pages/menu.ecpp
index e94e885..ff42045 100644
--- a/pages/menu.ecpp
+++ b/pages/menu.ecpp
@@ -32,23 +32,23 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
set_component = component;
}>
<div class="menu">
- <a href="whats_on.html?type=now" <& menu.setactive current=("whats_on") &>><$ tr("What's on?") $></a> |
- <a href="schedule.html" <& menu.setactive current=("schedule") &>><$ tr("Schedule") $></a> |
- <a href="timers.html" <& menu.setactive current=("timers") &>><$ tr("Timers") $></a> |
+ <a href="whats_on.html?type=now" <& menu.setactive current=("whats_on") &>><$ tr("What's on?") $></a>
+ | <a href="schedule.html" <& menu.setactive current=("schedule") &>><$ tr("Schedule") $></a>
+ | <a href="timers.html" <& menu.setactive current=("timers") &>><$ tr("Timers") $></a>
% if ( LiveFeatures< features::epgsearch >().Recent() ) {
- <a href="searchepg.html" <& menu.setactive current=("searchepg") &>><$ tr("Search") $></a> |
- <a href="searchtimers.html" <& menu.setactive current=("searchtimers") &>><$ tr("Searchtimers") $></a> |
+ | <a href="searchepg.html" <& menu.setactive current=("searchepg") &>><$ tr("Search") $></a>
+ | <a href="searchtimers.html" <& menu.setactive current=("searchtimers") &>><$ tr("Searchtimers") $></a>
% }
- <a href="recordings.html" <& menu.setactive current=("recordings") &>><$ tr("Recordings") $></a> |
- <a href="remote.html" <& menu.setactive current=("remote") &>><$ tr("Remote Control") $></a> |
- <a href="setup.html" <& menu.setactive current=("setup") &>><$ tr("Setup") $></a>
+ | <a href="recordings.html" <& menu.setactive current=("recordings") &>><$ tr("Recordings") $></a>
+ | <a href="remote.html" <& menu.setactive current=("remote") &>><$ tr("Remote Control") $></a>
+ | <a href="setup.html" <& menu.setactive current=("setup") &>><$ tr("Setup") $></a>
<# --- Used by D.H. to test infobox (not part of the released version)
| <a href="ibox_status.html" <& menu.setactive current=("status") &>><$ tr("Status Test") $></a>
--- #>
% if (LiveSetup().UseAuth()) {
| <a id="login" href="login.html?action=logout"><$ tr("Logout") $></a>
% }
- | <a<& tooltip.display domId=("aboutBox") &> href="#">?</a>
+ | <a <& tooltip.display domId=("aboutBox") &>>?</a>
</div>
% if (!component.empty()) {
<div class="pagemenu">
@@ -67,9 +67,6 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
</div>
</div>
% }
-<div style="display:none;">
- <& pageelems.about_tt_box &>
-</div>
<%def setactive>
<%args>
diff --git a/pages/pageelems.ecpp b/pages/pageelems.ecpp
index cab4a5c..c8efa22 100644
--- a/pages/pageelems.ecpp
+++ b/pages/pageelems.ecpp
@@ -125,16 +125,18 @@ int update_status(1);
<%def ajax_js>
<script type="text/javascript" src="js/mootools/mootools.v1.11.js"></script>
<script type="text/javascript" src="js/live/liveajax.js"></script>
+ <script type="text/javascript" src="js/live/infowin.js"></script>
+ <script type="text/javascript" src="js/live/hinttips.js"></script>
<%cpp>if (LiveSetup().GetShowInfoBox()) { </%cpp>
<script type="text/javascript" src="js/live/vdr_status.js"></script>
<script type="text/javascript"><!--
var InfoBox = new LiveVdrInfo('ibox.xml', 'infobox');
window.addEvent('domready', function(){
- InfoBox.request(<%cpp> if (update_status) { reply.sout() << "true"; } else { reply.sout() << "false"; } </%cpp>);
- });
+ InfoBox.request(<%cpp> if (update_status) { reply.sout() << "true"; } else { reply.sout() << "false"; } </%cpp>);
+ });
window.addEvent('unload', function(){
- InfoBox.pageFinished();
- });
+ InfoBox.pageFinished();
+ });
--></script>
<%cpp> } </%cpp>
</%def>
@@ -169,7 +171,7 @@ int update_status(1);
</%args>
<div class="epg_description" id="<$ (boxId) $>">
<div class="station">
- <div class="boxheader"><div><div><$ (caption) $><& tooltip.close domId=(boxId) &></div></div></div>
+ <div class="boxheader"><div><div class="caption"><$ (caption) $></div></div></div>
</div>
<div class="epg_content">
<div class="epg_tools">
@@ -191,11 +193,11 @@ int update_status(1);
</%def>
<%def about_tt_box>
- <div class="about_box" id="aboutBox">
+ <div class="epg_description" id="aboutBox">
<div class="station">
- <div class="boxheader"><div><div><$ tr(LIVESUMMARY) $><& tooltip.close domId=("aboutBox") &></div></div></div>
+ <div class="boxheader"><div><div class="caption"><$ tr(LIVESUMMARY) $></div></div></div>
</div>
- <div class="about_content">
+ <div class="epg_content">
<div>
<div class="about_head"><div><div><$ tr("Authors") $></div></div></div>
<div class="about_left"><$ tr("Project leader") $>:</div>
diff --git a/pages/recordings.ecpp b/pages/recordings.ecpp
index 2132541..5ceb7b4 100644
--- a/pages/recordings.ecpp
+++ b/pages/recordings.ecpp
@@ -20,7 +20,6 @@ using namespace std;
</%session>
<%request scope="page">
RecordingsTree recordingsTree(LiveRecordingsManager());
-EpgEvents epgEvents;
</%request>
<%include>page_init.eh</%include>
<%cpp>
@@ -33,7 +32,6 @@ EpgEvents epgEvents;
<head>
<title>VDR-Live - <$ pageTitle $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
<script type="text/javascript" src="treeview.js"></script>
</head>
@@ -52,11 +50,6 @@ EpgEvents epgEvents;
</div>
% }
</div>
-% if (Recordings.Count() > 0) {
- <div class="epg_data" style="display: none;">
-<& recordings.recordings_data &>
- </div>
-% }
</body>
</html>
<%include>page_exit.eh</%include>
@@ -98,16 +91,13 @@ for (iter = recordingsTree.begin(path); iter != end; ++iter) {
for (iter = recordingsTree.begin(path); iter != end; ++iter) {
RecordingsTree::RecordingsItemPtr recItem = iter->second;
if (!recItem->IsDir()) {
- EpgEventPtr epgEvent(RecordingsTree::CreateEpgEvent(recItem));
- if (epgEvent) {
- epgEvents.push_back(epgEvent);
- }
string day(FormatDateTime("%a,", recItem->StartTime()));
string dayLen(lexical_cast<string, int>(day.length() - 1) + ".25em;");
- string hint(tr("Click to view details.")); if (epgEvent && !epgEvent->ShortDescr().empty()) hint = (epgEvent->ShortDescr() + "<br />" + hint);
+ string shortDescr(recItem->RecInfo()->ShortText() ? recItem->RecInfo()->ShortText() : "");
+ string hint(tr("Click to view details.")); if (!shortDescr.empty()) hint = shortDescr + "<br />" + hint;
</%cpp>
<li class="recording">
- <& rec_item_file name=(recItem->Name()) level=(level) id=(recItem->Id()) day=(day) dayLen=(dayLen) startTime=(recItem->StartTime()) hint=(hint) shortDescr=(epgEvent ? epgEvent->ShortDescr() : "") archived=(epgEvent ? epgEvent->Archived() : "") archiveId=(recItem->ArchiveId()) &>
+ <& rec_item_file name=(recItem->Name()) level=(level) id=(recItem->Id()) day=(day) dayLen=(dayLen) startTime=(recItem->StartTime()) hint=(hint) shortDescr=(shortDescr) archived=(RecordingsManager::GetArchiveDescr(recItem->Recording())) &>
</li>
<%cpp>
}
@@ -117,24 +107,6 @@ for (iter = recordingsTree.begin(path); iter != end; ++iter) {
<# ---------------------------------------------------------------------- #>
-<%def recordings_data>
-<%cpp>
- // create hidden div for the tooltip hints.
- for (vector<EpgEventPtr>::iterator i = epgEvents.begin(); i != epgEvents.end(); ++i) {
- EpgEventPtr epg = *i;
- string start(epg->StartTime("%a,") + string(" ")
- + epg->StartTime(tr("%b %d %y")) + string(" ")
- + epg->StartTime(tr("%I:%M %p")));
- string tools_component = epg->Archived().empty() ? "recordings.rec_tools" : "recordings.archived_disc" ;
-</%cpp>
- <& pageelems.epg_tt_box boxId=(epg->Id()) caption=(epg->Caption()) tools_comp=(tools_component) time=(start) title=(epg->Title()) short_descr=(epg->ShortDescr()) long_descr=(epg->LongDescr()) archived=(epg->Archived()) elapsed=(epg->Elapsed()) &>
-<%cpp>
- }
-</%cpp>
-</%def>
-
-<# ---------------------------------------------------------------------- #>
-
<%def rec_tools>
<%args>
string id;
@@ -182,7 +154,6 @@ for (iter = recordingsTree.begin(path); iter != end; ++iter) {
string hint;
string shortDescr;
string archived;
- string archiveId;
</%args>
<div class="recording_item">
<div class="recording_imgs"><%cpp> reply.out() << StringRepeat(level + 1, "<img src=\"transparent.png\" alt=\"\" width=\"16px\" height=\"16px\" />"); </%cpp><%cpp> if (!archived.empty()) { </%cpp><& archived_disc archived=(archived) &><%cpp> } else { </%cpp><img src="<$ LiveSetup().GetThemedLink("img", "movie.png") $>" alt="movie" /><%cpp> } </%cpp></div>
@@ -190,7 +161,7 @@ for (iter = recordingsTree.begin(path); iter != end; ++iter) {
<div class="recording_day" style="width: <$ dayLen $>"><$ day $></div>
<div class="recording_date"><$ FormatDateTime(tr("%b %d %y"), startTime) $></div>
<div class="recording_time"><$ FormatDateTime(tr("%I:%M %p"), startTime) $></div>
- <div class="recording_name" <& tooltip.hint text=(hint) &><& tooltip.display domId=(id) &>><$ name $><br /><%cpp>if ((name != shortDescr) && (!shortDescr.empty())) {</%cpp><span><$ shortDescr $></span><%cpp> } else { </%cpp><span>&nbsp;</span><%cpp> } </%cpp></div>
+ <div class="recording_name"><a <& tooltip.hint text=(hint) &><& tooltip.display domId=(id) &>><$ name $><br /><%cpp>if ((name != shortDescr) && (!shortDescr.empty())) {</%cpp><span><$ shortDescr $></span><%cpp> } else { </%cpp><span>&nbsp;</span><%cpp> } </%cpp></a></div>
</div>
<div class="recording_actions">
<%cpp>
diff --git a/pages/remote.ecpp b/pages/remote.ecpp
index 10eea15..4805728 100644
--- a/pages/remote.ecpp
+++ b/pages/remote.ecpp
@@ -26,7 +26,6 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
<head>
<title>VDR-Live - <$ pageTitle $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
<script type="text/javascript"><!--
var newImg = new Image();
diff --git a/pages/schedule.ecpp b/pages/schedule.ecpp
index 39942bb..f427a53 100644
--- a/pages/schedule.ecpp
+++ b/pages/schedule.ecpp
@@ -59,7 +59,6 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
<head>
<title>VDR Live - <$ pageTitle $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
</head>
<body>
@@ -74,10 +73,8 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
}
else {
</%cpp>
- <table class="listing" cellspacing="0" cellpadding="0">
+ <table class="listing" cellspacing="0" cellpadding="0">
<%cpp>
- EpgEvents epgEvents;
-
string current_day = "";
const cEvent* PresentEvent = Schedule->GetPresentEvent();
time_t now = time(NULL) - ::Setup.EPGLinger * 60;
@@ -87,21 +84,16 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
if (Event->EndTime() <= now && Event != PresentEvent)
continue;
- string evntId("eventId_");
- evntId += lexical_cast<std::string, int>(++evntNr);
- EpgEventPtr epgEvent(new EpgEvent(evntId, Event, Channel->Name()));
- epgEvents.push_back(epgEvent);
+ EpgInfoPtr epgEvent = EpgEvents::CreateEpgInfo(Channel, Event);
+
+ string title(epgEvent->Title());
+ string short_description(epgEvent->ShortDescr());
+ string description(epgEvent->LongDescr());
+ string start(epgEvent->StartTime(tr("%I:%M %p")));
+ string end(epgEvent->EndTime(tr("%I:%M %p")));
+ string day(epgEvent->StartTime(tr("%A, %b %d %Y")));
+ string strEventID = lexical_cast<string>(Event->EventID());
- string title(Event->Title() ? Event->Title() : "");
- string short_description(Event->ShortText() ? Event->ShortText() : "");
- string description(Event->Description() ? Event->Description() : "");
- string start(Event->StartTime() ? FormatDateTime(tr("%I:%M %p"), Event->StartTime()) : "");
- string end(Event->EndTime() ? FormatDateTime(tr("%I:%M %p"), Event->EndTime()) : "");
- string day(Event->StartTime() ? FormatDateTime(tr("%A, %b %d %Y"), Event->StartTime()) : "");
- tEventID event = Event->EventID();
- ostringstream os;
- os << Event->EventID();
- string strEventID = os.str();
bool truncated = false;
bool lastEventCurrentDay = false;
{
@@ -132,26 +124,17 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
}
</%cpp>
<tr>
- <td class="action leftcol <? lastEventCurrentDay ? "bottomrow" ?>"><& pageelems.event_timer channelid=(channel_id) eventid=(event) &></td>
+ <td class="action leftcol <? lastEventCurrentDay ? "bottomrow" ?>"><& pageelems.event_timer channelid=(channel_id) eventid=(strEventID) &></td>
<td class="action <? lastEventCurrentDay ? "bottomrow" ?>"><%cpp>if (LiveFeatures<features::epgsearch>().Recent() ) { </%cpp><a href="searchresults.html?searchplain=<$ StringUrlEncode(title) $>"><img src="<$ LiveSetup().GetThemedLink("img", "search.png") $>" alt="" <& tooltip.hint text=(tr("Search for repeats.")) &>></img></a><%cpp> } else { </%cpp><img src="transparent.png" width="16" height="16"><%cpp> } </%cpp></td>
<td class="action <? lastEventCurrentDay ? "bottomrow" ?>"><a href="http://akas.imdb.com/Tsearch?title=<$ StringUrlEncode(title) $>"><img src="<$ LiveSetup().GetThemedLink("img", "imdb.png") $>" border="0" alt="" <& tooltip.hint text=(tr("Find more at the Internet Movie Database.")) &>></img></a></td>
<td class="topaligned <? lastEventCurrentDay ? "bottomrow" ?>"><div class="withmargin"><$ start $> - <$ end $></div></td>
- <td class="<? (Event == PresentEvent) ? "current" ?> topaligned rightcol <? lastEventCurrentDay ? "bottomrow" ?>"><div class="more withmargin" <& tooltip.hint text=(StringEscapeAndBreak(StringWordTruncate(description, 300, truncated)) + "<br />" + tr("Click to view details.")) &><& tooltip.display domId=(epgEvent->Id()) &>><span class="title"><$ title $></span><br /><span class="short"><%cpp>if (short_description.empty()) { </%cpp>&nbsp;<%cpp> } </%cpp><$ short_description $></span></div></td>
+ <td class="<? (Event == PresentEvent) ? "current" ?> topaligned rightcol <? lastEventCurrentDay ? "bottomrow" ?>"><div class="more withmargin"><a <& tooltip.hint text=(StringEscapeAndBreak(StringWordTruncate(description, 300, truncated)) + "<br />" + tr("Click to view details.")) &><& tooltip.display domId=(epgEvent->Id()) &>><span class="title"><$ title $></span><br /><span class="short"><%cpp>if (short_description.empty()) { </%cpp>&nbsp;<%cpp> } </%cpp><$ short_description $></span></a></div></td>
</tr>
<%cpp>
}
</%cpp>
</table>
- </div>
- <div class="epg_data" style="display: none;">
-<%cpp>
- // create hidden div for the tooltip hints.
- for (vector<EpgEventPtr>::iterator i = epgEvents.begin(); i != epgEvents.end(); ++i) {
- EpgEventPtr epg = *i;
-</%cpp>
- <& pageelems.epg_tt_box boxId=(epg->Id()) caption=(epg->Caption()) time=(epg->StartTime(tr("%I:%M %p")) + string(" - ") + epg->EndTime(tr("%I:%M %p"))) title=(epg->Title()) short_descr=(epg->ShortDescr()) long_descr=(epg->LongDescr()) elapsed=(epg->Elapsed()) &>
<%cpp>
- }
}
</%cpp>
</div>
diff --git a/pages/searchepg.ecpp b/pages/searchepg.ecpp
index 9ed1387..f885d4e 100644
--- a/pages/searchepg.ecpp
+++ b/pages/searchepg.ecpp
@@ -152,7 +152,6 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
<head>
<title>VDR Live - <$ tr("Search") $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
<script type="text/javascript"><!--
diff --git a/pages/searchresults.ecpp b/pages/searchresults.ecpp
index e2b83a2..63226d5 100644
--- a/pages/searchresults.ecpp
+++ b/pages/searchresults.ecpp
@@ -47,7 +47,6 @@ bool logged_in(false);
<head>
<title>VDR-Live - <$ pageTitle $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
</head>
<body>
@@ -60,8 +59,6 @@ bool logged_in(false);
<table class="listing" cellspacing="0" callpadding="0">
<%cpp>
string current_day = "";
- EpgEvents epgEvents;
- int evntNr = 0;
for (SearchResults::iterator result = results.begin(); result != results.end(); ++result) {
string channelname = Channels.GetByChannelID(result->Channel())->Name();
@@ -72,11 +69,8 @@ bool logged_in(false);
tEventID event = result->EventId();
tChannelID channel_id(result->Channel());
string description = result->Description();
+ string epgDomId(EpgEvents::GetDomId(result->Channel(), event));
- string evntId("eventId_");
- evntId += lexical_cast<std::string, int>(++evntNr);
- EpgEventPtr epgEvent(new EpgEvent(evntId, channelname, result->Title(), result->ShortText(), description, result->StartTime(), result->StopTime()));
- epgEvents.push_back(epgEvent);
bool truncated = false;
bool bottom = false;
@@ -104,22 +98,11 @@ bool logged_in(false);
<td class="action leftcol <? bottom ? "bottomrow"?>"><& pageelems.event_timer channelid=(channel_id) eventid=(event)&></td>
<td class="topaligned <? bottom ? "bottomrow"?>"><div class="withmargin"><a href="schedule.html?channel=<$ channelnr $>"><$ channelname $></a></div></td>
<td class="topaligned <? bottom ? "bottomrow"?>"><div class="withmargin"><$ start $> - <$ end $></div></td>
- <td class="topaligned rightcol <? bottom ? "bottomrow"?>"><div class="more withmargin"<& tooltip.hint text=(StringEscapeAndBreak(StringWordTruncate(description, 300, truncated)) + "<br />" + tr("Click to view details.")) &><& tooltip.display domId=(epgEvent->Id()) &>><span class="title"><$ result->Title() $></span><br /><span class="short"><%cpp>if (result->ShortText().empty()) { </%cpp>&nbsp;<%cpp> } </%cpp><$ result->ShortText() $></span></div></td>
+ <td class="topaligned rightcol <? bottom ? "bottomrow"?>"><div class="more withmargin"><a <& tooltip.hint text=(StringEscapeAndBreak(StringWordTruncate(description, 300, truncated)) + "<br />" + tr("Click to view details.")) &><& tooltip.display domId=(epgDomId) &>><span class="title"><$ result->Title() $></span><br /><span class="short"><%cpp>if (result->ShortText().empty()) { </%cpp>&nbsp;<%cpp> } </%cpp><$ result->ShortText() $></span></a></div></td>
</tr>
% }
</table>
</div>
- <div class="epg_data" style="display: none;">
-<%cpp>
- // create hidden div for the tooltip hints.
- for (vector<EpgEventPtr>::iterator i = epgEvents.begin(); i != epgEvents.end(); ++i) {
- EpgEventPtr epg = *i;
-</%cpp>
- <& pageelems.epg_tt_box boxId=(epg->Id()) caption=(epg->Caption()) time=(epg->StartTime(tr("%I:%M %p")) + string(" - ") + epg->EndTime(tr("%I:%M %p"))) title=(epg->Title()) short_descr=(epg->ShortDescr()) long_descr=(epg->LongDescr()) elapsed=(epg->Elapsed()) &>
-<%cpp>
- }
-</%cpp>
- </div>
</body>
</html>
diff --git a/pages/searchtimers.ecpp b/pages/searchtimers.ecpp
index 4cb4a1b..d061843 100644
--- a/pages/searchtimers.ecpp
+++ b/pages/searchtimers.ecpp
@@ -38,7 +38,6 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
<head>
<title>VDR-Live - <$ pageTitle $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
</head>
<body>
diff --git a/pages/setup.ecpp b/pages/setup.ecpp
index ac9f2c5..2ec0e28 100644
--- a/pages/setup.ecpp
+++ b/pages/setup.ecpp
@@ -75,7 +75,6 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
<head>
<title>VDR-Live - <$ pageTitle $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
<script type="text/javascript"><!--
function initform()
diff --git a/pages/timers.ecpp b/pages/timers.ecpp
index 292f26f..ff637f2 100644
--- a/pages/timers.ecpp
+++ b/pages/timers.ecpp
@@ -47,7 +47,6 @@ using namespace vdrlive;
<head>
<title>VDR-Live - <$ pageTitle $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
</head>
<body>
diff --git a/pages/tooltip.ecpp b/pages/tooltip.ecpp
index bd11415..e7ddaa6 100644
--- a/pages/tooltip.ecpp
+++ b/pages/tooltip.ecpp
@@ -4,58 +4,23 @@
using namespace vdrlive;
</%pre>
-<%def javascript>
-<%args>
-styleClass="domTThint";
-var[];
-value[];
-</%args>
- <script type="text/javascript" src="domLib.js"></script>
- <script type="text/javascript" src="domTT.js"></script>
- <script type="text/javascript" src="domTT_drag.js"></script>
- <script type="text/javascript">
- var domTT_styleClass = "<$ styleClass $>";
-% int idx = 0;
-% for (var_type::const_iterator it = var.begin(); it != var.end(); ++it, idx++) {
- var <$ *it $> = "<$ (value[idx]) $>";
-% }
- domTT_addPredefined('tipHint', 'trail', true, 'delay', 0, 'styleClass', 'domTThint');
- domTT_addPredefined('tipInfo', 'trail', true, 'delay', 0);
- domTT_addPredefined('tipDisp', 'trail', false, 'delay', 0, 'type', 'sticky', 'caption', false, 'offsetX', -30, 'offsetY', -30, 'draggable', true);
- </script>
-</%def>
-
<%def hint>
<%args>
text;
</%args>
-<%cpp> { </%cpp> onmouseover="domTT_activate(this, event, 'predefined', 'tipHint', 'content', '<$ text $>');" <%cpp> } </%cpp>
-</%def>
-
-<%def info>
-<%args>
-domId;
-</%args>
- onmouseover="domTT_activate(this, event, 'predefined', 'tipInfo', 'content', document.getElementById('<$ domId $>'));"
+title="<$ text $>"
</%def>
<%def display>
<%args>
domId;
</%args>
- onclick="domTT_close(domTT_lastOpened); return makeFalse(domTT_activate(this, event, 'predefined', 'tipDisp', 'content', document.getElementById('<$ domId $>'), 'id', '<$ (domId + "_tip") $>'));"
-</%def>
-
-<%def close>
-<%args>
-domId;
-</%args>
- <a href="#void" onclick="domTT_close('<$ (domId + "_tip") $>')"><img src="<$ LiveSetup().GetThemedLink("img", "close.png") $>" alt="" /></a>
+href="epginfo.html?epgid=<$ domId $>"
</%def>
<%def help>
<%args>
text;
</%args>
- <img src="<$ LiveSetup().GetThemedLink("img", "help.png") $>" onmouseover="domTT_close(domTT_lastOpened); domTT_activate(this, event, 'predefined', 'tipHint', 'content', '<$ text $>');"></img>
+<img src="<$ LiveSetup().GetThemedLink("img", "help.png") $>" alt="" <& hint text=(text) &>></img>
</%def>
diff --git a/pages/whats_on.ecpp b/pages/whats_on.ecpp
index 8f052ce..d149e55 100644
--- a/pages/whats_on.ecpp
+++ b/pages/whats_on.ecpp
@@ -79,7 +79,6 @@ if (type == "now") {
<head>
<title>VDR-Live - <$ head $></title>
<& pageelems.stylesheets &>
- <& tooltip.javascript var=("domTT_styleClass") value=("domTTepg") &>
<& pageelems.ajax_js &>
<script type="text/javascript"><!--
function showtime(selection)
@@ -109,7 +108,7 @@ if (type == "now") {
ReadLock channelsLock( Channels );
if (channelsLock) {
- int evntNr = 0;
+ // int evntNr = 0;
for (cChannel *Channel = Channels.First(); Channel && Channel->Number() <= LiveSetup().GetLastChannel(); Channel = Channels.Next(Channel)) {
if (Channel->GroupSep()) {
continue;
@@ -129,13 +128,23 @@ if (type == "now") {
continue;
}
- string evntId("eventId_");
- evntId += lexical_cast<std::string, int>(++evntNr);
- EpgEventPtr epgEvent(new EpgEvent(evntId, Event, Channel->Name()));
- epgEvents.push_back(epgEvent);
tChannelID channel_id(Channel->GetChannelID());
tEventID event = Event->EventID();
+
+ // string evntId("event_");
+ //
+ // string schanid(channel_id.ToString());
+ // replace(schanid.begin(), schanid.end(), '.', 'p');
+ // replace(schanid.begin(), schanid.end(), '-', 'm');
+ // evntId += schanid;
+ // evntId += '_';
+ // evntId += lexical_cast<std::string>(event);
+ // // evntId += lexical_cast<std::string, int>(++evntNr);
+ // EpgEventPtr epgEvent(new EpgEvent(evntId, Event, Channel->Name()));
+ // // epgEvents.push_back(epgEvent);
+ EpgInfoPtr epgEvent = EpgEvents::CreateEpgInfo(Channel, Event);
+
bool truncated = false;
string truncDescription = StringWordTruncate(epgEvent->LongDescr(), maximumTooltipHintLength, truncated);
string longDescription = StringEscapeAndBreak(StringWordTruncate(epgEvent->LongDescr(), maximumDescriptionLength))
@@ -168,7 +177,7 @@ if (type == "now") {
<div class="short withmargin"><$ (epgEvent->ShortDescr()) $></div>
<div class="description withmargin"><$ truncDescription $></div>
% if (truncated) {
- <div class="more withmargin"<& tooltip.hint text=(longDescription) &><& tooltip.display domId=(epgEvent->Id()) &>><$ tr("more") $> ...</div>
+ <div class="more withmargin"><a <& tooltip.hint text=(longDescription) &><& tooltip.display domId=(epgEvent->Id()) &>><$ tr("more") $> ...</a></div>
% }
</div>
</div>
@@ -193,11 +202,11 @@ if (type == "now") {
</div>
</td>
<td class="topaligned <? lastCurrentChanel ? "bottomrow"?>">
- <div class="more withmargin"
+ <div class="more withmargin"><a
% if (!longDescription.empty()) {
<& tooltip.hint text=(longDescription) &><& tooltip.display domId=(epgEvent->Id()) &>
% }
- ><span class="title"><$ (epgEvent->Title()) $></span><br /><span class="short"><$ (epgEvent->ShortDescr()) $></span></div>
+ ><span class="title"><$ (epgEvent->Title()) $></span><br /><span class="short"><$ (epgEvent->ShortDescr()) $></span></a></div>
</td>
<td class="topaligned rightcol <? lastCurrentChanel ? "bottomrow"?>"><div class="station withmargin"><a href="schedule.html?channel=<$ Channel->Number() $>" <& tooltip.hint text=(tr("View the schedule of this channel")) &>><$ (epgEvent->Caption()) $></a></div></td>
</tr>
@@ -210,17 +219,6 @@ if (type == "now") {
</table>
% }
</div>
- <div class="epg_data" style="display: none;">
-<%cpp>
- // create hidden div for the tooltip hints.
- for (vector<EpgEventPtr>::iterator i = epgEvents.begin(); i != epgEvents.end(); ++i) {
- EpgEventPtr epg = *i;
-</%cpp>
- <& pageelems.epg_tt_box boxId=(epg->Id()) caption=(epg->Caption()) time=(epg->StartTime(tr("%I:%M %p")) + string(" - ") + epg->EndTime(tr("%I:%M %p"))) title=(epg->Title()) short_descr=(epg->ShortDescr()) long_descr=(epg->LongDescr()) elapsed=(epg->Elapsed()) &>
-<%cpp>
- }
-</%cpp>
- </div>
</body>
</html>
<%include>page_exit.eh</%include>
diff --git a/recordings.cpp b/recordings.cpp
index a703f3c..5d5070a 100644
--- a/recordings.cpp
+++ b/recordings.cpp
@@ -21,7 +21,7 @@ namespace vdrlive {
string RecordingsManager::Md5Hash(const cRecording* recording) const
{
- return MD5Hash(recording->FileName());
+ return "recording_" + MD5Hash(recording->FileName());
/* unsigned char md5[MD5_DIGEST_LENGTH];
const char* fileName = recording->FileName();
MD5(reinterpret_cast<const unsigned char*>(fileName), strlen(fileName), md5);
@@ -45,6 +45,52 @@ namespace vdrlive {
return 0;
}
+ bool RecordingsManager::IsArchived(const cRecording* recording)
+ {
+ string filename = recording->FileName();
+
+ string vdrFile = filename + "/001.vdr";
+ if (0 == access(vdrFile.c_str(), R_OK))
+ return false;
+
+ filename += "/dvd.vdr";
+ return (0 == access(filename.c_str(), R_OK));
+ }
+
+ const std::string RecordingsManager::GetArchiveId(const cRecording* recording)
+ {
+ string filename = recording->FileName();
+
+ filename += "/dvd.vdr";
+ ifstream dvd(filename.c_str());
+
+ if (dvd) {
+ string archiveDisc;
+ string videoDisc;
+ dvd >> archiveDisc;
+ if ("0000" == archiveDisc) {
+ dvd >> videoDisc;
+ return videoDisc;
+ }
+ return archiveDisc;
+ }
+ return "";
+ }
+
+ const string RecordingsManager::GetArchiveDescr(const cRecording* recording)
+ {
+ string archived;
+ if (IsArchived(recording)) {
+ archived += " [";
+ archived += tr("On archive DVD No.");
+ archived += ": ";
+ archived += GetArchiveId(recording);
+ archived += "]";
+ }
+ return archived;
+ }
+
+
RecordingsTree::RecordingsTree(RecordingsManagerPtr recMan) :
m_maxLevel(0),
m_root(new RecordingsItemDir()),
@@ -177,7 +223,7 @@ namespace vdrlive {
{
}
- RecordingsTree::RecordingsItemRec::RecordingsItemRec(const string& id, const string& name, cRecording* recording) :
+ RecordingsTree::RecordingsItemRec::RecordingsItemRec(const string& id, const string& name, const cRecording* recording) :
RecordingsItem(name),
m_recording(recording),
m_id(id)
@@ -193,68 +239,7 @@ namespace vdrlive {
return m_recording->start;
}
- bool RecordingsTree::RecordingsItemRec::IsArchived() const
- {
- string filename = m_recording->FileName();
-
- string vdrFile = filename + "/001.vdr";
- if (0 == access(vdrFile.c_str(), R_OK))
- return false;
-
- filename += "/dvd.vdr";
- return (0 == access(filename.c_str(), R_OK));
- }
- const std::string RecordingsTree::RecordingsItemRec::ArchiveId() const
- {
- string filename = m_recording->FileName();
-
- filename += "/dvd.vdr";
- ifstream dvd(filename.c_str());
-
- if (dvd) {
- string archiveDisc;
- string videoDisc;
- dvd >> archiveDisc;
- if ("0000" == archiveDisc) {
- dvd >> videoDisc;
- return videoDisc;
- }
- return archiveDisc;
- }
- return "";
- }
-
- EpgEventPtr RecordingsTree::CreateEpgEvent(const RecordingsItemPtr recItem)
- {
- const cRecordingInfo* info = recItem->RecInfo();
- if (info) {
- std::string archived;
- if (recItem->IsArchived()) {
- archived += " [";
- archived += tr("On archive DVD No.");
- archived += ": ";
- archived += recItem->ArchiveId();
- archived += "]";
- }
- EpgEventPtr epgEvent(
- new EpgEvent(
- recItem->Id(),
- recItem->Name(),
- info->Title() ? info->Title() : recItem->Name(),
- info->ShortText() ? info->ShortText() : "",
- info->Description() ? info->Description() : "",
- archived,
- recItem->StartTime(),
- recItem->StartTime()
- )
- );
- return epgEvent;
- }
- else {
- return EpgEventPtr();
- }
- }
RecordingsManagerPtr LiveRecordingsManager()
{
diff --git a/recordings.h b/recordings.h
index 9e42ada..81b971f 100644
--- a/recordings.h
+++ b/recordings.h
@@ -10,8 +10,8 @@
namespace vdrlive {
// Forward declations from epg_events.h
- class EpgEvent;
- typedef std::tr1::shared_ptr<EpgEvent> EpgEventPtr;
+ class EpgInfo;
+ typedef std::tr1::shared_ptr<EpgInfo> EpgInfoPtr;
class RecordingsManager;
typedef std::tr1::shared_ptr<RecordingsManager> RecordingsManagerPtr;
@@ -33,6 +33,21 @@ namespace vdrlive {
*/
const cRecording* GetByMd5Hash(const std::string& hash) const;
+ /**
+ * Determine wether the recording has been archived on
+ * removable media (e.g. DVD-ROM)
+ */
+ static bool IsArchived(const cRecording* recording);
+
+ /**
+ * Provide an identification of the removable media
+ * (e.g. DVD-ROM Number or Name) where the recording has
+ * been archived.
+ */
+ static const std::string GetArchiveId(const cRecording* recording);
+
+ static const std::string GetArchiveDescr(const cRecording* recording);
+
private:
RecordingsManager();
@@ -58,10 +73,8 @@ namespace vdrlive {
virtual time_t StartTime() const = 0;
virtual bool IsDir() const = 0;
- virtual bool IsArchived() const = 0;
virtual const std::string& Name() const { return m_name; }
virtual const std::string Id() const = 0;
- virtual const std::string ArchiveId() const = 0;
virtual const cRecording* Recording() const { return 0; }
virtual const cRecordingInfo* RecInfo() const { return 0; }
@@ -84,9 +97,7 @@ namespace vdrlive {
virtual time_t StartTime() const { return 0; }
virtual bool IsDir() const { return true; }
- virtual bool IsArchived() const { return false; }
virtual const std::string Id() const { std::string e; return e; }
- virtual const std::string ArchiveId() const { std::string e; return e; }
private:
int m_level;
@@ -95,21 +106,19 @@ namespace vdrlive {
class RecordingsItemRec : public RecordingsItem
{
public:
- RecordingsItemRec(const std::string& id, const std::string& name, cRecording* recording);
+ RecordingsItemRec(const std::string& id, const std::string& name, const cRecording* recording);
virtual ~RecordingsItemRec();
virtual time_t StartTime() const;
virtual bool IsDir() const { return false; }
- virtual bool IsArchived() const ;
virtual const std::string Id() const { return m_id; }
- virtual const std::string ArchiveId() const;
virtual const cRecording* Recording() const { return m_recording; }
virtual const cRecordingInfo* RecInfo() const { return m_recording->Info(); }
private:
- cRecording *m_recording;
+ const cRecording *m_recording;
std::string m_id;
};
@@ -122,8 +131,6 @@ namespace vdrlive {
int MaxLevel() const { return m_maxLevel; }
- static EpgEventPtr CreateEpgEvent(const RecordingsItemPtr recItem);
-
private:
int m_maxLevel;
RecordingsItemPtr m_root;
diff --git a/tntconfig.cpp b/tntconfig.cpp
index 1a70aea..7f412ba 100644
--- a/tntconfig.cpp
+++ b/tntconfig.cpp
@@ -52,6 +52,7 @@ void TntConfig::WriteConfig()
file << "MapUrl ^/js([^.]*/)(.*\\.js) content@ js$1$2 text/javascript" << endl;
file << "MapUrl ^/css.*/(.+) content@ css/$1 text/css" << endl;
+ file << "MapUrl ^/img.*/(.+\\.png) content@ css/$1 image/png" << endl;
file << "MapUrl /([^/]+/.+) content@ $1" << endl;
file << "MapUrl /([^.]+)(\\..+)? $1@" << endl;
file << "PropertyFile " << m_propertiesPath << endl;