includes/clientside/static/ajax.js
author Dan
Mon, 07 Dec 2009 15:12:22 -0500
changeset 1153 124ebcbb6a31
parent 1125 367768040a61
child 1226 de56132c008d
permissions -rw-r--r--
PostgreSQL: bugfix in ACL related SQL query

/*
 * AJAX applets
 */
 
window.ajaxReset = function()
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  var ns_id = strToPageID(physical_title);
  if ( ns_id[1] == 'Special' || ns_id[1] == 'Admin' )
    return false;
  enableUnload();
  setAjaxLoading();
  var redir = ( disable_redirect ) ? '&redirect=no' : '';
  ajaxGet(append_sid(scriptPath + '/ajax.php?title=' + physical_title +'&_mode=getpage&noheaders' + redir), function(ajax) {
    // Allow for 404 here, it's generated by the "page not found" error message
    // (even with noheaders specified, probably should be fixed)
    if ( ajax.readyState == 4 && ( ajax.status == 200 || ajax.status == 404 ) ) {
      unsetAjaxLoading();
      document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
      selectButtonMajor('article');
      unselectAllButtonsMinor();
      // if we're on a userpage, call the onload function to rebuild the tabs
      if ( typeof(userpage_onload) == 'function' )
      {
        window.userpage_blocks = [];
        userpage_onload();
      }
    }
  });
}

// Miscellaneous AJAX applets

window.ajaxProtect = function(existing_level)
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  
  // touch this variable to allow it to be used in child functions
  void(existing_level);
  
  // require re-auth
  if ( auth_level <= USER_LEVEL_MEMBER )
  {
    load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
    ajaxDynamicReauth(function(key)
      {
        ajaxProtect(existing_level);
      }, user_level);
    
    return false;
  }
  
  load_component(['messagebox', 'jquery', 'jquery-ui', 'l10n', 'fadefilter', 'flyin']);
  
  // preload language
  $lang.get('meta_meta');
  
  var mp = miniPrompt(function(parent)
    {
      var icon_full = gen_sprite_html(cdnPath + '/images/protect-icons.png', 22, 22, 0, 0);
      var icon_semi = gen_sprite_html(cdnPath + '/images/protect-icons.png', 22, 22, 22, 0);
      var icon_none = gen_sprite_html(cdnPath + '/images/protect-icons.png', 22, 22, 44, 0);
      
      $(parent).append('<h3>' + $lang.get('onpage_protect_heading') + '</h3>');
      $(parent).append('<p>' + $lang.get('onpage_protect_msg_select_level') + '</p>');
      
      $(parent).append('<div class="protectlevel"><label><input type="radio" id="protect_level_1" name="protect_level" /> ' + icon_full + ' ' + $lang.get('onpage_protect_btn_full') + '</label></div>');
      $(parent).append('<div class="protectlevel_hint" id="protect_level_1_hint">' + $lang.get('onpage_protect_btn_full_hint') + '</div>');
      $(parent).append('<div class="protectlevel"><label><input type="radio" id="protect_level_2" name="protect_level" /> ' + icon_semi + ' ' + $lang.get('onpage_protect_btn_semi') + '</label></div>');
      $(parent).append('<div class="protectlevel_hint" id="protect_level_2_hint">' + $lang.get('onpage_protect_btn_semi_hint') + '</div>');
      $(parent).append('<div class="protectlevel"><label><input type="radio" id="protect_level_0" name="protect_level" /> ' + icon_none + ' ' + $lang.get('onpage_protect_btn_none') + '</label></div>');
      $(parent).append('<div class="protectlevel_hint" id="protect_level_0_hint">' + $lang.get('onpage_protect_btn_none_hint') + '</div>');
      
      $(parent).append('<table class="protectreason"><tr><td valign="top">' + $lang.get('onpage_protect_lbl_reason') + '</td><td><input id="protect_reason" size="30" type="text" /><br /><small>' + $lang.get('onpage_protect_lbl_reason_hint') + '</small></td></tr></table>');
      
      $(parent).append('<p class="buttons"><a class="submitbutton abutton abutton_green" style="font-weight: bold;" href="#" onclick="ajaxProtectSubmit(this); return false;">' + $lang.get('onpage_protect_btn_submit') + '</a> <a class="submitbutton abutton" href="#" onclick="miniPromptDestroy(this); return false;">' + $lang.get('etc_cancel') + '</a></p>');
      
      $('.protectlevel', parent).css('line-height', '22px');
      $('h3', parent).css('text-align', 'center');
      $('.protectlevel_hint', parent)
        .css('font-size', 'smaller')
        .css('margin-left', '52px')
        .hide();
      $('p.buttons', parent).css('margin-top', '15px').css('text-align', 'center');
      
      if ( typeof(existing_level) == 'number' )
      {
        $('#protect_level_' + existing_level, parent).attr('checked', 'checked');
        $('#protect_level_' + existing_level + '_hint', parent).show();
        $('#protect_level_' + existing_level, parent).parent().append(' <small><span style="color: #050; background-color: #eee; padding: 0 4px;">' + $lang.get('onpage_protect_lbl_current') + '</span></small>');
      }
      
      $('input:radio', parent).click(function()
        {
          var mp = miniPromptGetParent(this);
          $('.protectlevel_hint:visible', mp).hide('blind', 150);
          $('#' + this.id + '_hint').show('blind', 150);
          $('#protect_reason').focus();
        });
      $('input:text', parent).keyup(function(e)
        {
          if ( e.keyCode == 13 )
            ajaxProtectSubmit(this);
        });
    });
}

window.ajaxProtectSubmit = function(el)
{
  var mp = miniPromptGetParent(el);
  
  var reason = trim($('#protect_reason', mp).attr('value'));
  if ( reason == '' )
  {
    var oldbg = $('#protect_reason').css('background-color');
    if ( jQuery.fx.off )
    {
      $('#protect_reason').css('background-color', '#a00');
      setTimeout(function()
        {
          $('#protect_reason').css('background-color', oldbg);
        }, 1000);
    }
    else
    {
      $('#protect_reason').css('background-color', '#a00').animate({ backgroundColor: oldbg }, 1000);
    }
    return false;
  }
  
  var level = 0;
  if ( $('#protect_level_1', mp).attr('checked') )
    level = 1;
  if ( $('#protect_level_2', mp).attr('checked') )
    level = 2;
  
  var whitey = whiteOutMiniPrompt(mp);
  $.post(stdAjaxPrefix + '&_mode=protect', { level: level, reason: reason }, function(response, statustext)
    {
      if ( response.success )
      {
        whiteOutReportSuccess(whitey);
        // update protect button
        var btn = $('#tb_ajax_protect_btn').get(0);
        btn.level = level;
        btn.setAttribute('onclick', null);
        btn.onclick = null;
        $(btn).click(function()
          {
            ajaxProtect(this.level);
            return false;
          });
        var status = '';
        switch(level)
        {
          case 1: status = $lang.get('onpage_btn_protect_on'); break;
          case 0: status = $lang.get('onpage_btn_protect_off'); break;
          case 2: status = $lang.get('onpage_btn_protect_semi'); break;
        }
        $('#tb_ajax_protect_status').text(status);
      }
      else
      {
        whiteOutReportFailure(whitey);
        alert($lang.get('page_err_' + response.error));
      }
    }, 'json');
}

