diff admajax.js.php @ 1069:5f92fa5e683a

Refactor how the "AJAX" stuff works.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 24 Jan 2017 17:25:48 +0200
parents
children e23057465ca2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/admajax.js.php	Tue Jan 24 17:25:48 2017 +0200
@@ -0,0 +1,1013 @@
+<?php
+//
+// FAPWeb - Simple Web-based Demoparty Management System
+// Party administration page AJAX javascript module
+// (C) Copyright 2012-2017 Tecnic Software productions (TNSP)
+//
+$sessionType = "admin";
+header("Content-Type: application/javascript");
+require_once "mconfig.inc.php";
+require_once "msite.inc.php";
+require_once "msession.inc.php";
+
+// Initiate SQL database connection
+if (!stConnectSQLDB())
+{
+  // Error occured, bail out early
+  cmPrintPageFooter();
+  exit;
+}
+
+// Fetch non-"hardcoded" settings from SQL database
+stReloadSettings();
+
+// Check if sessions are enabled
+if (!stChkSetting("admPassword") || !stAdmSessionAuth(FALSE))
+  exit;
+
+echo
+  "\n".
+  "var jsSettingsArgs = [];\n";
+
+foreach (stExecSQL("SELECT * FROM settings_groups") as $group)
+{
+  $args = array();
+  if (($res = stExecSQL("SELECT * FROM settings WHERE vgroup=".$group["id"])) !== false)
+  {
+    foreach ($res as $item)
+    {
+      switch ($item["vtype"])
+      {
+        case VT_STR:
+        case VT_TEXT: $type = 1; break;
+        case VT_INT:  $type = 2; break;
+        case VT_BOOL: $type = 3; break;
+      }
+      $args[] = "\"".$item["key"]."\":".$type;
+    }
+  }
+
+  echo "jsSettingsArgs[".$group["id"]."] = {".implode(",", $args)."};\n";
+}
+
+echo
+  "\n".
+  "function jsUpdateSettings(id)\n".
+  "{\n".
+  "  if (id in jsSettingsArgs)\n".
+  "    jsSendPOSTRequest(\"action=update&type=settings&id=\"+id+\"&\"+jsMakePostArgs(jsSettingsArgs[id], \"st\", \"\"));\n".
+  "  return false;\n".
+  "}\n".
+  "\n";
+
+stCommonAJAX("admajax.php", "admlogout.php");
+?>
+
+var activeAttendee = -1, prevAttendee = -1, activeAttendeeTmp = "";
+var activeEntry = -1, prevEntry = -1, activeEntryTmp = "";
+
+var registeredTabs = Object();
+var activeTabs = Object();
+
+
+//
+// Tab related code
+//
+function jsUpdateTabList(tabset, extra)
+{
+  var tabs = "";
+  var content = "";
+
+  // Update the tab header list for this tabset
+  for (var id in registeredTabs[tabset])
+  {
+    var thead = registeredTabs[tabset][id];
+    tabs += "<a id=\"tabHead"+ tabset + id +
+      "\" href=\"#\" onClick=\"jsSwitchActiveTab('"+tabset+"', '"+id+
+      "')\">"+ thead +"</a> ";
+    
+    content += "<div id=\"tabCont"+ tabset + id +"\"></div>";
+  }
+
+  // Update the DOM elements
+  var item = document.getElementById("tabHeaders"+ tabset);
+  if (item) item.innerHTML = tabs + extra;
+
+  item = document.getElementById("tabContents"+ tabset);
+  if (item) item.innerHTML = content;
+}
+
+
+function jsRegisterTab(tabset, id, name)
+{
+  // Create tabset object "array" if it does not exist
+  if (!registeredTabs[tabset])
+    registeredTabs[tabset] = Object();
+
+  // Register tab
+  registeredTabs[tabset][id] = name;
+}
+
+
+function jsSwitchActiveTab(tabset, tab)
+{
+  // Go through all registered tabs to update their state
+  for (var id in registeredTabs[tabset])
+  {
+    var tabContent = document.getElementById("tabCont"+ tabset + id);
+    var tabHead = document.getElementById("tabHead"+ tabset + id);
+    if (tabContent && tabHead)
+    {
+      tabContent.style.display = (tab == id) ? "block" : "none";
+      tabHead.className = (tab == id) ? "active" : "inactive";
+      if (tab == id)
+      {
+        // Set active tab and refresh contents
+        activeTabs[tabset] = id;
+        setTimeout(function(qtabset, qid) {
+          switch (qtabset)
+          {
+            case "CM": refreshDispatchCM(qid); break;
+            case "CC": refreshDispatchCC(qid); break;
+            case "CS": refreshDispatchCS(qid); break;
+            default: jsMessageBox("Invalid tabset value: '"+ qtabset +"'.");
+          }
+        }, 10, tabset, id);
+        jsCancelUploadCBS();
+      }
+      else
+      {
+        // Clear inactive tabs
+        tabContent.innerHTML = "";
+      }
+    }
+  }
+}
+
+
+//
+// Admin interface specific popups
+//
+function jsHandleAdminPopupKeys(ev)
+{
+  ev = ev || window.event;
+  var key = ev.keyCode ? ev.keyCode : ev.which;
+  if (key == 27)
+  {
+    jsCloseAdminPopup();
+    return false;
+  }
+  else
+    return true;
+}
+
+
+function jsCloseAdminPopup()
+{
+  var nitem = document.getElementById("adminPopup");
+  if (nitem)
+  {
+    document.onkeydown = null;
+
+    nitem.innerHTML = "";
+    nitem.style.display = "none";
+  }
+}
+
+
+function jsOpenAdminPopup(txt)
+{
+  var nitem = document.getElementById("adminPopup");
+  if (nitem)
+  {
+    document.onkeydown = jsHandleAdminPopupKeys;
+
+    nitem.innerHTML = txt;
+    nitem.style.display = "block";
+  }
+}
+
+
+//
+// Generic element refresh
+//
+function jsRefreshItems(id,name,extra)
+{
+  var msuccess = function(txt)
+  {
+    var nitem = document.getElementById(id);
+    if (nitem) nitem.innerHTML = txt;
+  }
+
+  jsSendPOSTRequest("action=get&type="+name+extra, msuccess);
+}
+
+
+function jsDeleteItem(id,prefix,type,func,dsc,dsc2)
+{
+  var msuccess = function(txt)
+  {
+    var item = document.getElementById(prefix+id);
+    item.style.display = "none";
+    setTimeout(func, 50);
+  }
+
+  // Clearly mark the element when asking confirmation
+  var item = document.getElementById(prefix+id);
+  var tmp = item.className;
+  item.className += " deleteWarning";
+
+  // Ask confirmation for deletion
+  var mcb_ok = function (data)
+  {
+    jsSendPOSTRequest("action=delete&type="+type+"&id="+id, msuccess);
+    item.className = tmp;
+  }
+  
+  var mcb_cancel = function (data)
+  {
+    item.className = tmp;
+  }
+  
+  if (dsc)
+    jsConfirmBox("Are you sure you want to delete "+dsc+" #"+id+"?", mcb_ok, mcb_cancel, 0);
+  else
+    jsConfirmBox(dsc2, mcb_ok, mcb_cancel, 0);
+}
+
+
+function jsRefreshPanels(id, tab, args, set_default)
+{
+  var msuccess = function(txt)
+  {
+    var nitem = document.getElementById(tab);
+    if (nitem)
+    {
+      if (nitem.innerHTML == "")
+      {
+        nitem.innerHTML =
+          "<div id=\"tabHeaders"+id+"\" class=\"tabHeadersSub\"></div>" +
+          "<div id=\"tabContents"+id+"\" class=\"tabContentsSub\"></div>";
+      }
+
+      try {
+        var tmp = JSON.parse("{"+ txt +"}");
+        registeredTabs[id] = tmp;
+        jsUpdateTabList(id, "");
+        if (activeTabs[id])
+          jsSwitchActiveTab(id, activeTabs[id]);
+        else
+        if (set_default == "*")
+        {
+          for (var nid in registeredTabs[id])
+          {
+            jsSwitchActiveTab(id, nid);
+            break;
+          }
+        }
+        else
+        if (set_default != "")
+          jsSwitchActiveTab(id, set_default);
+      }
+      catch (err) {
+        jsErrorMessageBox("JSON.parse("+ txt +") failure: "+ err);
+      }
+    }
+  }
+
+  jsSendPOSTRequest("action=get&type="+args, msuccess);
+}
+
+
+function refreshDispatchCC(id)
+{
+  switch (id)
+  {
+    case "News"      : jsRefreshItems("tabContCCNews", "news", ""); break;
+    case "Attendees" : jsRefreshItems("tabContCCAttendees", "attendees", ""); activeAttendee = -1; break;
+    case "Voting"    : jsRefreshItems("tabContCCVoting", "voters", ""); break;
+    case "Compos"    : jsRefreshItems("tabContCCCompos", "compos", ""); break;
+    case "InfoSys"   : jsRefreshItems("tabContCCInfoSys", "infoMain", ""); break;
+    case "Settings"  : jsRefreshPanels("CS", "tabContCCSettings", "settingslist", "*"); break;
+    case "Entries"   : jsRefreshPanels("CM", "tabContCCEntries", "compolist", ""); break;
+    default          : alert("Invalid id: '"+ id +"'.'");
+  }
+}
+
+
+function refreshDispatchCS(id)
+{
+  jsRefreshItems("tabContCS"+ id, "settings", "&id="+ id);
+}
+
+
+function refreshDispatchCM(id)
+{
+  prevEntry = activeEntry = -1;
+  jsRefreshItems("tabContCM"+ id, "entries", "&id="+ id);
+}
+
+
+//
+// Site news management
+//
+function addNews()
+{
+  var args = jsMakePostArgs({"title":1,"text":1,"author":1}, "nn", "");
+
+  var msuccess = function(txt)
+  {
+    setTimeout(function () { refreshDispatchCC("News"); }, 50);
+  }
+
+  if (args != "")
+    jsSendPOSTRequest("action=add&type=news&"+args, msuccess);
+
+  return false;
+}
+
+
+function deleteNews(id)
+{
+  jsDeleteItem(id, "news", "news", function() { refreshDispatchCC("News"); }, "news item");
+}
+
+
+function updateNews(id)
+{
+  var args = jsMakePostArgs({"title":1,"text":1,"author":1}, "ne", id);
+
+  var msuccess = function(txt)
+  {
+    jsRefreshItems("news"+id, "newsitem", "&id="+id);
+  }
+
+  if (args != "")
+    jsSendPOSTRequest("action=update&type=news&id="+id+"&"+args, msuccess);
+}
+
+
+//
+// Visitor/attendee management
+//
+function addAttendee()
+{
+  var args = jsMakePostArgs({"name":1,"groups":1,"oneliner":1,"email":1}, "ne", "x");
+
+  var msuccess = function(txt)
+  {
+    setTimeout(function() { refreshDispatchCC("Attendees"); }, 50);
+  }
+
+  if (args != "")
+    jsSendPOSTRequest("action=add&type=attendees&"+args, msuccess);
+
+  return false;
+}
+
+
+function deleteAttendee(id)
+{
+  jsDeleteItem(id, "attendee", "attendees", function () { refreshDispatchCC("Attendees"); }, "attendee");
+}
+
+
+function updateAttendee(id)
+{
+  var args = jsMakePostArgs({"name":1,"groups":1,"oneliner":1,"email":1,"reghost":1}, "at", id);
+
+  var msuccess = function(txt)
+  {
+    activateAttendee(-1);
+  }
+
+  if (args != "")
+    jsSendPOSTRequest("action=update&type=attendees&id="+id+"&"+args, msuccess);
+}
+
+
+function activateAttendee(id)
+{
+  var msuccess1 = function(txt)
+  {
+    var nitem = document.getElementById("attendee"+prevAttendee);
+    if (nitem)
+    {
+      nitem.innerHTML = txt;
+      nitem.className = activeAttendeeTmp;
+    }
+  }
+
+  var msuccess2 = function(txt)
+  {
+    var nitem = document.getElementById("attendee"+activeAttendee);
+    if (nitem)
+    {
+      nitem.innerHTML = txt;
+      activeAttendeeTmp = nitem.className;
+      nitem.className += " active";
+      activeAttendee = id;
+    }
+  }
+
+  if (activeAttendee != id)
+  {
+    prevAttendee = activeAttendee;
+    activeAttendee = id;
+
+    if (prevAttendee != -1)
+      jsSendPOSTRequest("action=get&type=attendee&id="+prevAttendee+"&edit=0", msuccess1);
+
+    if (activeAttendee != -1)
+      jsSendPOSTRequest("action=get&type=attendee&id="+activeAttendee+"&edit=1", msuccess2);
+  }
+}
+
+
+//
+// Compo management
+//
+function addCompo()
+{
+  var args = jsMakePostArgs({"name":1, "description":1}, "nc", "");
+
+  var msuccess = function(txt)
+  {
+    setTimeout(function () { refreshDispatchCC("Compos"); }, 50);
+  }
+
+  if (args != "")
+    jsSendPOSTRequest("action=add&type=compo&"+args, msuccess);
+  return false;
+}
+
+
+function updateCompoType(id)
+{
+  var msuccess = function(txt)
+  {
+    jsRefreshItems("compo"+id, "compo", "&id="+id);
+  }
+
+  jsSendPOSTRequest("action=update&type=compotype&id="+id+"&ctype="+jsGetValue("cotype"+id+"Sel", 4), msuccess);
+}
+
+
+function updateCompoVoting(id)
+{
+  var msuccess = function(txt)
+  {
+    jsRefreshItems("covoting"+id, "compovoting", "&id="+id);
+  }
+
+  jsSendPOSTRequest("action=update&type=compovoting&id="+id+"&voting="+jsGetValue("covotingbutton"+id, 3), msuccess);
+}
+
+
+function updateCompo(id)
+{
+  var args = jsMakePostArgs({"name":1, "description":1, "notes":1, "visible":3, "voting":3, "show_authors":3, "cpath":1, "preview_type":4}, "co", id, true);
+
+  var msuccess = function(txt)
+  {
+    jsRefreshItems("compo"+id, "compo", "&id="+id);
+  }
+
+  if (args != "")
+    jsSendPOSTRequest("action=update&type=compo&id="+id+"&"+args, msuccess);
+}
+
+
+function deleteCompo(id)
+{
+  jsDeleteItem(id, "compo", "compo", function () { refreshDispatchCC("Compos"); }, 0, 
+    "Are you ABSOLUTELY sure you want to delete compo #"+id+"? "+
+    "This will delete all votes AND entries related to it!");
+}
+
+
+//
+// Entry management
+//
+function refreshEntryCompos()
+{
+  setTimeout(function () { refreshDispatchCC("Entries"); }, 50);
+}
+
+
+function activateEntry(id, force)
+{
+  var msuccess1 = function(txt)
+  {
+    var nitem = document.getElementById("entry"+prevEntry);
+    if (nitem)
+    {
+      nitem.innerHTML = txt;
+      nitem.className = activeEntryTmp;
+    }
+  }
+
+  var msuccess2 = function(txt)
+  {
+    var nitem = document.getElementById("entry"+activeEntry);
+    if (nitem)
+    {
+      nitem.innerHTML = txt;
+      activeEntryTmp = nitem.className;
+      nitem.className += " active";
+      activeEntry = id;
+    }
+  }
+
+  if (activeEntry != id || force)
+  {
+    jsCancelUploadCBS();
+
+    prevEntry = activeEntry;
+    activeEntry = id;
+
+    if (prevEntry != -1)
+      jsSendPOSTRequest("action=get&type=entry&id="+prevEntry+"&edit=0", msuccess1);
+
+    if (activeEntry != -1)
+      jsSendPOSTRequest("action=get&type=entry&id="+activeEntry+"&edit=1", msuccess2);
+  }
+}
+
+
+function addEntry(id)
+{
+  var args = jsMakePostArgs({"name":1, "author":1, "filename":1, "info":1, "notes":1, "evalue":2}, "ne", id, true);
+
+  jsCancelUploadCBS();
+
+  var msuccess = function(txt)
+  {
+    setTimeout(function (q) { refreshDispatchCM(qid); }, 50, id);
+    refreshEntryCompos();
+  }
+
+  if (args != "")
+    jsSendPOSTRequest("action=add&type=entry&compo_id="+id+"&"+args, msuccess);
+
+  return false;
+}
+
+
+function updateEntry(cid, id, edit)
+{
+  var args = jsMakePostArgs({"name":1, "author":1, "filename":1, "info":1, "notes":1, "compo_id":4, "evalue":2, "preview_type":4}, "en", id, true);
+  var has_id = "compo_id" in lastPostArgs;
+  var compo_id = lastPostArgs["compo_id"];
+
+  jsCancelUploadCBS();
+
+  if (!("compo_id" in lastPostArgs))
+  {
+    args += "&compo_id=" + cid;
+    compo_id = cid;
+  }
+
+  var msuccess = function(txt)
+  {
+    if (cid != compo_id)
+    {
+      var nitem = document.getElementById("entry"+ id);
+      if (nitem)
+        nitem.style.display = "none";
+    }
+    else
+      activateEntry(edit ? id : -1, true);
+    refreshEntryCompos();
+  }
+
+  var mcb_ok = function(data)
+  {
+    jsSendPOSTRequest("action=update&type=entry&id="+id+"&edit="+edit+"&"+args, msuccess);
+    refreshEntryCompos();
+  }
+
+  var mcb_cancel = function(data)
+  {
+    activeEntry = -1;
+  }
+
+  if (args != "")
+  {
+    if (cid != compo_id)
+    {
+      jsConfirmBox("Are you sure you want to change entry #"+id+
+        "'s compo from "+cid+" to "+compo_id+"?", mcb_ok, mcb_cancel);
+    }
+    else
+      mcb_ok();
+  }
+}
+
+
+function deleteEntry(cid, id)
+{
+  jsCancelUploadCBS();
+  jsDeleteItem(id, "entry", "entries", function() { refreshDispatchCM(cid); refreshEntryCompos(); }, "entry");
+}
+
+
+//
+// Votekey management
+//
+function voteKeyInfoRefresh()
+{
+  jsRefreshItems("vkeyInfo", "votekey", "info");
+}
+
+
+function voteKeyRefresh(id)
+{
+  var msuccess2 = function(txt)
+  {
+    var nitem = document.getElementById("vkey"+id);
+    if (nitem)
+      nitem.className = txt;
+    voteKeyInfoRefresh();
+  }
+
+  var msuccess1 = function(txt)
+  {
+    var nitem = document.getElementById("vkey"+id);
+    if (nitem)
+      nitem.innerHTML = txt;
+    voteKeyInfoRefresh();
+  }
+
+  jsSendPOSTRequest("action=get&type=votekey&id="+id, msuccess1);
+  jsSendPOSTRequest("action=get&type=votekeyclass&id="+id, msuccess2);
+}
+
+
+function voteKeyUpdate(id, type, args)
+{
+  var msuccess = function(txt)
+  {
+    voteKeyRefresh(id);
+    voteKeyInfoRefresh();
+  }
+
+  jsSendPOSTRequest("action=votekey&type="+type+"&id="+id+"&"+args, msuccess);
+}
+
+
+function voteKeySetActive(id)
+{
+  var args = jsMakePostArgs({"active":3}, "vk", id);
+
+  var mcb_ok = function (data)
+  {
+    voteKeyUpdate(id, "active", args);
+  }
+
+  var mcb_cancel = function (data)
+  {
+    voteKeyRefresh(id);
+  }
+  
+  if (lastPostArgs["active"] == 0)
+    jsConfirmBox("Are you sure you want to deactivate vote key #"+id+"?", mcb_ok, mcb_cancel, 0);
+  else
+    mcb_ok(0);
+}
+
+
+function voteKeyAssign(id, mode)
+{
+  var args = jsMakePostArgs({"key_id":2}, "vk", id);
+
+  var mcb_ok = function (data)
+  {
+    voteKeyUpdate(id, (mode ? "assign" : "clear"), args);
+  }
+  
+  if (mode == 0)
+    jsConfirmBox("Are you sure you want to clear vote key assign #"+id+"?", mcb_ok, 0, 0);
+  else
+    mcb_ok(0);
+}
+
+
+//
+// Misc functions
+//
+function showScreenCmd(str)
+{
+  jsSendPOSTRequest("action=screencmd&cmd="+str);
+}
+
+
+function performSystemCheck()
+{
+  jsSendPOSTRequest("action=check", jsMessageBox);
+}
+
+
+function generateEntryPositions(id, patch)
+{
+  var msuccess = function (data)
+  {
+    refreshDispatchCM(id);
+    refreshCurrEntryData();
+    refreshCurrEntryListData();
+  }
+
+  var mcb_ok = function (data)
+  {
+    if (id == 0)
+      jsSendPOSTRequest("action=randomize&type=all&patch="+patch, msuccess);
+    else
+      jsSendPOSTRequest("action=randomize&type=compo&id="+id+"&patch="+patch, msuccess);
+  }
+  
+  if (patch == 0)
+  {
+    jsConfirmBox("Are you <b>ABSOLUTELY CERTAIN</b> you want to delete and regenerate entry show positions "+
+      (id == 0 ? "for ALL compos" : "for this compo") +"? "+
+      "<b>This will completely annihilate current show position numbers! AND FUCK UP YOUR EXPORTED FILE ORDER (ask ccr to know what that means.)</b>", mcb_ok, 0, 0);
+  }
+  else
+    mcb_ok(0);
+}
+
+
+//
+// Competition control
+//
+function setShowMode(mode)
+{
+  jsSendPOSTRequest("action=ctrl&type=setShowMode&mode="+mode);
+}
+
+
+function refreshCurrEntryData()
+{
+  jsRefreshItems("ctrlCurrCompoData", "infoCurrCompoData", "");
+}
+
+
+function refreshCurrEntryListData()
+{
+  jsRefreshItems("ctrlEntryList", "infoCurrEntryList", "");
+  refreshCurrEntryData();
+}
+
+
+function activateCompo()
+{
+  jsSendPOSTRequest("action=ctrl&type=setCompoID&id="+jsGetValue("ctrlCompoListSel", 4), refreshCurrEntryListData);
+}
+
+
+function setSelectedEntry()
+{
+  jsSendPOSTRequest("action=ctrl&type=setEntry&index="+jsGetValue("ctrlEntryListSel", 4), refreshCurrEntryData);
+}
+
+
+function switchEntry(dir)
+{
+  jsSendPOSTRequest("action=ctrl&type="+ (dir < 0 ? "prevEntry" : "nextEntry"), refreshCurrEntryListData);
+}
+
+
+//
+// Rotation list editing and handling
+//
+function refreshRotationListSel()
+{
+  jsRefreshItems("ctrlRotationListsSel", "infoRotationLists", "");
+}
+
+
+function refreshRotationListEdit(id)
+{
+  jsRefreshItems("ctrlRotationListEdit", "infoRotationListEdit", "&id="+id+"&full=0");
+  refreshRotationListSel();
+}
+
+
+function refreshActiveRotationList()
+{
+  jsRefreshItems("ctrlActiveRotationList", "infoActiveRotationList", "");
+}
+
+
+function setRotateDuration()
+{
+  var duration = jsGetValue("ctrlRotSlideDuration", 2);
+  jsSendPOSTRequest("action=ctrl&type=setRotateDuration&duration="+duration);
+}
+
+
+function setActiveRotationList()
+{
+  var id = jsGetValue("ctrlRotationListsSel", 4);
+  if (id > 0)
+    jsSendPOSTRequest("action=ctrl&type=setActiveRotationList&id="+id, refreshActiveRotationList);
+  else
+    jsErrorMessageBox("No rotation list selected?");
+}
+
+
+function updateRotationList(id)
+{
+  var name = jsGetValue("ctrlEDRotationListName", 1);
+  jsSendPOSTRequest("action=ctrl&type=updateRotationList&id="+id+"&name="+name, refreshRotationListSel);
+}
+
+
+function moveRotationListSlide(list_id, dir)
+{
+  var msuccess = function(txt)
+  {
+    refreshRotationListEdit(list_id);
+  }
+
+  var slide = jsGetValue("ctrlEDRotationListSel", 4);
+  if (slide)
+  {
+    var str = slide.split("_");
+    jsSendPOSTRequest("action=ctrl&type=moveRotationListSlide&list_id="+list_id+
+      "&slide_id="+parseInt(str[0])+"&order_num="+parseInt(str[1])+"&dir="+dir, msuccess);
+  }
+  else
+    jsErrorMessageBox("No slide selected?");
+}
+
+
+function addRotationListSlide(list_id)
+{
+  var msuccess = function(txt)
+  {
+    refreshRotationListEdit(list_id);
+  }
+
+  var slide_id = jsGetValue("ctrlEDDisplaySlidesSel", 4);
+  if (slide_id > 0)
+    jsSendPOSTRequest("action=ctrl&type=addRotationListSlide&list_id="+list_id+"&slide_id="+slide_id, msuccess);
+  else
+    jsErrorMessageBox("No slide selected?");
+}
+
+
+function removeRotationListSlide(list_id)
+{
+  var msuccess = function(txt)
+  {
+    refreshRotationListEdit(list_id);
+  }
+
+  var slide = jsGetValue("ctrlEDRotationListSel", 4);
+  if (slide)
+  {
+    var str = slide.split("_");
+    jsSendPOSTRequest("action=ctrl&type=removeRotationListSlide&list_id="+list_id+
+      "&slide_id="+parseInt(str[0])+"&order_num="+parseInt(str[1]), msuccess);
+  }
+  else
+    jsErrorMessageBox("No slide selected?");
+}
+
+
+function editRotationList()
+{
+  var id = jsGetValue("ctrlRotationListsSel", 4);
+  if (id > 0)
+    jsSendPOSTRequest("action=get&type=infoRotationListEdit&id="+id, jsOpenAdminPopup);
+  else
+    jsErrorMessageBox("No rotation list selected?");
+}
+
+
+function newRotationList()
+{
+  var msuccess = function(txt)
+  {
+    refreshRotationListSel();
+    jsOpenAdminPopup(txt);
+  }
+
+  jsSendPOSTRequest("action=ctrl&type=newRotationList", msuccess);
+}
+
+
+function deleteRotationList()
+{
+  var mcb_ok = function(txt)
+  {
+    jsSendPOSTRequest("action=ctrl&type=deleteRotationList&id="+id, refreshRotationListSel);
+  }
+
+  var id = jsGetValue("ctrlRotationListsSel", 4);
+  if (id > 0)
+  {
+    jsConfirmBox("Are you <b>sure</b> you want to delete rotation list #"+id+"?",
+      mcb_ok, 0, 0);
+  }
+  else
+    jsErrorMessageBox("No rotation list selected?");
+}
+
+
+//
+// Display slide editing and handling
+//
+function refreshDisplaySlideListSel()
+{
+  jsRefreshItems("ctrlDisplaySlidesSel", "infoDisplaySlides", "");
+}
+
+
+function updateDisplaySlide(id)
+{
+  var vtitle = jsGetValue("ctrlDisplaySlideTitle", 1);
+  var vtext = jsGetValue("ctrlDisplaySlideText", 1);
+  jsSendPOSTRequest("action=ctrl&type=updateDisplaySlide&id="+id+"&title="+vtitle+"&text="+vtext, refreshDisplaySlideListSel);
+  jsCloseAdminPopup();
+  return false;
+}
+
+
+function editDisplaySlide()
+{
+  var id = jsGetValue("ctrlDisplaySlidesSel", 4);
+  if (id > 0)
+    jsSendPOSTRequest("action=get&type=infoDisplaySlideEdit&id="+id, jsOpenAdminPopup);
+  else
+    jsErrorMessageBox("No display slide selected?");
+}
+
+
+function copyDisplaySlide()
+{
+  var msuccess = function(txt)
+  {
+    refreshDisplaySlideListSel();
+    jsOpenAdminPopup(txt);
+  }
+
+  var id = jsGetValue("ctrlDisplaySlidesSel", 4);
+  if (id > 0)
+    jsSendPOSTRequest("action=ctrl&type=copyDisplaySlide&id="+id, msuccess);
+  else
+    jsErrorMessageBox("No display slide selected?");
+}
+
+
+function newDisplaySlide()
+{
+  var msuccess = function(txt)
+  {
+    refreshDisplaySlideListSel();
+    jsOpenAdminPopup(txt);
+  }
+
+  jsSendPOSTRequest("action=ctrl&type=newDisplaySlide", msuccess);
+}
+
+
+function deleteDisplaySlide()
+{
+  var msuccess = function(txt)
+  {
+    refreshDisplaySlideListSel();
+    refreshRotationListSel();
+  }
+
+  var mcb_ok = function(txt)
+  {
+    jsSendPOSTRequest("action=ctrl&type=deleteDisplaySlide&id="+id, msuccess);
+  }
+
+  var id = jsGetValue("ctrlDisplaySlidesSel", 4);
+  if (id > 0)
+  {
+    jsConfirmBox("Are you <b>sure</b> you want to delete slide list #"+id+"?",
+      mcb_ok, 0, 0);
+  }
+  else
+    jsErrorMessageBox("No display slide selected?");
+}
+
+
+function activateTempSlide()
+{
+  var id = jsGetValue("ctrlDisplaySlidesSel", 4);
+  var duration = jsGetValue("ctrlTempSlideDuration", 2);
+  if (id > 0)
+    jsSendPOSTRequest("action=ctrl&type=setTempSlide&id="+id+"&duration="+duration, jsMessageBox);
+  else
+    jsErrorMessageBox("No slide selected?");
+}
+
+
+function skipToNextSlide()
+{
+  jsSendPOSTRequest("action=ctrl&type=skipToNextSlide");
+}