<%pre>
/* javascript functions depending on settings and language,
 *  but not depending on other parameters
 *  -> static page, no input parameters
*/
#include <setup.h>
#include <livefeatures.h>
using namespace vdrlive;
#if TNTVERSION >= 30000
const char *TNT_ARRAY = "[]";
#else
const char *TNT_ARRAY = "";
#endif
</%pre>

<%cpp>
reply.setHeader (tnt::httpheader::contentType, "text/javascript; charset=utf-8", true);
</%cpp>

const regExLineBreak = /(<|&lt;)br\\/?(>|&gt;)/g

function addErrorIcon(s, numErrors, durationDeviation, duration, numTsFiles) {
  if (numErrors <= -100) return
  s.a += '<div class=\"recording_errors\"><img class=\"icon\" src=\"<$LiveSetup().GetThemedLinkPrefixImg()$>'
  if (durationDeviation > 300 || numErrors  > 0) s.a += 'RecordingErrors.png'
  else if (durationDeviation > 60) s.a += 'NotCheckedForRecordingErrors.png'
  else {
    if (numErrors == 0) s.a += 'NoRecordingErrors.png'
    if (numErrors  > 0) s.a += 'RecordingErrors.png'
    if (numErrors  < 0) s.a += 'NotCheckedForRecordingErrors.png'
  }
  s.a += '\" title=\"'
  if (numErrors == 0) s.a += '<p><$tr("No recording errors")$></p>'
  if (numErrors  > 0) s.a += '<p><$tr("Number of recording errors:")$> ' + String(numErrors) + '</p>'
  if (numErrors  < 0) s.a += '<p><$tr("Recording errors unknown")$></p>'
% if (LiveFeatures<features::tvscraper>().Loaded()) {
    s.a += '<p><$tr("Duration deviation from event length")$>: '
    addTime(s, durationDeviation)
    s.a += ' <$tr("min")$></p>'
% }
  s.a += '<p><$tr("Recording length")$>: '
  s.a += duration
  s.a += ' <$tr("h")$></p><p><$tr("Number of ts files")$>: '
  s.a += String(numTsFiles)
  s.a += '</p>\" /> </div>'
}

function addHdSdIcon(s, hdsd, toolTip, frameParams) {
  s.a += '<div class=\"recording_sd_hd\"><img class=\"sdhd\" src=\"<$LiveSetup().GetThemedLinkPrefixImg()$>'
  s.a += hdsd
  s.a += '.png\" title=\"<p>'
  s.a += toolTip
  if (frameParams.length != 0) {
    s.a += '</p><p>'
    s.a += frameParams
  }
  s.a += '</p>\" /></div>'
}

%// do not HTML-encode name; will be URI-encoded here!
function IMDbS(s, im_db, name, label) {
  if (im_db.length == 0) {
    s.a += '<a href=\"http://www.imdb.com/find?s=all&q='
    if (name.startsWith("%") ) {
      s.a += encodeURIComponent(name.substr(1))
    } else {
      s.a += encodeURIComponent(name)
    }
  } else {
    s.a += '<a href=\"https://www.imdb.com/title/'
    s.a += im_db
  }
  s.a += '\" target=\"_blank\"><img class=\"icon\" src=\"<$LiveSetup().GetThemedLinkPrefixImg()$>imdb.png\" title=\"'
  addEncodeHtml(s, LabelAndAction(label, '<$tr("Find more in the IMDb (Internet Movie Database)")$>'))
  s.a += '\"/></a>'
}