window.ajaxRename = function()
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  
  // updated - 1.1.4 to use miniPrompt
  load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
  miniPrompt(ajaxRenameConstructDialog);
}

var ajaxRenameConstructDialog = function(div)
{
  // title
  var heading = document.createElement('h3');
  heading.appendChild(document.createTextNode($lang.get('ajax_rename_prompt_short')));
  div.appendChild(heading);
  
  // form
  var form = document.createElement('form');
  form.action = 'javascript:void(0);';
  
  // box
  var box = document.createElement('input');
  box.size = '43';
  box.style.width = '100%';
  form.appendChild(box);
  div.appendChild(form);
  
  // notice
  var notice = document.createElement('small');
  notice.appendChild(document.createTextNode($lang.get('ajax_rename_notice')));
  div.appendChild(notice);
  
  // button area
  var btndiv = document.createElement('div');
  btndiv.className = 'mp-buttons';
  
  // buttons
  var btn_submit = document.createElement('a');
  btn_submit.href = '#';
  btn_submit.appendChild(document.createTextNode($lang.get('etc_go')));
  btn_submit.className = 'abutton abutton_green';
  
  var btn_cancel = document.createElement('a');
  btn_cancel.href = '#';
  btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
  btn_cancel.className = 'abutton';
  
  btndiv.appendChild(btn_submit);
  btndiv.appendChild(document.createTextNode(' | '));
  btndiv.appendChild(btn_cancel);
  div.appendChild(btndiv);
  
  // events
  btn_submit.onclick = function()
  {
    ajaxRenameSubmit(this);
    return false;
  }
  btn_cancel.onclick = function()
  {
    miniPromptDestroy(this);
    return false;
  }
  form.onsubmit = function()
  {
    ajaxRenameSubmit(this);
    return false;
  }
  
  setTimeout(function()
    {
      box.focus();
    }, ( aclDisableTransitionFX ? 200 : 750 ));
}

window.ajaxRenameSubmit = function(obj)
{
  var box = miniPromptGetParent(obj);
  if ( !box )
    return false;
  
  var input = box.getElementsByTagName('input')[0];
  if ( !input )
    return false;
    
  var newname = input.value;
  newname = trim(newname);
  
  if ( newname.length < 1 )
  {
    alert($lang.get('ajax_rename_too_short'));
    return false;
  }
  
  if ( !newname )
  {
    return false;
  }
  
  var innerBox = getElementsByClassName(box, 'div', 'mp-body')[0];
  var whiteout = whiteOutElement(innerBox);
  whiteout.style.width = ( $dynano(whiteout).Width() - 78 ) + 'px';
  whiteout.style.left = ( $dynano(whiteout).Left() + 44 ) + 'px';
  
  ajaxPost(stdAjaxPrefix + '&_mode=rename', 'newtitle=' + ajaxEscape(newname), function(ajax)
    {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        whiteout.parentNode.removeChild(whiteout);
        var response = String(ajax.responseText);
        if ( !check_json_response(response) )
        {
          handle_invalid_json(response);
          return false;
        }
        response = parseJSON(response);
        if ( response.success )
        {
          miniPromptDestroy(box, true);
          ajaxRenameDoClientTransform(newname);
          new MessageBox( MB_OK|MB_ICONINFORMATION, $lang.get('ajax_rename_success_title'), $lang.get('ajax_rename_success_body', { page_name_new: newname }) );
          mb_previously_had_darkener = false;
        }
        else
        {
          var errmsg = $lang.get('page_err_' + response.error);
          alert(errmsg);
        }
      }
    }, true);
}

window.ajaxRenameDoClientTransform = function(newname)
{
  var obj = document.getElementById('h2PageName');
  if ( obj )
  {
    obj.firstChild.nodeValue = newname;
  }
  document.title = newname;
}

window.ajaxDeletePage = function()
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  
  // require re-auth
  if ( auth_level <= USER_LEVEL_MEMBER )
  {
    load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
    ajaxDynamicReauth(function(key)
      {
        ajaxDeletePage();
      }, user_level);
    
    return false;
  }
  
  load_component(['l10n', 'messagebox', 'jquery', 'jquery-ui', 'fadefilter', 'flyin']);
  
  // stage 1: prompt for reason and confirmation
  miniPrompt(function(parent)
    {
      // heading/title
      var h3 = document.createElement('h3');
      h3.appendChild(document.createTextNode($lang.get('ajax_delete_header')));
      parent.appendChild(h3);
      
      // "please enter your reason"
      var p1 = document.createElement('p');
      p1.appendChild(document.createTextNode($lang.get('ajax_delete_prompt_reason')));
      parent.appendChild(p1);
      
      // textbox + label thereof
      var p2 = document.createElement('p');
      var tb = document.createElement('input');
      var dl = document.createElement('label');
      
      tb.type = 'text';
      tb.size = '30';
      tb.onkeyup = function(e)
      {
        if ( e )
        if ( e.keyCode )
        if ( e.keyCode == 13 )
        {
          if ( ajaxDeletePageSubmit(this) )
          {
            miniPromptDestroy(this);
          }
        }
        else if ( e.keyCode == 27 )
        {
          miniPromptDestroy(this);
        }
      }
      
      dl.appendChild(document.createTextNode($lang.get('ajax_delete_lbl_reason') + ' '));
      dl.appendChild(tb);
      p2.appendChild(dl);
      parent.appendChild(p2);
      
      // notice underneath
      var p3 = document.createElement('p');
      p3.style.fontSize = 'smaller';
      p3.appendChild(document.createTextNode($lang.get('ajax_delete_msg_confirm')));
      parent.appendChild(p3);
      
      // confirmation + submit/cancel (structure)
      var divleft  = document.createElement('div');
      var divright = document.createElement('div');
      var divclear = document.createElement('div');
      
      divleft.style.cssFloat = 'left';
      divleft.style.styleFloat = 'left';
      
      divright.style.cssFloat = 'right';
      divright.style.styleFloat = 'right';
      
      divclear.style.clear = 'both';
      
      parent.appendChild(divleft);
      parent.appendChild(divright);
      parent.appendChild(divclear);
      
      // confirmation + submit/cancel (controls)
      var cb = document.createElement('input');
      var cl = document.createElement('label');
      
      cb.type = 'checkbox';
      cb.checked = false;
      
      // a bit of a hack here, doesn't seem to work in fx3
      cb.onblur = function(e)
      {
        var parent = this.parentNode.parentNode.parentNode;
        var submitter = parent.getElementsByTagName('a')[0];
        if ( submitter )
          submitter.focus();
      }
      
      cl.appendChild(cb);
      cl.appendChild(document.createTextNode(' ' + $lang.get('ajax_delete_lbl_confirm')));
      divleft.appendChild(cl);
      
      var btn_submit = document.createElement('a');
      btn_submit.className = 'abutton abutton_red';
      btn_submit.href = '#';
      btn_submit.appendChild(document.createTextNode($lang.get('ajax_delete_btn_delete')));
      btn_submit.onclick = function()
      {
        if ( ajaxDeletePageSubmit(this) )
        {
          miniPromptDestroy(this);
        }
        return false;
      }
      
      var btn_cancel = document.createElement('a');
      btn_cancel.className = 'abutton';
      btn_cancel.href = '#';
      btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
      btn_cancel.onclick = function()
      {
        miniPromptDestroy(this);
        return false;
      }
      
      divright.appendChild(btn_submit);
      divright.appendChild(document.createTextNode(' '));
      divright.appendChild(btn_cancel);
      
      var timeout = ( aclDisableTransitionFX ) ? 10 : 1000;
      setTimeout(function()
        {
          tb.focus();
        }, timeout);
    });
}

window.ajaxDeletePageSubmit = function(prompt_obj)
{
  prompt_obj = miniPromptGetParent(prompt_obj).childNodes[1];
  var inputs = prompt_obj.getElementsByTagName('input');
  var reason = inputs[0];
  var confirm = inputs[1];
  
  if ( trim(reason.value) == '' )
  {
    // flash the background of the reason entry
    $(reason.parentNode).effect("highlight", {}, 1000);
    return false;
  }
  
  if ( !confirm.checked )
  {
    // flash the background of the confirm checkbox
    $(confirm.parentNode).effect("highlight", {}, 1000);
    return false;
  }
  
  prompt_obj.innerHTML = '<img alt="loading" style="display: block; margin: 0 auto;" src="' + cdnPath + '/images/loading-big.gif" />';
  
  // tenemos la confirmación y la razón - borre la página.
  setAjaxLoading();
  ajaxPost(stdAjaxPrefix + '&_mode=deletepage', 'reason=' + ajaxEscape(trim(reason.value)), function(ajax)
    {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        unsetAjaxLoading();
        
        // show the response in the same prompt window
        prompt_obj.style.textAlign = 'center';
        prompt_obj.innerHTML = '';
        
        var p1 = document.createElement('div');
        p1.style.marginBottom = '15px';
        p1.appendChild(document.createTextNode(ajax.responseText));
        prompt_obj.appendChild(p1);
        
        var p2 = document.createElement('p');
        var a = document.createElement('a');
        a.className = 'abutton';
        a.href = '#';
        a.appendChild(document.createTextNode($lang.get('etc_close')));
        a.onclick = function()
        {
          miniPromptDestroy(this);
          window.location.reload();
          return false;
        }
        p2.appendChild(a);
        prompt_obj.appendChild(a);
        
        a.focus();
      }
    });
  
  return true;
}

window.ajaxDelVote = function()
{
  load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
  
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  miniPromptMessage({
      title: $lang.get('ajax_delvote_confirm_title'),
      message: $lang.get('ajax_delvote_confirm_body'),
      buttons: [
        {
          text: $lang.get('ajax_delvote_btn_submit'),
          color: 'red',
          style: {
            fontWeight: 'bold'
          },
          onclick: function(e)
          {
            miniPromptDestroy(this);
            setAjaxLoading();
            ajaxGet(stdAjaxPrefix+'&_mode=delvote', function(ajax) {
              if ( ajax.readyState == 4 && ajax.status == 200 ) {
                unsetAjaxLoading();
                alert(ajax.responseText);
              }
            }, true);
          }
        },
        {
          text: $lang.get('etc_cancel'),
          onclick: function(e)
          {
            miniPromptDestroy(this);
          }
        }
      ]
    });
}

window.ajaxResetDelVotes = function()
{
  load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
  
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  miniPromptMessage({
      title: $lang.get('ajax_delvote_reset_confirm_title'),
      message: $lang.get('ajax_delvote_reset_confirm_body'),
      buttons: [
        {
          text: $lang.get('ajax_delvote_reset_btn_submit'),
          color: 'red',
          style: {
            fontWeight: 'bold'
          },
          onclick: function(e)
          {
            var box = miniPromptGetParent(this);
            var whitey = whiteOutMiniPrompt(box);
            ajaxGet(stdAjaxPrefix+'&_mode=resetdelvotes', function(ajax) {
              if ( ajax.readyState == 4 && ajax.status == 200 ) {
                whiteOutReportSuccess(whitey);
                
                item = document.getElementById('mdgDeleteVoteNoticeBox');
                if(item)
                {
                  opacity('mdgDeleteVoteNoticeBox', 100, 0, 1000);
                  setTimeout("document.getElementById('mdgDeleteVoteNoticeBox').style.display = 'none';", 1000);
                }
              }
            }, true);
          }
        },
        {
          text: $lang.get('etc_cancel'),
          onclick: function(e)
          {
            miniPromptDestroy(this);
          }
        }
      ]
    });
}

// Editing/saving category information
// This was not easy to write, I hope enjoy it, and dang I swear I'm gonna
// find someone to work on just the Javascript part of Enano...

window.ajaxCatEdit = function()
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  setAjaxLoading();
  ajaxGet(stdAjaxPrefix+'&_mode=catedit', function(ajax) {
    if ( ajax.readyState == 4 && ajax.status == 200 ) {
      unsetAjaxLoading();
      edit_open = false;
      eval(ajax.responseText);
    }
  });
}

window.ajaxCatSave = function()
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  if(!catlist)
  {
    alert('Var catlist has no properties');
    return;
  }
  query='';
  for(i=0;i<catlist.length;i++)
  {
    var s = ( document.forms.mdgCatForm['mdgCat_' + catlist[i]]['checked'] ) ? true : false;
    if(s) query = query + '&' + catlist[i] + '=true';
  }
  setAjaxLoading();
  query = query.substring(1, query.length);
  ajaxPost(stdAjaxPrefix+'&_mode=catsave', query, function(ajax) {
    if ( ajax.readyState == 4 && ajax.status == 200 ) {
      unsetAjaxLoading();
      edit_open = false;
      if(ajax.responseText != 'GOOD') alert(ajax.responseText);
      ajaxReset();
    }
  });
}

// History stuff

window.ajaxHistory = function()
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  setAjaxLoading();
  ajaxGet(stdAjaxPrefix+'&_mode=histlist', function(ajax) {
    if ( ajax.readyState == 4 && ajax.status == 200 ) {
      unsetAjaxLoading();
      edit_open = false;
      selectButtonMajor('article');
      selectButtonMinor('history');
      document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
      buildDiffList();
    }
  });
}