<#
// [0] : EPG ID  (without event_)
// [1] : Timer ID = SortedTimers::EncodeDomId(SortedTimers::GetTimerId(*timer))
//               (without timer_)
// [2] : scraper s_IMDB_ID
// [3] : image.path  (nach "/tvscraper/")
// [4] : "pt" if m_s_image.width <= m_s_image.height, otherwise= ""
// [5] : title (scraper)
// [6] : season / episode (scraper, for TV shows, if available. Otherwise: Empty)
// [7] : runtime (scraper)
// [8] : relase date (scraper)
// [9] : channelnr
// [10] : channelname
// [11] : Name (not HTML-encoded -> can be used for search [URI encode] and in HTML [HTML encode])
// [12] : Shorttext
// [13] : Description
// [14] : Day, time & duration of event
#>
function addEvent(s, bottomrow_i, obj) {
// first column, timer /red or green, ...
  let label = obj[14].replace(/^(\\S*).*$/, '$1') + '<$ tr(" - ") $>' + obj[11]
  let bottomrow = ''
  if (bottomrow_i != 0) bottomrow = 'bottomrow'
  s.a += `<tr><td class=\"action leftcol ${bottomrow}\"><div class=\"event_actions\">`
  if (obj[1].length != 0) {
    s.a += '<a href=\"edit_timer.html?timerid=timer_'
    s.a += obj[1].replace(/&ts=\w*$/, '')
    s.a += '\"><img class=\"icon' + (obj[1].endsWith('&ts=r') ? ' fading' : '') + '\" src=\"<$ LiveSetup().GetThemedLink("img", "") $>' + (obj[1].endsWith('&ts=r') ? 'record_timer_active.png' : (obj[1].endsWith('&ts=i') ? 'record_timer_inactive.png' : 'record_timer_active.png'))
    s.a += '\" title=\"&lt;p&gt;'
    addEncodeHtml(s, label)
    s.a += '&lt;/p&gt;&lt;p class=&quot;click-action&quot;&gt;<$tr("Edit timer")$>&lt;/p&gt;\" />'
  } else {
    s.a += '<a href=\"edit_timer.html?epgid=event_'
    s.a += obj[0]
    s.a += '\"><img class=\"icon\" src=\"<$ LiveSetup().GetThemedLink("img", "record.png") $>'
    s.a += '\" title=\"&lt;p&gt;'
    addEncodeHtml(s, label)
    s.a += '&lt;/p&gt;&lt;p class=&quot;click-action&quot;&gt;<$tr("Record this")$>&lt;/p&gt;\" />'
  }
  s.a += '</a>'
% if (LiveFeatures<features::epgsearch>().Recent() ) {
    s.a += '<a href=\"searchresults.html?searchplain='
    s.a += encodeURIComponent(obj[11])
    s.a += '\"><img class=\"icon\" src=\"<$ LiveSetup().GetThemedLink("img", "search.png") $>'
    s.a += '\" title=\"&lt;p&gt;'
    addEncodeHtml(s, label)
    s.a += '&lt;/p&gt;&lt;p class=&quot;click-action&quot;&gt;<$tr("Search for repeats")$>&lt;/p&gt;\"/></a>'
% } if (LiveSetup().GetShowIMDb() ) {
    IMDbS(s, obj[2], obj[11], label)
% }
  s.a += '</div></td>'
// second col, dependent on width: channel
  if (obj[9] > 0) {
    s.a += `<td class=\"td-hidden-xs ${bottomrow}\"><div class=\"hidden-xs withmargin\"><a href=\"schedule.html?channel=`
    s.a += obj[9]
    s.a += '\">'
    s.a += obj[10]
    s.a += '</a></div></td>'
  }
// third col: image
% if (!LiveSetup().GetTvscraperImageDir().empty() ) {
    s.a += `<td class=\"enlarge-image ${bottomrow}\">`
    addScraperImageTitle(s, obj[3], obj[4], obj[5], obj[6], obj[7], obj[8]);
    s.a += '</td>'
% }
  s.a += `<td class=\"td-hidden-xs ${bottomrow}\"><div class=\"hidden-xs withmargin\">`
  s.a += obj[14]
  s.a += '</div></td>'
// col with times, channel, name, short text
  s.a += `<td class=\"rightcol ${bottomrow}\"><div class=\"withmargin\">`
// first line: link to channel, times, channel
  s.a += '<div class=\"margin-bottom display-xs\">'
  if (obj[9] > 0) {
    s.a += '<a href=\"schedule.html?channel='
    s.a += obj[9]
    s.a += '\">'
  }
  s.a += '<span class=\"normal-font\">'
  s.a += obj[14]
  if (obj[9] > 0) {
    s.a += ' '
    s.a += obj[10]
  }
  s.a += '</span>'
  if (obj[9] > 0) s.a += '</a>'
  s.a += '</div><div>'
// second & third line: Link to event, event title, short text
  addEventRec(s, 'event_', obj[0], '&history_num_back=1', obj[11], '', obj[12], obj[13])
  s.a += '</div></div></td></tr>'
}