window.ajaxHistView = function(oldid, ttl) {
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  if(!ttl) ttl=title;
  setAjaxLoading();
  ajaxGet(append_sid(scriptPath+'/ajax.php?title='+ttl+'&_mode=getpage&oldid='+oldid), function(ajax) {
    if ( ajax.readyState == 4 && ajax.status == 200 ) {
      unsetAjaxLoading();
      edit_open = false;
      document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
    }
  });
}

window.ajaxRollback = function(id) {
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  setAjaxLoading();
  ajaxGet(stdAjaxPrefix+'&_mode=rollback&id='+id, function(ajax) {
    if ( ajax.readyState == 4 && ajax.status == 200 ) {
      unsetAjaxLoading();
      
      var response = String(ajax.responseText + '');
      if ( !check_json_response(response) )
      {
        handle_invalid_json(response);
        return false;
      }
      
      response = parseJSON(response);
      if ( response.success )
      {
        alert( $lang.get('page_msg_rb_success_' + response.action, { dateline: response.dateline }) )
      }
      else
      {
        if ( response.action )
        {
          alert( $lang.get('page_err_' + response.error, { action: response.action }) );
        }
        else
        {
          alert( $lang.get('page_err_' + response.error) );
        }
      }
    }
  });
}

window.ajaxClearLogs = function()
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  
  // require re-auth
  if ( auth_level <= USER_LEVEL_MEMBER )
  {
    load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
    ajaxDynamicReauth(function(key)
      {
        ajaxClearLogs();
      }, user_level);
    
    return false;
  }
  
  load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
  
  miniPromptMessage({
      title: $lang.get('ajax_clearlogs_confirm_title'),
      message: $lang.get('ajax_clearlogs_confirm_body'),
      buttons: [
        {
          text: $lang.get('ajax_clearlogs_btn_submit'),
          color: 'red',
          style: {
            fontWeight: 'bold'
          },
          onclick: function(e)
          {
            miniPromptDestroy(this);
            setAjaxLoading();
            ajaxGet(stdAjaxPrefix+'&_mode=flushlogs', function(ajax) {
              if ( ajax.readyState == 4 && ajax.status == 200 ) {
                unsetAjaxLoading();
                alert(ajax.responseText);
                window.location.reload();
              }
            });
          }
        },
        {
          text: $lang.get('etc_cancel'),
          onclick: function(e)
          {
            miniPromptDestroy(this);
          }
        }
      ]
    });
}

window.buildDiffList = function()
{
  arrDiff1Buttons = getElementsByClassName(document, 'input', 'clsDiff1Radio');
  arrDiff2Buttons = getElementsByClassName(document, 'input', 'clsDiff2Radio');
  var len = arrDiff1Buttons.length;
  if ( len < 1 )
    return false;
  timelist = new Array();
  for ( var i = 0; i < len; i++ )
  {
    timelist.push( arrDiff2Buttons[i].id.substr(6) );
  }
  timelist.push( arrDiff1Buttons[len-1].id.substr(6) );
  delete(timelist.toJSONString);
  for ( var i = 1; i < timelist.length-1; i++ )
  {
    if ( i >= timelist.length ) break;
    arrDiff2Buttons[i].style.display = 'none';
  }
}

window.selectDiff1Button = function(obj)
{
  var this_time = obj.id.substr(6);
  var index = parseInt(in_array(this_time, timelist));
  for ( var i = 0; i < timelist.length - 1; i++ )
  {
    if ( i < timelist.length - 1 )
    {
      var state = ( i < index ) ? 'inline' : 'none';
      var id = 'diff2_' + timelist[i];
      document.getElementById(id).style.display = state;
      
      // alert("Debug:\nIndex: "+index+"\nState: "+state+"\ni: "+i);
    }
  }
}

window.selectDiff2Button = function(obj)
{
  var this_time = obj.id.substr(6);
  var index = parseInt(in_array(this_time, timelist));
  for ( var i = 1; i < timelist.length; i++ )
  {
    if ( i < timelist.length - 1 )
    {
      var state = ( i > index ) ? 'inline' : 'none';
      var id = 'diff1_' + timelist[i];
      document.getElementById(id).style.display = state;
      
      // alert("Debug:\nIndex: "+index+"\nState: "+state+"\ni: "+i);
    }
  }
}

window.ajaxHistDiff = function()
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  var id1=false;
  var id2=false;
  for ( i = 0; i < arrDiff1Buttons.length; i++ )
  {
    k = i + '';
    kpp = i + 1;
    kpp = kpp + '';
    if(arrDiff1Buttons[k].checked) id1 = arrDiff1Buttons[k].id.substr(6);
    if(arrDiff2Buttons[k].checked) id2 = arrDiff2Buttons[k].id.substr(6);
  }
  if(!id1 || !id2) { alert('BUG: Couldn\'t get checked radiobutton state'); return; }
  setAjaxLoading();
  ajaxGet(stdAjaxPrefix+'&_mode=pagediff&diff1='+id1+'&diff2='+id2, function(ajax)
    {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        unsetAjaxLoading();
        document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
      }
    });
}

// Change the user's preferred style/theme

window.ajaxChangeStyle = function()
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  load_component(['l10n', 'fadefilter', 'jquery', 'jquery-ui']);
  
  // force string fetch
  $lang.get('etc_cancel');
  
  // preload some images
  var i1 = new Image();
  i1.src = cdnPath + '/images/loading-big.gif';
  var i2 = new Image();
  i2.src = cdnPath + '/images/check-large.png';
  
  darken(true, 70, 'theme-selector-shade');
  
  $('body').append('<div id="theme-selector-wrapper"><div id="theme-selector-body"><div id="theme-selector-inner"><div class="theme-selector-spinner"></div></div></div></div>');
  $('#theme-selector-wrapper')
    .css('top', String(getScrollOffset()) + 'px')
    .css('left', 0)
    .css('z-index', String( getHighestZ() + 20 ));
  
  $.get(stdAjaxPrefix + '&_mode=theme_list', {}, function(data, status)
    {
      $('#theme-selector-inner .theme-selector-spinner').fadeOut(650);
      $('#theme-selector-body').animate({ width: 728 }, 600, function()
        {
          // avoiding jQuery's fade functions because they insist on toggling display as well
          if ( !aclDisableTransitionFX )
            changeOpac(0, 'theme-selector-inner');
          $('#theme-selector-inner').html('<h3></h3>');
          $('#theme-selector-inner > h3').text($lang.get('ajax_thmsel_lbl_choosetheme'));
          $('#theme-selector-inner').append('<ul></ul>');
          for ( var i = 0; i < data.length; i++ )
          {
            var bgi = data[i].have_thumb ? cdnPath + '/themes/' + data[i].theme_id + '/preview.png' : cdnPath + '/images/themepreview.png';
            var maxheight = getHeight() - 325;
            $('#theme-selector-inner > ul')
              .css('clip', 'rect(0px, auto, auto, 0px)')
              .css('overflow', 'auto')
              .css('max-height', maxheight)
              .append('<li id="theme_' + i + '"><a href="#"><span></span></a></li>');
            $('#theme-selector-inner li#theme_' + i + ' > a')
              .css('background-image', 'url(' + bgi + ')')
              .attr('enano:theme_id', data[i].theme_id);
            $('#theme-selector-inner li#theme_' + i + ' > a > span')
              .text(data[i].theme_name);
          }
          $('#theme-selector-inner').append('<span class="menuclear"></span>');
          $('#theme-selector-inner').append('<div style="padding-top: 40px;"><a class="abutton abutton_green" style="font-size: larger;" href="#" onclick="ajaxChangeStyleClose(); return false;">' + $lang.get('etc_cancel') + '</a></div>');
          
          $('#theme-selector-body').animate({ height: $('#theme-selector-inner').height() + 30 }, 600, function()
            {
              if ( !aclDisableTransitionFX )
                opacity('theme-selector-inner', 0, 100, 750);
            });
          
          $('#theme-selector-inner li a').click(function()
            {
              var theme_id = $(this).attr('enano:theme_id');
              $('span', this).html('&nbsp;').addClass('loading').fadeTo('fast', 0.6)
              $.get(stdAjaxPrefix + '&_mode=get_styles', { theme_id: theme_id }, function(data, status)
                {
                  if ( data.length > 1 )
                  {
                    $('#theme-selector-inner').css('height', $('#theme-selector-inner').height()).fadeOut(600, function()
                    {
                      var div = document.createElement('div');
                      if ( !aclDisableTransitionFX )
                        domObjChangeOpac(0, div);
                      
                      $(div).attr('id', 'theme-selector-style-list').append('<h3></h3>');
                      $('h3', div).text($lang.get('ajax_thmsel_lbl_choosestyle'));
                      
                      for ( var i = 0; i < data.length; i++ )
                      {
                        $(div).append('<a class="abutton block stylebtn" id="stylebtn_' + i + '" enano:style_id="' + data[i] + '">' + themeid_to_title(data[i]) + '</a>');
                      }
                      
                      $(div).append('<div style="padding-top: 40px;"><a class="abutton abutton_green" style="font-size: larger;" href="#" onclick="ajaxChangeStyleClose(); return false;">' + $lang.get('etc_cancel') + '</a></div>');
                      
                      if ( !aclDisableTransitionFX )
                        changeOpac(0, 'theme-selector-style-list');
                      $(this).html(div).show();
                      
                      $('#theme-selector-body').animate({width: 300, height: $('#theme-selector-style-list').height() + 30}, 300, function()
                        {
                          if ( !aclDisableTransitionFX )
                            opacity('theme-selector-style-list', 0, 100, 500);
                        });
                      
                      $('.stylebtn').click(function()
                        {
                          ajaxChangeThemeSetLoading();
                          $.post(stdAjaxPrefix + '&_mode=change_theme', { theme_id: theme_id, style_id: $(this).attr('enano:style_id') }, function(data, status)
                            {
                              if ( data.error )
                              {
                                alert(data.error);
                                ajaxChangeStyleClose();
                                return false;
                              }
                              ajaxChangeThemeShowSuccess();
                            }, 2000);
                          
                          return false;
                        });
                    });
                  }
                  else
                  {
                    if ( !data[0] )
                    {
                      alert('Didn\'t find any CSS files. :-/');
                      ajaxChangeStyleClose();
                    }
                    
                    $.post(stdAjaxPrefix + '&_mode=change_theme', { theme_id: theme_id, style_id: data[0] }, function(data, status)
                      {
                        if ( data.error )
                        {
                          alert(data.error);
                          ajaxChangeStyleClose();
                          return false;
                        }
                        ajaxChangeThemeShowSuccess();
                      }, 'json');
                  }
                }, 'json');
              return false;
            }); // click function
        }); // animate
    }, 'json'); // get
}

window.ajaxChangeThemeSetLoading = function()
{
  $('#theme-selector-body').animate({width: 130, height: 130});
  $('#theme-selector-inner').empty().html('<div class="theme-selector-spinner"></div>');
}

window.ajaxChangeThemeShowSuccess = function()
{
  if ( aclDisableTransitionFX )
  {
    $('#theme-selector-inner').empty();
  }
  else
  {
    setTimeout(function()
      {
        $('#theme-selector-inner').empty();
      }, 10);
  }
  
  $('#theme-selector-body').animate({width: 400, height: 300 }, 600, function()
      {
        $('#theme-selector-inner').append('<img src="' + cdnPath + '/images/check-large.png" alt=" " style="display: block; margin: 15px auto;" />');
        $('#theme-selector-inner').append('<h3>' + $lang.get('ajax_thmsel_msg_success') + '</h3>');
        $('#theme-selector-inner').append('<div style="padding-top: 20px;"><a class="abutton abutton_green" style="font-size: larger;" href="#" onclick="window.location.reload(); return false;">' + $lang.get('ajax_thmsel_btn_reload') + '</a></div>');
        $('#theme-selector-inner').append('<div style="padding-top: 25px;"><a href="#" style="font-size: smaller;" onclick="ajaxChangeStyleClose(); return false;">' + $lang.get('ajax_thmsel_btn_close') + '</a><br /><small>' + $lang.get('ajax_thmsel_btn_close_hint') + '</small></div>');
        $('#theme-selector-inner').fadeIn();
      });
}

window.ajaxChangeStyleClose = function()
{
  setTimeout(function()
    {
      enlighten(false, 'theme-selector-shade');
      $('#theme-selector-wrapper').fadeOut(500, function()
        {
          $(this).remove();
        });
    }, ( aclDisableTransitionFX ? 0 : 300));
  if ( !aclDisableTransitionFX )
    opacity('theme-selector-inner', 100, 0, 250);
}

function themeid_to_title(id)
{
  if ( typeof(id) != 'string' )
    return false;
  id = id.substr(0, 1).toUpperCase() + id.substr(1);
  id = id.replace(/_/g, ' ');
  id = id.replace(/-/g, ' ');
  return id;
}

window.ajaxSetPassword = function()
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  load_component('crypto');
  pass = hex_sha1(document.getElementById('mdgPassSetField').value);
  setAjaxLoading();
  ajaxPost(stdAjaxPrefix+'&_mode=setpass', 'password='+pass, function(ajax)
    {
      unsetAjaxLoading();
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        alert(ajax.responseText);
      }
    }, true);
}