function eventString(bottomrow, data) {
  const s = Object.create(null)
  s.a = ""
  addEvent(s, bottomrow, data)
  return s.a
}

// existing recording
// do not HTML-encode name; will be HTML-encoded here!
function existingRecordingSR(s, col_span1, bottomrow, imdb_id, id, stillRecording, image, pt, s_title, s_season_episode, s_runtime, s_date, day_time_duration, errors, hd_sd, channel_name, newR, name, short_text, description, durationDeviation, folder, duration, numTsFiles) {
  let still_recording = '';
  if (stillRecording === 'still_recording') {
    still_recording = ' still_recording';
  }
  s.a += '<tr class=\"existing_recording'
  s.a += still_recording
  s.a += '\"><td colspan='
  s.a += col_span1 > 0 ? col_span1.toString() : '1'
  s.a += ' class=\"leftcol '
  s.a += bottomrow
  s.a += '\">'
  if (col_span1 > 0) {
    // no column span means to skip the label (used in streaming page)
    s.a += '<div class=\"label\"><$ tr("Existing Recording:") $></div>'
  }
<%cpp>
#if VDRVERSNUM >= 20505
</%cpp>
  s.a += '<div class=\"recording_status\">'
  addErrorIcon(s, errors, durationDeviation, duration, numTsFiles)
  addHdSdIcon(s, hd_sd, channel_name, \"\")
  s.a += '</div>'
<%cpp>
#endif
</%cpp>
  s.a += '</td><td class=\"td-hidden-xs '
  s.a += bottomrow
  s.a += '\"><div class=\"hidden-xs recording_day withmargin\">'
  s.a += day_time_duration
  s.a += '</div></td><td class=\"rightcol more '
  s.a += bottomrow
  s.a += '\">'
  addColEventRec(s, still_recording, day_time_duration, 'recording_', id, name, folder, short_text, description)
  s.a += '</td></tr>'
}

function existingRecordingString(col_span, bottomrow, id, stillRecording, imdb_id, image, pt, s_title, s_season_episode, s_runtime, s_date, day_time_duration, errors, hd_sd, channel_name, newR, name, short_text, description, durationDeviation, folder, duration, size, numTsFiles) {
  const s = Object.create(null)
  s.a = ""
  existingRecordingSR(s, col_span, bottomrow, imdb_id, id, stillRecording, image, pt, s_title, s_season_episode, s_runtime, s_date, day_time_duration, errors, hd_sd, channel_name, newR, name, short_text, description, durationDeviation, folder, duration, numTsFiles)
  return s.a
}

function RecordingActionStream(s, id, label) {
  s.a += '<a class=\"hidden-xs invisible\" href=\"stream.html?recid=recording_'
  s.a += id
  s.a += '\" title=\"'
  addEncodeHtml(s, LabelAndAction(label, '<$tr("Play this recording in the browser")$>'))
  s.a += '\"><img class=\"hidden-xs invisible icon\" src=\"<$LiveSetup().GetThemedLinkPrefixImg()$>stream_button.png\" /></a>'
}

async function RecordingsSt_a(s, level, displayFolder, data) {
  var recs_param =  '';
  for (obj_i of data) {
    if (typeof recs[obj_i] === 'undefined') {
      recs_param += '&r<$ TNT_ARRAY $>='
      recs_param += obj_i;
    }
  }
  if (recs_param.length == 0) {
    RecordingsSt_int(s, level, displayFolder, data);
  } else {
    var recs_param_a = 'vdr_start=<$ cToSvInt(LiveSetup().GetVdrStart() ) $>&recordings_tree_creation=';
    recs_param_a += recordings_tree_creation;
    recs_param_a += recs_param;
    const response = await fetch("get_recordings.html", {
      method: "POST",
      headers: {
       "Content-Type": "application/x-www-form-urlencoded",
      },
      body: recs_param_a,
    });
    const new_recs = await response.text();
    eval(new_recs);
    if (vdr_restart) {
      location.reload();
    } else {
      RecordingsSt_int(s, level, displayFolder, data);
    }
  }
}

%// do not HTML-encode title; will be HTML-encoded here!
function addEventRec(s, eventprefix, eventid, http_params, title, folder, shortText, description) {
// eventprefix == 'recording_' or 'event_'
  s.a += '<a href="epginfo.html?epgid='
  s.a += eventprefix
  s.a += eventid
  s.a += http_params
  s.a += '" class="apopup" title="'
  if (description.length != 0) {
    s.a += '<p>'
    s.a += description.replaceAll(regExLineBreak, '</p><p>')
    s.a += '</p>'
  }
  s.a += '<p class=&quot;click-details&quot;><$tr("Click to view details")$></p>"><div class="margin-bottom bold-font">'
  addEncodeHtml(s, title)
  if (folder.length != 0) {
    s.a += '<span class="normal-font"> ('
    s.a += folder.replaceAll("~", "~<wbr>")
    s.a += ')</span>'
  }
  s.a += '</div>'
  // to avoid a link over empty space
  if (shortText.length == 0 && description.length == 0) {
    s.a += '</a>'
    add2ndLine(s, shortText, description)
  } else {
    add2ndLine(s, shortText, description)
    s.a += '</a>'
  }
}

<#
// [0] : ID (prefix "recording_" removed !!!)
// [1] : still_recording
// scraper data
// [2] : IMDB ID
// [3] : image.path  (behind "/tvscraper/")
// [4] : "pt" if m_s_image.width <= m_s_image.height, otherwise= ""
// [5] : title (scraper)
// [6] : season / episode (scraper, for TV shows, if available. Otherwise: Empty)
// [7] : runtime (scraper)
// [8] : release date (scraper)
// end scraper data
// [9] : Day, time
// [10] : Number of recording errors
// [11] : audio/SD/HD/UHD r/s/h/u
// [12] : channel name
// [13] : _new
// [14] : Name (do not HTML-encode name; will be HTML-encoded here!)
// [15] : Shorttext
// [16] : Description
// [17] : Recording Length deviation in seconds
// [18] : Folder (if displayFolder == true. Otherwise empty)
// [19] : duration
// [20] : size
// [21] : numTsFiles
// [22] : frame parameter text
// before calling, make sure that all recs[obj_i] exist!
#>
function RecordingsSt_int(s, level, displayFolder, data) {
  s.a += '<li class="recording"><div><table class="recordings">'
  for (obj_i of data) {
    if (typeof recs[obj_i] === 'undefined') continue;
    let obj = recs[obj_i]
    <#
    // the previous name 'is_recoding' [action still underway] had a different
    // notion than EpgInfo::IsRecording() [kind of element], which is confusing;
    // so we better choose a moire concise name
    #>
    let still_recording = '';
    if (obj[1] === 'still_recording') {
      still_recording = ' still_recording';
    }
    <#
    // the class 'recording_new' allows the style sheet to optionally highlight a new
    // recording's entire row instead of just its title and the short text, whereas the
    // class 'recording_name_new' only applies for the title/short-text column; just
    // appending '_new' to 'recording_item' instead of adding a distinct class would
    // not be flexible enough within the style sheet (i.e, result in too many duplicate
    // style definitions)
    #>
    s.a += '<tr class="recording_item'
    s.a += obj[13] ? ' recording_new' : ''
    <#
    // similar to 'recording_new', the "joint" class 'recording_item.still_recording'
    // allows the style sheet to optionally highlight/animate the entire row of a
    // recording still underway, whereas the 'recording_name[_new].still_recording'
    // will only apply to the title/short-text column
    #>
    s.a += still_recording
    s.a += '">'
    if (level > 0) s.a +=
      '<td class="space"> <img src="img/transparent.png" style="width: calc(var(--icon-width, 16px) * ' + String(level) + '); height: var(--icon-height, 16px)" /></td>'
    s.a += '<td class="td-hidden-xs"><div class="hidden-xs recording_imgs">'
    s.a += '<input type="checkbox" name="deletions<$TNT_ARRAY$>" value="recording_'
    s.a += obj[0]
    s.a += '" />'
    s.a += '</div></td>'  // end of recording_imgs
%   if (!LiveSetup().GetTvscraperImageDir().empty() ) {
    s.a += '<td class=\"space\">'
      addScraperImageTitle(s, obj[3], obj[4], obj[5], obj[6], obj[7], obj[8]);
    s.a += '</td>'
%   }
    s.a += '<td class="td-hidden-xs"><div class="hidden-xs recording_day'
    s.a += still_recording
    s.a += ' margin-bottom">'
    s.a += obj[9]
    s.a += '</div><div class="hidden-xs recording_channel'
    s.a += still_recording
    s.a += '">'
    s.a += obj[12]
    s.a += '</div></td><td class="td-hidden-xs"><div class="hidden-xs recording_info"><div class="hidden-xs recording_duration'
    s.a += still_recording
    s.a += ' margin-bottom">'
    s.a += obj[19]
    if (obj[17] > 0) {
      s.a += '<span title=\"<$tr("Duration deviation from event length")$>\"> ['
      addTime(s, obj[17])
      s.a += ']</span>'
    }
    s.a += '</div><div class="hidden-xs recording_size'
    s.a += still_recording
    s.a += '">'
    s.a += obj[20]
    s.a += '</div></div></td><td><div class="recording_status">'
    addErrorIcon(s, obj[10], obj[17], obj[19], obj[21])
    addHdSdIcon(s, obj[11], obj[12], obj[22])
    s.a += '</div></td>'  // end column with error/sd-hd image
    s.a += '<td><div class=\"recording_name'
    s.a += obj[13]
    s.a += still_recording
    s.a += '\">'
    if (displayFolder == 1) folder = obj[18]
    else folder = ''
    s.a += '<div class="withmargin">'
    addEventRec_r(s, obj[0], obj[14], folder, obj[15], obj[16]);
    s.a += '</div></div></td>'  // end of recording_name / short description
    s.a += '<td><div class=\"recording_actions\">'
    let label = obj[9] + '<$ tr(" - ") $>' + obj[14]
    let xsVisibleIcons = 2;       // the invariant edit and delete icons
    let xsInvisibleIcons = 0;     // the small-screen invisible stream icons
%   if (cDevice::PrimaryDevice() && cDevice::PrimaryDevice()->HasDecoder() ) {
      RecordingActionS(s, obj[0], "vdr_request/play_recording?param=", "play.png", label, "<$tr("Play this recording")$>")
      xsVisibleIcons++;
%   }
%   if (LiveFeatures<features::streamdev_server>().Loaded()) {
%     // use same logic as in pageelems.epg_tool_box
%     if (LiveSetup().GetUseStreamdev()) {
        RecordingActionStream(s, obj[0], label)
        xsInvisibleIcons++
%     }
      RecordingActionPlaylist(s, obj[0], label)
      xsInvisibleIcons++
%   }
%   if (LiveSetup().GetShowIMDb() ) {
      IMDbS(s, obj[2], obj[14], label)
      xsVisibleIcons++;
%   }
    RecordingActionS(s, obj[0], "edit_recording.html?recid=", "edit.png", label, "<$tr("Edit recording")$>")
    RecordingActionS(s, obj[0], "epginfo.html?epgid=del_", "del.png", label, "<$tr("Delete this recording")$>")
    let xsClassName = 'xs-visible-' + xsVisibleIcons
    xsClassName += ' xs-invisible-' + xsInvisibleIcons
    xsClassName += ' xs-columns-' + Math.floor((xsVisibleIcons + 2) / 3)
    s.a += '<div class=\"notpresent ' + xsClassName + '\"></div>'
    s.a += '</div></td></tr>'
  }
  s.a += '</table></div></li>'
}