window.ajaxDisableEmbeddedPHP = function()
{
  // IE <6 pseudo-compatibility
  if ( KILL_SWITCH )
    return true;
  if ( !confirm($lang.get('ajax_killphp_confirm')) )
    return false;
  var $killdiv = $dynano('php_killer');
  if ( !$killdiv.object )
  {
    alert('Can\'t get kill div object');
    return false;
  }
  $killdiv.object.innerHTML = '<img alt="Loading..." src="' + scriptPath + '/images/loading-big.gif" /><br />Making request...';
  var url = makeUrlNS('Admin', 'Home', 'src=ajax');
  ajaxPost(url, 'act=kill_php', function(ajax) {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        if ( ajax.responseText == '1' )
        {
          var $killdiv = $dynano('php_killer');
          //$killdiv.object.innerHTML = '<img alt="Success" src="' + scriptPath + '/images/error.png" /><br />Embedded PHP in pages has been disabled.';
          $killdiv.object.parentNode.removeChild($killdiv.object);
          var newdiv = document.createElement('div');
          // newdiv.style = $killdiv.object.style;
          newdiv.className = $killdiv.object.className;
          newdiv.innerHTML = '<img alt="Success" src="' + scriptPath + '/images/error.png" /><br />' + $lang.get('ajax_killphp_success');
          $killdiv.object.parentNode.appendChild(newdiv);
          $killdiv.object.parentNode.removeChild($killdiv.object);
        }
        else
        {
          var $killdiv = $dynano('php_killer');
          $killdiv.object.innerHTML = ajax.responseText;
        }
      }
    });
}

var catHTMLBuf = false;

window.ajaxCatToTag = function()
{
  if ( KILL_SWITCH )
    return false;
  setAjaxLoading();
  ajaxGet(stdAjaxPrefix + '&_mode=get_tags', function(ajax)
    {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        unsetAjaxLoading();
        var resptext = String(ajax.responseText + ' ');
        resptext = resptext.substr(0, resptext.length-1);
        if ( resptext.substr(0, 1) != '{' )
        {
          handle_invalid_json(resptext);
          return false;
        }
        var json = parseJSON(resptext);
        var catbox = document.getElementById('mdgCatBox');
        if ( !catbox )
          return false;
        var linkbox = catbox.parentNode.firstChild.firstChild.nextSibling;
        linkbox.firstChild.nodeValue = $lang.get('catedit_catbox_link_showcategorization');
        linkbox.onclick = function() { ajaxTagToCat(); return false; };
        catHTMLBuf = catbox.innerHTML;
        catbox.innerHTML = '';
        catbox.appendChild(document.createTextNode($lang.get('tags_lbl_page_tags')+' '));
        if ( json.tags.length < 1 )
        {
          catbox.appendChild(document.createTextNode($lang.get('tags_lbl_no_tags')));
        }
        for ( var i = 0; i < json.tags.length; i++ )
        {
          catbox.appendChild(document.createTextNode(json.tags[i].name));
          if ( json.tags[i].can_del )
          {
            catbox.appendChild(document.createTextNode(' '));
            var a = document.createElement('a');
            a.appendChild(document.createTextNode('[X]'));
            a.href = '#';
            a._js_tag_id = json.tags[i].id;
            a.onclick = function() { ajaxDeleteTag(this, this._js_tag_id); return false; }
            catbox.appendChild(a);
          }
          if ( ( i + 1 ) < json.tags.length )
            catbox.appendChild(document.createTextNode(', '));
        }
        if ( json.can_add )
        {
          catbox.appendChild(document.createTextNode(' '));
          var addlink = document.createElement('a');
          addlink.href = '#';
          addlink.onclick = function() { try { ajaxAddTagStage1(); } catch(e) { }; return false; };
          addlink.appendChild(document.createTextNode($lang.get('tags_btn_add_tag')));
          catbox.appendChild(addlink);
        }
      }
    });
}

var addtag_open = false;

window.ajaxAddTagStage1 = function()
{
  if ( addtag_open )
    return false;
  var catbox = document.getElementById('mdgCatBox');
  var adddiv = document.createElement('div');
  var text = document.createElement('input');
  var addlink = document.createElement('a');
  addlink.href = '#';
  addlink.onclick = function() { ajaxAddTagStage2(this.parentNode.firstChild.nextSibling.value, this.parentNode); return false; };
  addlink.appendChild(document.createTextNode($lang.get('tags_btn_add')));
  text.type = 'text';
  text.size = '15';
  text.onkeyup = function(e)
  {
    if ( e.keyCode == 13 )
    {
      ajaxAddTagStage2(this.value, this.parentNode);
    }
  }
  
  adddiv.style.margin = '5px 0 0 0';
  adddiv.appendChild(document.createTextNode($lang.get('tags_lbl_add_tag')+' '));
  adddiv.appendChild(text);
  adddiv.appendChild(document.createTextNode(' '));
  adddiv.appendChild(addlink);
  catbox.appendChild(adddiv);
  addtag_open = true;
}

var addtag_nukeme = false;

window.ajaxAddTagStage2 = function(tag, nukeme)
{
  if ( !addtag_open )
    return false;
  if ( addtag_nukeme )
    return false;
  addtag_nukeme = nukeme;
  tag = ajaxEscape(tag);
  setAjaxLoading();
  ajaxPost(stdAjaxPrefix + '&_mode=addtag', 'tag=' + tag, function(ajax)
    {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        unsetAjaxLoading();
        var nukeme = addtag_nukeme;
        addtag_nukeme = false;
        var resptext = String(ajax.responseText + ' ');
        resptext = resptext.substr(0, resptext.length-1);
        if ( resptext.substr(0, 1) != '{' )
        {
          handle_invalid_json(resptext);
          return false;
        }
        var json = parseJSON(resptext);
        var parent = nukeme.parentNode;
        parent.removeChild(nukeme);
        addtag_open = false;
        if ( json.success )
        {
          var node = parent.childNodes[1];
          var insertafter = false;
          var nukeafter = false;
          if ( node.nodeValue == $lang.get('tags_lbl_no_tags') )
          {
            nukeafter = true;
          }
          insertafter = parent.childNodes[ parent.childNodes.length - 3 ];
          // these need to be inserted in reverse order
          if ( json.can_del )
          {
            var a = document.createElement('a');
            a.appendChild(document.createTextNode('[X]'));
            a.href = '#';
            a._js_tag_id = json.tag_id;
            a.onclick = function() { ajaxDeleteTag(this, this._js_tag_id); return false; }
            insertAfter(parent, a, insertafter);
            insertAfter(parent, document.createTextNode(' '), insertafter);
          }
          insertAfter(parent, document.createTextNode(json.tag), insertafter);
          if ( !nukeafter )
          {
            insertAfter(parent, document.createTextNode(', '), insertafter);
          }
          if ( nukeafter )
          {
            parent.removeChild(insertafter);
          }
        }
        else
        {
          alert(json.error);
        }
      }
    });
}

window.ajaxDeleteTag = function(parentobj, tag_id)
{
  var arrDelete = [ parentobj, parentobj.previousSibling, parentobj.previousSibling.previousSibling ];
  var parent = parentobj.parentNode;
  var writeNoTags = false;
  if ( parentobj.previousSibling.previousSibling.previousSibling.nodeValue == ', ' )
    arrDelete.push(parentobj.previousSibling.previousSibling.previousSibling);
  else if ( parentobj.previousSibling.previousSibling.previousSibling.nodeValue == $lang.get('tags_lbl_page_tags') + ' ' )
    arrDelete.push(parentobj.nextSibling);
  
  if ( parentobj.previousSibling.previousSibling.previousSibling.nodeValue == $lang.get('tags_lbl_page_tags') + ' ' &&
       parentobj.nextSibling.nextSibling.firstChild )
    if ( parentobj.nextSibling.nextSibling.firstChild.nodeValue == $lang.get('tags_btn_add_tag'))
      writeNoTags = true;
    
  ajaxPost(stdAjaxPrefix + '&_mode=deltag', 'tag_id=' + String(tag_id), function(ajax)
    {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        if ( ajax.responseText == 'success' )
        {
          for ( var i = 0; i < arrDelete.length; i++ )
          {
            try
            {
              parent.removeChild(arrDelete[i]);
            } catch(e) {}
          }
          if ( writeNoTags )
          {
            var node1 = document.createTextNode($lang.get('tags_lbl_no_tags'));
            var node2 = document.createTextNode(' ');
            insertAfter(parent, node1, parent.firstChild);
            insertAfter(parent, node2, node1);
          }
        }
        else
        {
          alert(ajax.responseText);
        }
      }
    });
}

window.ajaxTagToCat = function()
{
  if ( !catHTMLBuf )
    return false;
  var catbox = document.getElementById('mdgCatBox');
  if ( !catbox )
    return false;
  addtag_open = false;
  var linkbox = catbox.parentNode.firstChild.firstChild.nextSibling;
  linkbox.firstChild.nodeValue = $lang.get('tags_catbox_link');
  linkbox.onclick = function() { ajaxCatToTag(); return false; };
  catbox.innerHTML = catHTMLBuf;
  catHTMLBuf = false;
}

var keepalive_interval = false;

window.ajaxPingServer = function()
{
  ajaxGet(stdAjaxPrefix + '&_mode=ping', function(ajax)
    {
    });
}

window.ajaxToggleKeepalive = function()
{
  if ( readCookie('admin_keepalive') == '1' )
  {
    createCookie('admin_keepalive', '0', 3650);
    if ( keepalive_interval )
      clearInterval(keepalive_interval);
    var span = document.getElementById('keepalivestat');
    span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_off');
  }
  else
  {
    createCookie('admin_keepalive', '1', 3650);
    if ( !keepalive_interval )
      keepalive_interval = setInterval('ajaxPingServer();', 600000);
    var span = document.getElementById('keepalivestat');
    span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_on');
    ajaxPingServer();
  }
}

var keepalive_onload = function()
{
  if ( readCookie('admin_keepalive') == '1' )
  {
    if ( !keepalive_interval )
      keepalive_interval = setInterval('ajaxPingServer();', 600000);
    var span = document.getElementById('keepalivestat');
    span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_on');
  }
  else
  {
    if ( keepalive_interval )
      clearInterval(keepalive_interval);
    var span = document.getElementById('keepalivestat');
    span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_off');
  }
};

window.aboutKeepAlive = function()
{
  load_component(['messagebox', 'flyin', 'fadefilter']);
  new MessageBox(MB_OK|MB_ICONINFORMATION, $lang.get('user_keepalive_info_title'), $lang.get('user_keepalive_info_body'));
}

window.ajaxUpdateCheck = function(targetelement)
{
  if ( !document.getElementById(targetelement) )
  {
    return false;
  }
  var target = document.getElementById(targetelement);
  target.innerHTML = '';
  var img = document.createElement('img');
  img.src = cdnPath + '/images/loading.gif';
  img.alt = 'Loading...';
  target.appendChild(img);
  ajaxGet(makeUrlNS('Admin', 'Home/updates.xml'), function(ajax)
    {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        var releases = new Array();
        var update_available = false;
        if ( ajax.responseXML == null )
        {
          alert("Error fetching updates list:\n" + ajax.responseText);
          return false;
        }
        if ( ajax.responseXML.firstChild.tagName == 'enano' )
        {
          var enanotag = ajax.responseXML.firstChild;
          for ( var i = 0; i < enanotag.childNodes.length; i++ )
          {
            if ( enanotag.childNodes[i].tagName == 'error' )
            {
              alert(enanotag.childNodes[i].firstChild.nodeValue);
            }
            else if ( enanotag.childNodes[i].tagName == 'latest' )
            {
              // got <latest>
              var latesttag = enanotag.childNodes[i];
              for ( var j = 0; j < latesttag.childNodes.length; j++ )
              {
                var node = latesttag.childNodes[j];
                if ( node.tagName == 'release' )
                {
                  var releasedata = new Object();
                  for ( var k = 0; k < node.attributes.length; k++ )
                  {
                    releasedata[node.attributes[k].nodeName] = node.attributes[k].nodeValue;
                  }
                  releases.push(releasedata);
                }
                else if ( node.tagName == 'haveupdates' )
                {
                  update_available = true;
                }
              }
              break;
            }
          }
        }
        else
        {
          return false;
        }
        var thediv = document.getElementById(targetelement);
        thediv.innerHTML = '';
        if ( !thediv )
        {
          return false;
        }
        if ( releases.length > 0 )
        {
          thediv.className = 'tblholder';
          // FIXME: l10n
          if ( update_available )
          {
            var infobox = document.createElement('div');
            infobox.className = 'info-box-mini';
            infobox.appendChild(document.createTextNode('An update for Enano is available. The newest release is highlighted below.'));
            infobox.style.borderWidth = '0';
            infobox.style.margin = '0 0 0 0';
            thediv.appendChild(infobox);
          }
          else
          {
            var infobox = document.createElement('div');
            infobox.className = 'info-box-mini';
            infobox.appendChild(document.createTextNode('No new updates are available. The latest available releases are shown below.'));
            infobox.style.borderWidth = '0';
            infobox.style.margin = '0 0 0 0';
            thediv.appendChild(infobox);
          }
          var table = document.createElement('table');
          table.setAttribute('border', '0');
          table.setAttribute('cellspacing', '1');
          table.setAttribute('cellpadding', '4');
          
          var tr = document.createElement('tr');
          
          var td1 = document.createElement('th');
          var td2 = document.createElement('th');
          var td3 = document.createElement('th');
          var td4 = document.createElement('th');
          
          // FIXME: l10n
          td1.appendChild( document.createTextNode('Release type') );
          td2.appendChild( document.createTextNode('Version') );
          td3.appendChild( document.createTextNode('Code name') );
          td4.appendChild( document.createTextNode('Release notes') );
          
          tr.appendChild(td1);
          tr.appendChild(td2);
          tr.appendChild(td3);
          tr.appendChild(td4);
            
          table.appendChild(tr);
          
          var cls = 'row2';
          
          var j = 0;
          for ( var i in releases )
          {
            j++;
            if ( j > 5 )
              break;
            if ( update_available && j == 1 )
              cls = 'row1_green';
            else
              cls = ( cls == 'row1' ) ? 'row2' : 'row1';
            var release = releases[i];
            var tr = document.createElement('tr');
            
            var td1 = document.createElement('td');
            var td2 = document.createElement('td');
            var td3 = document.createElement('td');
            var td4 = document.createElement('td');
            
            td1.className = cls;
            td2.className = cls;
            td3.className = cls;
            td4.className = cls;
            
            if ( release.tag )
              td1.appendChild( document.createTextNode(release.tag) );
            
            if ( release.version )
              td2.appendChild( document.createTextNode(release.version) );
            
            if ( release.codename )
              td3.appendChild( document.createTextNode(release.codename) );
            
            if ( release.relnotes )
            {
              var a = document.createElement('a');
              a.href = release.relnotes;
              a.appendChild(document.createTextNode('View'));
              td4.appendChild( a );
            }
            
            tr.appendChild(td1);
            tr.appendChild(td2);
            tr.appendChild(td3);
            tr.appendChild(td4);
            
            table.appendChild(tr);
          }
          thediv.appendChild(table);
        }
        else
        {
          thediv.appendChild(document.createTextNode('No releases available.'));
        }
      }
    });
}

window.ajaxPluginAction = function(action, plugin_filename, btnobj, send_confirm)
{
  // if installing, uninstalling, or re-importing, confirm
  if ( action == 'install' || action == 'uninstall' || action == 'reimport' )
  {
    var prompt = miniPrompt(function(div)
      {
        var txtholder = document.createElement('div');
        txtholder.style.textAlign = 'center';
        txtholder.appendChild(document.createTextNode($lang.get('acppl_msg_confirm_' + action)));
        txtholder.appendChild(document.createElement('br'));
        txtholder.appendChild(document.createElement('br'));
        
        // create buttons
        var btn_go = document.createElement('a');
        btn_go.className = 'abutton abutton_red';
        btn_go.href = '#';
        btn_go._action = action;
        btn_go._filename = plugin_filename;
        btn_go._button = btnobj;
        btn_go.appendChild(document.createTextNode($lang.get('acppl_btn_' + action)));
        btn_go.style.fontWeight = 'bold';
        txtholder.appendChild(btn_go);
        
        // space
        txtholder.appendChild(document.createTextNode(' '));
        
        // cancel
        var btn_cancel = document.createElement('a');
        btn_cancel.className = 'abutton abutton_blue';
        btn_cancel.href = '#';
        btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
        
        txtholder.appendChild(btn_cancel);
        div.appendChild(txtholder);
        
        btn_go.onclick = function()
        {
          ajaxPluginAction(this._action + '_confirm', this._filename, this._button);
          miniPromptDestroy(this);
          return false;
        };
        btn_cancel.onclick = function()
        {
          miniPromptDestroy(this);
          return false;
        };
      });
    return true;
  }
  action = action.replace(/_confirm$/, '');
  // white-out the plugin info box
  if ( btnobj )
  {
    var td = btnobj.parentNode.parentNode.parentNode.parentNode;
    var blackbox = whiteOutElement(td);
  }
  var request = {
      mode: action,
      plugin: plugin_filename
    };
  if ( send_confirm )
  {
    request.install_confirmed = true;
  }
  request = toJSONString(request);
  ajaxPost(makeUrlNS('Admin', 'PluginManager/action.json'), 'r=' + ajaxEscape(request), function(ajax)
    {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        var response = String(ajax.responseText + '');
        if ( !check_json_response(response) )
        {
          handle_invalid_json(response);
          return false;
        }
        response = parseJSON(response);
        if ( blackbox )
        {
          blackbox.parentNode.removeChild(blackbox);
        }
        if ( response.success )
        {
          ajaxPage( namespace_list['Admin'] + 'PluginManager' );
          return true;
        }
        if ( response.need_confirm )
        {
          miniPromptMessage({
              title: $lang.get(response.confirm_title),
              message: $lang.get(response.confirm_body),
              buttons: [
                {
                  text: $lang.get('acppl_btn_install'),
                  color: 'red',
                  style: {
                    fontWeight: 'bold'
                  },
                  onclick: function() {
                    ajaxPluginAction(action + '_confirm', plugin_filename, btnobj, true);
                    miniPromptDestroy(this);
                  }
                },
                {
                  text: $lang.get('etc_cancel'),
                  color: 'blue',
                  onclick: function() {
                    miniPromptDestroy(this);
                  }
                }
              ]
            });
          return true;
        }
        // wait for fade effect to finish its run
        setTimeout(function()
          {
            miniPrompt(function(div)
              {
                if ( blackbox )
                {
                  blackbox.parentNode.removeChild(blackbox);
                }
                var txtholder = document.createElement('div');
                txtholder.style.textAlign = 'center';
                txtholder.appendChild(document.createTextNode(response.error));
                txtholder.appendChild(document.createElement('br'));
                txtholder.appendChild(document.createElement('br'));
                
                // close button
                var btn_cancel = document.createElement('a');
                btn_cancel.className = 'abutton abutton_red';
                btn_cancel.href = '#';
                btn_cancel.appendChild(document.createTextNode($lang.get('etc_ok')));
                
                txtholder.appendChild(btn_cancel);
                div.appendChild(txtholder);
                
                btn_cancel.onclick = function()
                {
                  miniPromptDestroy(this);
                  return false;
                }
              });
          }, 750);
      }
    });
}

window.ajaxReverseDNS = function(o, text)
{
  if(text) var ipaddr = text;
  else var ipaddr = o.innerHTML;
  rDnsObj = o;
  rDnsBannerObj = bannerOn('Retrieving reverse DNS info...');
  ajaxGet(stdAjaxPrefix+'&_mode=rdns&ip='+ipaddr, function(ajax) {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        off = fetch_offset(rDnsObj);
        dim = fetch_dimensions(rDnsObj);
        right = off['left'] + dim['w'];
        top = off['top'] + dim['h'];
        var thediv = document.createElement('div');
        thediv.className = 'info-box';
        thediv.style.margin = '0';
        thediv.style.position = 'absolute';
        thediv.style.top  = top  + 'px';
        thediv.style.display = 'none';
        thediv.style.zIndex = getHighestZ() + 2;
        thediv.id = 'mdgDynamic_rDnsInfoDiv_'+Math.floor(Math.random() * 1000000);
        thediv.innerHTML = '<b>Reverse DNS:</b><br />'+ajax.responseText+' <a href="#" onclick="elem = document.getElementById(\''+thediv.id+'\'); elem.innerHTML = \'\'; elem.style.display = \'none\';return false;">Close</a>';
        var body = document.getElementsByTagName('body');
        body = body[0];
        bannerOff(rDnsBannerObj);
        body.appendChild(thediv);
        thediv.style.display = 'block';
        left = fetch_dimensions(thediv);
        thediv.style.display = 'none';
        left = right - left['w'];
        thediv.style.left = left + 'px';
        thediv.style.display = 'block';
        fadeInfoBoxes();
      }
    });
}