//Dokumentation unter ] <nowiki>
/*global mw: true */
(function($, libs){

/*lädt index.php mit Parametern über GET
  newwindow: 0 - im selben Fenster
             1 - in neuem Fenster (Hintergrund)
             2 - in neuem Fenster (Vordergrund)
  Rückgabe: neues Fenster
*/
function get (params, newwindow) {
   var url = mw.config.get('wgScript') + '?' + $.param(params),
       fenster;
   if (newwindow === 0) {
      window.location.href = url;
      return;
   } else {
      fenster = window.open(url);
      if (newwindow === 1) {
         window.focus();
         fenster.blur();
      } else {
         window.blur();
         fenster.focus();
      }
   }
   return fenster;
}

/*lädt index.php mit Parametern über POST
  newwindow: 0 - im selben Fenster
             1 - in neuem Fenster (Hintergrund)
             2 - in neuem Fenster (Vordergrund)
  Rückgabe: neues Fenster
*/
function post (params, newwindow) {
   var formular = '';
   for (var p in params) {
       var v = params;
       if (typeof v === 'boolean') {
          formular += mw.html.element('input', {type: 'checkbox', name: p, value: '1', checked: v});
       } else { //String
          v = '' + v;
          if (v.indexOf('\n') > -1) {
             formular += mw.html.element('textarea', {name: p}, '\n' + v); //damit führende Newlines nicht verschluckt werden
          } else {
             formular += mw.html.element('input', {type: 'hidden', name: p, value: v});
          }
       }
   }
   var html = mw.html.element('form', {style: 'display: none;', method: 'post', enctype: 'multipart/form-data', action: mw.config.get('wgScript')}, new mw.html.Raw(formular));

   if (newwindow === 0) {
      var formContainer = document.createElement('div');
      formContainer.style.display = 'none';
      document.body.appendChild(formContainer);
      formContainer.innerHTML = html;
      var commitForm = formContainer.firstChild;
      commitForm.submit();
      return;
   } else { //FIXME besser target = _blank
      var fenster = window.open('about:blank');
      var script = 'document.getElementsByTagName("form").submit();';
      fenster.document.write('<html><head><title></title></head><body>' + html + //FIXME Zeichensatz (?)
      '<' + 'script type="text/javascript">' +
      script +
      '</' + 'script></body></html>');
      if (newwindow === 1) {
         window.focus();
         fenster.blur();
      } else {
         window.blur();
         fenster.focus();
      }
      return fenster;
    }
}

/*lädt die angegebene URL (muss eine edit-URL sein) per AJAX,
  fügt die summary ein,
  speichert per AJAX
  und ruft die Callback-Funktion auf, mit true im Erfolgsfall, false, wenn auf der neuen Seite eine Editbox ist
  Code zu Teilen von Ireas abgeschaut
  ist notwendig, da die gesichteten Versionen eigene Formularelemente hinzufügen, daher in Sicht-Namensräumen beim Bearbeiten alter Versionen auf jeden Fall verwenden
*/
function editForFR (url, summary, callback) {
  $.get(url, {}, function (html) {
    var $form = $(html).find('#editform'),
        data = $form.find(':input').serializeArray();
    for (var i = 0; i < data.length; i++) {
        if (data.name === 'wpSummary') {
           data.value = summary;
        } else if (data.name === 'wpMinoredit') {
           data.value = true;
        }
    }
    $.post($form.attr('action'), data, function (html) {
      callback($(html).find('#editform').length === 0); //FIXME fehlgeschlagener Revert
    });
  });
}

function validate (param) {
  if (typeof param.title !== 'string') return false;
  if (typeof param.summary !== 'string') return false;
  if (param.method === undefined) param.method = 'interface';
  if (param.method !== 'interface' && param.method !== 'API') return false;
  if (param.newwindow === undefined) param.newwindow = 0;
  if (param.newwindow !== 0 && param.newwindow !== 1 && param.newwindow !== 2) return false;
  if (param.method === 'API' && param.newwindow !== 0) return false;
  if (param.callback === undefined) param.callback = function () {};
  if (!$.isFunction(param.callback)) return false;
  return true;
}

//Funktionen für action=edit
var edit = {
  //validiert Parameter, setzt Standardwerte
  validate: function (param) {
    if (param.title === undefined) param.title = mw.config.get('wgPageName');
    if (typeof param.section == 'string' && param.section != 'new' && param.section != 'last' && !param.section.match(/^T-\d+$/)) return false;
    if (typeof param.section == 'number' && (param.section < 0 || Math.floor(param.section) != param.section)) return false;
    if (typeof param.section == 'object') {
       if (!((typeof param.section.title == 'string' && typeof param.section.number == 'undefined') ||
             (typeof param.section.title == 'undefined' && typeof param.section.number == 'string'))) return false;
       if (typeof param.section.count == 'undefined') param.section.count = 1;
       if (typeof param.section.count != 'number') return false;
    }
    if (typeof param.section != 'undefined' && typeof param.section != 'string' && typeof param.section != 'number' && typeof param.section != 'object') return false;
    if (typeof param.change == 'string') {
       var vorsatz = param.change;
       param.change = function (text) {
                          return vorsatz + text;
                      };
    }
    if (typeof param.change != 'function') return false;
    if (param.action === undefined) param.action = 'wpDiff';
    if (param.action != 'wpDiff' && param.action != 'wpPreview' && param.action != 'wpSave') return false;
    if (param.minor === undefined) param.minor = false;
    if (typeof param.minor != 'boolean') return false;
    if (param.create === undefined) param.create = false;
    if (typeof param.create != 'boolean') return false;
    if (param.transcluded === undefined) param.transcluded = false;
    if (typeof param.transcluded != 'boolean') return false;

    if (!validate(param)) return false;

    edit.info(param);
    return true;
  },
  //holt Informationen (alter Text, Token, etc.)
  info: function (param) {
    if (param.section == 'last' || typeof param.section == 'object') return edit.info_section(param); //erst Abschnitt ermitteln
    var query_param = {action: 'query', titles: param.title, prop: 'info|revisions', rvprop: 'timestamp', inprop: 'watched', intoken: 'edit', format: 'json'};
    if (param.section != 'new') {
       query_param.rvprop += '|content';
       if (param.section !== undefined) query_param.rvsection = param.section; //number oder string (z. B. 'T-1');
    }
    $.getJSON(mw.util.wikiScript('api'), query_param, function (json) {
       if (!json || !json.query || !json.query.pages) return param.callback('error', param.title);
       var pages = json.query.pages;

       param.api = {watchthis: false, //beobachten?
                    basetimestamp: '',
                    starttimestamp: '',
                    edittoken: '',
                    text: '' }; //Text
      for (var p in pages) {
          if (pages.watched !== undefined) param.api.watchthis = true;
          param.api.starttimestamp = pages.starttimestamp.replace(/\D/g, '');
          param.api.basetimestamp = param.api.starttimestamp;
          param.api.edittoken = pages.edittoken;
          if (pages.missing !== undefined) {
             if (!param.create) return param.callback('missing', param.title);
          } else {
             param.api.basetimestamp = pages.revisions.timestamp.replace(/\D/g, '');
             param.api.text = pages.revisions;
          }
      }
      if (param.section == 'new') param.api.text = '';

      var ret = param.change(param.api.text);
      if (ret === false) return param.callback('nochange', param.title, param.api.text);
      param.api.text = ret;

      if (param.method == 'API') {
         edit.api(param);
      } else {
         edit.interfc(param);
      }
    });
  },
  //holt Informationen über Abschnitt (falls nötig)
  info_section: function (param) {
    $.getJSON(mw.util.wikiScript('api'), {action: 'parse', page: param.title, prop: 'sections', format: 'json'}, function (json) {
       if (!json || !json.parse || !json.parse.sections) return param.callback('error', param.title);
       var sections = json.parse.sections;
       var sec = null;
       if (param.section == 'last')
          sec = sections;
       var hits = 0;
       for (var i = 0; i < sections.length; i++) {
           if (sec !== null) break;
           if (sections.line == param.section.title || sections.number == param.section.number) hits++;
           if (hits == param.section.count) sec = sections;
       }
       if (sec === null) {
          param.callback('missingsection', param.title);
       } else {
          if (sec.index.match(/^\d+$/)) {
             param.section = Number(sec.index);
             edit.info(param);
          } else {
             if (param.transcluded) {
                param.section = sec.index;
                param.title = sec.fromtitle;
                edit.info(param);
             } else {
                param.callback('transcluded', param.title, sec.fromtitle, sec.index);
             }
          }
       }
    });
  },
  //bearbeitet über API
  api: function (param) {
    var params = {action: 'edit', title: param.title, text: param.api.text, summary: param.summary, basetimestamp: param.api.basetimestamp, starttimestamp: param.api.starttimestamp, watchlist: 'nochange', token: param.api.edittoken, format: 'json'};
    if (param.section !== undefined) params.section = param.section;
    if (param.minor) {
       params.minor = '';
    } else {
       params.notminor = '';
    }
    if (!param.create) params.nocreate = '';
    $.post(mw.util.wikiScript('api'), params, function (json) {
       if (!json) return param.callback('error', param.title);
       if (json.edit && json.edit.result == 'Success') return param.callback('success', param.title, param.api.text);
       if (json.error && (json.error.code == 'pagedeleted' || json.error.code == 'editconflict')) return param.callback('conflict', param.title);
       param.callback('error', param.title);
    });
  },
  //bearbeitet über Interface
  interfc: function (param) {
    var params = {title: param.title, action: 'edit', wpTextbox1: param.api.text, wpSummary: param.summary, wpMinoredit: param.minor, wpWatchthis: param.api.watchthis, wpEdittime: param.api.basetimestamp, wpStarttime: param.api.starttimestamp, wpEditToken: param.api.edittoken};
    if (param.section !== undefined) params.section = param.section;
    params = param.action;
    //In meinen Augen ist eine Warnung das erwünschte Verhalten
    //params.wpAutoSummary = 'd41d8cd98f00b204e9800998ecf8427e';
    var fenster = post(params, param.newwindow);
    if (fenster !== undefined) param.callback('success', param.title, fenster);
  }
};

//Funktionen für action=delete
var del = {
  //validiert Parameter, setzt Standardwerte
  validate: function (param) {
    if (param.title === undefined) param.title = mw.config.get('wgPageName');
    if (!validate(param)) return false;

    if (param.method == 'API') {
       del.info(param);
    } else {
       del.interfc(param);
    }
    return true;
  },
  //holt Informationen (Token)
  info: function (param) {
    $.getJSON(mw.util.wikiScript('api'), {action: 'query', prop: 'info', intoken: 'delete', titles: param.title, format: 'json'}, function(json) {
       if (!json || !json.query || !json.query.pages) return param.callback('error', param.title);
       var pages = json.query.pages;
       param.api = {deletetoken: ''};
       for (var p in pages) {
           if (pages.missing !== undefined) return param.callback('missing', param.title);
           param.api.deletetoken = pages.deletetoken;
       }
       del.api(param);
    });
  },
  //löscht über API
  api: function (param) {
    var params = {action: 'delete', title: param.title, reason: param.summary, token: param.api.deletetoken, format: 'json'};
    $.post(mw.util.wikiScript('api'), params, function (json) {
       if (json && json) {
          param.callback('success', param.title);
       } else {
          param.callback('error', param.title);
       }
    }, 'json');
  },
  //löscht über Interface
  interfc: function (param) {
    var fenster = get({title: param.title, action: 'delete', wpReason: param.summary}, param.newwindow);
    if (fenster !== undefined) param.callback('success', param.title, fenster);
  }
};

//Funktionen für action=(un)block
var block = {
  //validiert Parameter, setzt Standardwerte
  validate: function (param) {
    if (param.title === undefined) param.title = mw.config.get('wgTitle');
    if (param.expiry === undefined) param.expiry = 'infinite';
    if (typeof param.expiry != 'string') return false;
    if (param.anononly === undefined) param.anononly = true;
    if (typeof param.anononly != 'boolean') return false;
    if (param.nocreate === undefined) param.nocreate = false;
    if (typeof param.nocreate != 'boolean') return false;
    if (param.autoblock === undefined) param.autoblock = false;
    if (typeof param.autoblock != 'boolean') return false;
    if (param.noemail === undefined) param.noemail = false;
    if (typeof param.noemail != 'boolean') return false;
    if (param.allowusertalk === undefined) param.allowusertalk = true;
    if (typeof param.allowusertalk != 'boolean') return false;
    if (param.watchuser === undefined) param.watchuser = true;
    if (typeof param.watchuser != 'boolean') return false;

    if (!validate(param)) return false;

    if (param.method == 'API') {
       block.info(param);
    } else {
       block.interfc(param);
    }
    return true;
  },
  //holt Informationen (Token)
  info: function (param) {
    var block = param.expiry == 'unblock' ? 'unblock' : 'block';
    $.getJSON(mw.util.wikiScript('api'), {action: 'query', prop: 'info', intoken: block, titles: 'User:' + param.title, format: 'json'}, function(json) {
       if (!json || !json.query || !json.query.pages) return param.callback('error', param.title);
       var pages = json.query.pages;
       param.api = {blocktoken: ''};
       for (var p in pages) {
           param.api.blocktoken = pages;
       }
       block.api(param);
    });
  },
  //sperrt über API
  api: function (param) {
    var block = (param.expiry == 'unblock') ? 'unblock' : 'block';
    var params = {action: block, user: param.title, reason: param.summary, expiry: param.expiry, token: param.api.deletetoken, format: 'json'};
    if (param.anononly) params.anononly = '';
    if (param.nocreate) params.nocreate = '';
    if (param.autoblock) params.autoblock = '';
    if (param.noemail) params.noemail = '';
    if (param.allowusertalk) params.allowusertalk = '';
    if (param.watchuser) params.watchuser = '';
    $.post(mw.util.wikiScript('api'), params, function (json) {
       if (json && json) {
          param.callback('success', param.title);
       } else {
          param.callback('error', param.title);
       }
    }, 'json');
  },
  //sperrt über Interface
  interfc: function (param) {
    var params = {wpTarget: param.title};
    if (param.expiry == 'unblock') {
       params.title = 'Special:Unblock';
       params.wpReason = param.summary;
    } else {
       params.title = 'Special:Block';
       params.wpExpiry = 'other';
       params = param.expiry;
       params.wpReason = 'other';
       params = param.summary;
       //funktioniert nicht (mahr)
       params.wpAnonOnly = param.anononly; //oder params.wpHardBlock = !param.anononly;
       params.wpCreateAccount = param.nocreate;
       params.wpEnableAutoblock = param.autoblock;
       params.wpEmailBan = param.noemail;
       params.wpAllowUsertalk = param.allowusertalk;
       params.wpWatchUser = param.watchuser;
    }
    var fenster = get(params, param.newwindow);
    if (fenster !== undefined) param.callback('success', param.title, fenster);
  }
};

//Funktionen für action=protect
var protect = {
  //validiert Parameter, setzt Standardwerte
  validate: function (param) {
    if (param.title === undefined) param.title = mw.config.get('wgPageName');
    if (param.expiry === undefined) param.expiry = 'infinite';
    if (typeof param.expiry != 'string') return false;
    if (param.cascade === undefined) param.cascade = false;
    if (typeof param.cascade != 'boolean') return false;
    if (param.protections === undefined) param.protections = 'edit=autoconfirmed|move=autoconfirmed';
    if (typeof param.protections != 'string') return false;
    if (param.protections == 'move') param.protections = 'move=sysop';
    if (param.protections == 'sysop') param.protections = 'edit=sysop|move=sysop';
    if (param.expiry == 'unprotect') param.protections = param.protections.replace(new RegExp ('=\\w+', 'g'), '=all');
    if (!validate(param)) return false;

    if (param.method == 'API') {
       protect.info(param);
    } else {
       protect.interfc(param);
    }
    return true;
  },
  //holt Informationen (Token)
  info: function (param) {
    $.getJSON(mw.util.wikiScript('api'), {action: 'query', prop: 'info', intoken: 'protect', titles: param.title, format: 'json'}, function(json) {
       if (!json || !json.query || !json.query.pages) return param.callback('error', param.title);
       var pages = json.query.pages;
       param.api = {protecttoken: ''};
       for (var p in pages) {
           param.api.protecttoken = pages.protecttoken;
       }
       protect.api(param);
    });
  },
  //schützt über API
  api: function (param) {
    var params = {action: 'protect', title: param.title, reason: param.summary, protections: param.protections, expiry: param.expiry, token: param.api.protecttoken, format: 'json'};
    if (param.cascade) params.cascade = '';
    $.post(mw.util.wikiScript('api'), params, function (json) {
       if (json && json.protections) {
          param.callback('success', param.title);
       } else {
          param.callback('error', param.title);
       }
    }, 'json');
  },
  //schützt über Interface
  interfc: function (param) {
    var params = {title: param.title, action: 'protect', 'mwProtect-reason': 'other', 'mwProtect-reason-other': param.summary, 'mwProtect-cascade': param.cascade ? '1' : '0'};
    var protections = param.protections.split('|');
    for (var i = 0; i < protections.length; i++) {
        var protection = protections.split('=');
        if (protection == 'all') protection = '';
        params] = protection;
        params] = param.expiry;
    }
    var fenster = get(params, param.newwindow);
    if (fenster !== undefined) param.callback('success', param.title, fenster);
  }
};

//Funktionen für action=move
var move = {
  //validiert Parameter, setzt Standardwerte
  validate: function (param) {
    if (param.title === undefined) param.title = mw.config.get('wgPageName');
    if (typeof param.to != 'string') return false;
    if (param.movetalk === undefined) param.movetalk = true;
    if (typeof param.movetalk != 'boolean') return false;
    if (param.movesubpages === undefined) param.movesubpages = false;
    if (typeof param.movesubpages != 'boolean') return false;
    if (param.noredirect === undefined) param.noredirect = false;
    if (typeof param.noredirect != 'boolean') return false;
    if (!validate(param)) return false;

    if (param.method == 'API') {
       move.info(param);
    } else {
       move.interfc(param);
    }
    return true;
  },
  //holt Informationen (Token)
  info: function (param) {
    $.getJSON(mw.util.wikiScript('api'), {action: 'query', prop: 'info', intoken: 'move', titles: param.title, format: 'json'}, function(json) {
       if (!json || !json.query || !json.query.pages) return param.callback('error', param.title);
       var pages = json.query.pages;
       param.api = {movetoken: ''};
       for (var p in pages) {
           if (pages.missing !== undefined) return param.callback('missing', param.title);
           param.api.movetoken = pages.movetoken;
       }
       move.api(param);
    });
  },
  //verschiebt über API
  api: function (param) {
    var params = {action: 'move', from: param.title, to: param.to, reason: param.summary, token: param.api.movetoken, format: 'json'};
    if (param.movetalk) params.movetalk = '';
    if (param.movesubpages) params.movesubpages = '';
    if (param.noredirect) params.noredirect = '';
    $.post(mw.util.wikiScript('api'), params, function (json) {
       if (json && json.move) {
          param.callback('success', param.title);
       } else {
          param.callback('error', param.title);
       }
    }, 'json');
  },
  //verschiebt über Interface
  interfc: function (param) {
    var params = {title: 'Special:MovePage', target: param.title, wpNewTitle: param.to, wpReason: param.summary, wpMovetalk: param.movetalk ? '1' : '0', wpLeaveRedirect: param.noredirect ? '0' : '1', wpMovesubpages: param.movesubpages ? '1' : '0'};
    var fenster = get(params, param.newwindow);
    if (fenster !== undefined) param.callback('success', param.title, fenster);
  }
};

//Funktionen für action=mail
var mail = {
  //validiert Parameter, setzt Standardwerte
  validate: function (param) {
    if (param.title === undefined) param.title = mw.config.get('wgTitle');
    if (typeof param.text != 'string') return false;
    if (param.ccme === undefined) param.ccme = false;
    if (typeof param.ccme != 'boolean') return false;
    if (!validate(param)) return false;

    if (param.method == 'API') {
       mail.info(param);
    } else {
       mail.interfc(param);
    }
    return true;
  },
  //holt Informationen (Token)
  info: function (param) {
    $.getJSON(mw.util.wikiScript('api'), {action: 'query', prop: 'info', intoken: 'email', titles: 'User:' + param.title, format: 'json'}, function(json) {
       if (!json || !json.query || !json.query.pages) return param.callback('error', param.title);
       var pages = json.query.pages;
       param.api = {emailtoken: ''};
       for (var p in pages) {
           param.api.emailtoken = pages.emailtoken;
       }
       mail.api(param);
    });
  },
  //mailt über API
  api: function (param) {
    var params = {action: 'emailuser', target: param.title, subject: param.summary, text: param.text, token: param.api.emailtoken, format: 'json'};
    if (param.ccme) params.ccme = '';
    $.post(mw.util.wikiScript('api'), params, function (json) {
       if (json && json.emailuser) {
          param.callback('success', param.title);
       } else {
          param.callback('error', param.title);
       }
    }, 'json');
  },
  //mailt über Interface
  interfc: function (param) {
    var params = {title: 'Special:EmailUser', target: param.title, wpSubject: param.summary, wpText: param.text, wpCCMe: param.ccme ? '1' : '0'}; //TODO: geraten
    var fenster = get(params, param.newwindow);
    if (fenster !== undefined) param.callback('success', param.title, fenster);
  }
};

//Funktionen zum Revertieren
var revert = {
  //validiert Parameter, setzt Standardwerte
  validate: function (param) {
    if (param.title === undefined) param.title = mw.config.get('wgPageName');
    if (typeof param.revert != 'object') return false;
    if (!validate(param)) return false;

    if (param.revert.rollback) {
       revert.info_rollback(param);
    } else if (param.revert.fakerollback) {
       revert.info_fakerollback(param);
    } else if (param.revert.reset && param.method == 'API' && param.forceapi) {
       revert.info_reset(param);
    } else if (param.revert.reset && param.method == 'API') {
       revert.pseudoapi_reset(param);
    } else if (param.revert.reset) {
       revert.interfc_reset(param);
    } else if (param.revert.undo && param.method == 'API' && param.forceapi) {
       revert.info_undo(param);
    } else if (param.revert.undo && param.method == 'API') {
       revert.pseudoapi_undo(param);
    } else if (param.revert.undo) {
       revert.interfc_undo(param);
    } else {
       return false;
    }

    return true;
  },
  //holt Informationen (Token)
  info_rollback: function (param) {
    $.getJSON(mw.util.wikiScript('api'), {action: 'query', prop: 'revisions', rvtoken: 'rollback', titles: param.title, format: 'json'}, function(json) {
       if (!json || !json.query || !json.query.pages) return param.callback('error', param.title);
       var pages = json.query.pages;
       param.api = {rollbacktoken: ''};
       for (var p in pages) {
           param.api.rollbacktoken = pages.rollbacktoken;
       }
       if (param.method == 'API') {
          revert.api_rollback(param);
       } else {
          revert.interfc_rollback(param);
       }
    });
  },
  info_fakerollback: function (param) {
    $.getJSON(mw.util.wikiScript('api'), {action: 'query', prop: 'revisions', titles: param.title, rvlimit: 50, rvprop: 'ids|user', format: 'json'}, function (json) {
       if (!json || !json.query || !json.query.pages) return param.callback('error', param.title);
       var pages = json.query.pages, revisions;
       for (var p in pages) {
           revisions = pages.revisions;
       }
       if (revisions.user != param.revert.fakerollback) return param.callback('conflict', param.title);
       for (var i = 1; i < revisions.length; i++) {
           if (revisions.user != param.revert.fakerollback) {
              param.revert = {reset: revisions.id};
              return revert.validate(param);
           }
       }
       return param.callback('error', param.title);
    });
  },
  info_reset: function (param) {
    $.get(mw.config.get('wgScript') + '?action=raw&oldid=' + param.revert.reset, '', function (text) {
       if (!edit.validate({
           title: param.title, change: function () { return text; }, summary: param.summary, minor: true, method: param.method, newwindow: param.newwindow, callback: param.callback
       })) param.callback('error', param.title);
    });
  },
  info_undo: function (param) {
    var query_param = {action: 'query', titles: param.title, prop: 'info|revisions', rvprop: 'timestamp', intoken: 'edit', format: 'json'};
    $.getJSON(mw.util.wikiScript('api'), query_param, function (json) {
       if (!json || !json.query || !json.query.pages) return param.callback('error', param.title);
       var pages = json.query.pages;

       param.api = {basetimestamp: '',
                    starttimestamp: '',
                    edittoken: ''
                   };
      for (var p in pages) {
          param.api.starttimestamp = pages.starttimestamp.replace(/\D/g, '');
          param.api.edittoken = pages.edittoken;
          if (pages.missing !== undefined) {
             return param.callback('error', param.title);
          } else {
             param.api.basetimestamp = pages.revisions.timestamp.replace(/\D/g, '');
          }
      }
      revert.api_undo(param);
    });
  },
  //revertiert über API
  api_rollback: function (param) {
    var params = {action: 'rollback', title: param.title, summary: param.summary, user: param.revert.rollback, token: param.api.rollbacktoken, format: 'json'};
    $.post(mw.util.wikiScript('api'), params, function (json) {
       if (json && json.rollback) {
          param.callback('success', param.title);
       } else if (json && json.error && json.error.code == 'allreadyrolled') {
          param.callback('conflict', param.title);
       } else {
          param.callback('error', param.title);
       }
    }, 'json');
  },
  api_undo: function (param) {
    var params = {action: 'edit', title: param.title, summary: param.summary, minor: '', basetimestamp: param.api.basetimestamp, starttimestamp: param.api.starttimestamp, nocreate: '', watchlist: 'nochange', undo: param.revert.undo, token: param.api.edittoken, format: 'json'};
    if (param.revert.undoafter) params.undoafter = param.revert.undoafter;
    $.post(mw.util.wikiScript('api'), params, function (json) {
       if (!json) return param.callback('error', param.title);
       if (json.edit && json.edit.result == 'Success') return param.callback('success', param.title, param.api.text);
       if (json.error && (json.error.code == 'pagedeleted' || json.error.code == 'editconflict')) return param.callback('conflict', param.title, param.api.text);
       param.callback('error', param.title);
    }, 'json');
  },
  //revertiert über im Hintergrund über Interface, nötig für FR
  pseudoapi_reset: function (param) {
    var params = {action: 'edit', title: param.title, oldid: param.revert.reset};
    editForFR(mw.config.get('wgScript') + '?' + $.param(params), param.summary, function (success) {
       param.callback(success ? 'success' : 'error', param.title);
    });
  },
  pseudoapi_undo: function (param) {
    var params = {action: 'edit', title: param.title, undo: param.revert.undo};
    if (param.revert.undoafter) params.undoafter = param.revert.undoafter;
    editForFR(mw.config.get('wgScript') + '?' + $.param(params), param.summary, function (success) {
       param.callback(success ? 'success' : 'error', param.title);
    });
  },
  //revertiert über Interface
  interfc_rollback: function (param) {
    var params = {action: 'rollback', title: param.title, summary: param.summary, user: param.revert.rollback, token: param.api.rollbacktoken};
    var fenster = get(params, param.newwindow);
    if (fenster !== undefined) param.callback('success', param.title, fenster);
  },
  interfc_reset: function (param) {
    var params = {action: 'edit', title: param.title, oldid: param.revert.reset, summary: param.summary, minor: '1'};
    var fenster = get(params, param.newwindow);
    if (fenster !== undefined) param.callback('success', param.title, fenster);
  },
  interfc_undo: function (param) {
    var params = {action: 'edit', title: param.title, summary: param.summary, undo: param.revert.undo, minor: '1'};
    if (param.revert.undoafter) params.undoafter = param.revert.undoafter;
    var fenster = get(params, param.newwindow);
    if (fenster !== undefined) param.callback('success', param.title, fenster);
  }
};

var autoedit = {
version: 2.3,
/* Parameter werden als JSON-Objekt übergeben:
 * Standardparameter, die bei allen Aktionen vorhanden sind:
 *   title:      string, zu bearbeitende/löschende/... Seite (Standard: aktuelle Seite) bzw. betroffener Benutzer (Standard: Besitzer der aktuellen Benutzer(diskussions)seite)
 *   summary:    string, Kommentar, erforderlich
 *   method:     string, 'interface' (Standard), falls Aktion über normales Interface durchgeführt werden soll, 'API' für API
 *   newwindow:  number, mit der Bedeutung
 *                    0: im selben Fenster öffnen (Standard)
 *                    1: in neuem Fenster öffnen, Fokus beim alten Fenster lassen (funktioniert aber nicht, verhält sich wie 2)
 *                    2: in neuem Fenster öffnen, fokusieren
 *   callback:   function, wird evt. mit Status (siehe Liste) aufgerufen (string), zweiter Parameter ist der Titel
 *                    error: interner Fehler
 *                    conflict: Bearbeitungskonflikt (nur bei method == 'API'), tritt beim Bearbeiten oder Zurücksetzen auf
 *                    success: Seite wurde erfolgreich bearbeitet (nur bei method == 'API' oder newwindow > 0), dritter Parameter ist evt. neues Fenster
 * Rückgabe: false für falschen Aufruf, sonst true
 */

/*
 * editiert eine Seite (mehr oder weniger) automatisch
 *
 * über Parameter edit kann auch eine andere Funktion (del, block, protect, move, mail, revert) aufgerufen werden
 *
 * weitere Parameter
 *   section:     mixed, zu bearbeitender Abschnitt (Standard: gesamten Text)
 *                     number, Nummer des Abschnitts
 *                     string, 'new', neuer Abschnitt
 *                             'last', letzter Abschnitt
 *                             'T-n', transkludierter Abschnitt
 *                     object, {title: 'xxx'} bzw. {title: 'xxx', count: y} Abschnitt mit bestimmtem Titel (count bei mehreren gleichnamigen)
 *                             {number: '1.2.3'} Abschnitt mit bestimmter Gliederung
 *   change:      mixed (erforderlich)
 *                     string, Text, der dem aktuellen Text vorangestellt werden soll
 *                     function, wird mit aktuellem Text aufgerufen und gibt entweder neuen Text zurück oder false zum Abbruch
 *   minor:       boolean, als klein markieren (Standard: false)
 *   action:      string, einer der Werte (wird nur beachtet, wenn über Interface bearbeitet wird)
 *                     wpDiff: Seite mit Änderungen im Bearbeiten-Modus mit Versionsunterschied öffnen (Standard)
 *                     wpPreview: Seite mit Änderungen im Bearbeiten-Modus mit Vorschau öffnen
 *                     wpSave: Seite mit Änderungen direkt abspeichern
 *   create:      boolean, Seite neu anlegen, falls nicht existent (Standard: false)
 *   transcluded: boolean, falls Abschnitt von anderer Seite eingebunden wird, wird diese bearbeitet (Standard: false)
 *   zusätzliche Statuswerte für callback:
 *                     missing: Seite nicht vorhanden, create == false
 *                     missingsection: Abschnitt nicht vorhanden
 *                     transcluded: Abschnitt stammt von anderer Seite, dritter Parameter ist diese Seite, vierter der Abschnitt, transcluded == false
 *                     nochange: change gab false zurück, dritter Parameter ist alter Text
 * Rückgabe: false für falschen Aufruf, sonst true
 */
edit: function (param) {
  if (typeof param != 'object') return false;
  if (param.edit === undefined) param.edit = 'edit';
  switch (param.edit) {
  case 'edit': return edit.validate(param);
  case 'del': return del.validate(param);
  case 'block': return block.validate(param);
  case 'protect': return protect.validate(param);
  case 'move': return move.validate(param);
  case 'mail': return mail.validate(param);
  case 'revert': return revert.validate(param);
  default: return false;
  }
},

/*
 * löscht eine Seite (mehr oder weniger) automatisch
 */
del: function (param) {
  if (typeof param != 'object') return false;
  return del.validate(param);
},

/*
 * sperrt einen Benutzer (mehr oder weniger) automatisch
 *
 * weitere Parameter
 *   expiry:        string, Sperrdauer (Standard: infinite, unblock zum Entsperren)
 *   anononly:      boolean, nur unangemeldete Benutzer sperren (Standard: true)
 *   nocreate:      boolean, Erstellung von Benutzerkonten verhindern (Standard: false)
 *   autoblock:     boolean, Autoblock einsetzen (Standard: false)
 *   noemail:       boolean, E-Mail-Versand blockieren (Standard: false)
 *   allowusertalk: boolean, Schreibzugriff auf Diskussionsseite zulassen (Standard: true)
 *   watchuser:     boolean, Benutzer beobachten (Standard: true)
 */
block: function (param) {
  if (typeof param != 'object') return false;
  return block.validate(param);
},

/*
 * schützt eine Seite (mehr oder weniger) automatisch
 *
 * weitere Parameter
 *   expiry:      string, Sperrdauer (Standard: infinite, unprotect zum Entsperren)
 *   protections: string, Sperroptionen in der Form "Art=Level", Pipe-separiert
 *                       (Standard: edit=autoconfirmed|move=autoconfirmed, move für move=sysop, sysop für edit=sysop|move=sysop)
 *   cascade:     boolean, Kaskadensperre (Standard: false)
 */
protect: function (param) {
  if (typeof param != 'object') return false;
  return protect.validate(param);
},

/*
 * verschiebt eine Seite (mehr oder weniger) automatisch
 *
 * weitere Parameter
 * to:           string, Zielseite
 * movetalk:     boolean, Diskussionsseite mitverschieben (Standard: true)
 * movesubpages: boolean, Unterseiten mitverschieben (Standard: false)
 * noredirect:   boolean, Weiterleitung unterdrücken (Standard: false)
 */
move: function (param) {
  if (typeof param != 'object') return false;
  return move.validate(param);
},

/*
 * versendet (mehr oder weniger) automatisch eine E-Mail
 *
 * weitere Parameter
 * text: string, Text der E-Mail (erforderlich)
 * ccme: boolean, Kopie an sich selbst verschicken (Standard: false)
 */
mail: function (param) {
  if (typeof param != 'object') return false;
  return mail.validate(param);
},

/*
 * revertiert eine Bearbeitung (mehr oder weniger) automatisch
 *
 * weitere Parameter
 * revert: object mit einem der folgenden Parameter
 *     rollback:     Name des zurückzusetzenden Benutzers
 *     fakerollback: Name des zurückzusetzenden Benutzers
 *     reset:        Versionsnummer der alten Version
 *     undo:         Versionsnummer der zu revertierenden Version, ggfls. undoafter
 *
 * method == 'API' arbeitet nur mit der forceapi-Option wirklich über API
 */
revert: function (param) {
  if (typeof param != 'object') return false;
  return revert.validate(param);
},

/*
 * bearbeitet mehrere Seiten automatisch
 *
 * Parameter
 *   titles:   array aus strings, Titel der zu bearbeitenden Artikel
 *   param:    mixed
 *                object: Parameterobjekt wie für edit, title und callback werden automatisch gesetzt,
 *                        wird für method ein von 'API' (Standard) abweichender Wert gewählt,
 *                        muss newwindow auf > 0 gesetzt werden
 *                function: wird mit Titel aufgerufen, liefert Objekt wie oben zurück
 *   callback: function, optional, wird mit der Anzahl aller Titel,
 *                       der Anzahl der erfolgreichen Bearbeitungen,
 *                       einem Array der fehlgeschlagenen Bearbeitungen und
 *                       einem Objekt {'Titel: 'status'} aufgerufen
 *   delay:    number (Standard: 12) Zeit in Sekunden zwischen zwei Bearbeitungen
 */
massedit: function (titles, param, callback, delay) {
 if (typeof callback != 'function') callback = function(){};
 if (typeof delay != 'number') delay = 12;
 var count = titles.length; //Anzahl aller Artikel/...
 var success = 0; //Anzahl der erfolgreichen Bearbeitungen
 var fail = ; //Liste der gescheiterten Bearbeitungen
 var stat = {}; //Objekt mit allen Bearbeitungen
 var cb = function (status, title) {
     stat = status;
     if (status == 'success') {
        success++;
     } else {
        fail.push(title);
     }
     if (success + fail.length == count) callback(count, success, fail, stat);
 };
 var is_func = $.isFunction(param);
 $.eachAsync(titles, {delay: delay * 1000, bulk: 0, loop: function (i, title) {
   var p = is_func ? param(title) : param;
   p.title = title;
   if (p.method === undefined) p.method = 'API';
   p.callback = cb;
   if (!autoedit.edit(p)) cb('error', title);
   }});
},

/*
 * bearbeitet eine Reihe von Seiten nacheinander, bricht beim ersten Fehler ab
 *
 * Parameter
 *  params:       array aus objects: Parameter für edit
 *  autosave:     mixed
 *                  boolean: Bearbeitung automatisch abspeichern? (gültig für alle Seiten)
 *                  array:   diese Angabe für jede Seite einzeln
 *  callback:     function, optional, wird aufgerufen mit
 *                           dem Resultat der letzten Bearbeitung und
 *                           der Anzahl aller Bearbeitungen
 *  replace_last: boolean, optional, letzte Bearbeitung wird im aktuellen Fenster durchgeführt
 */
edit_series: function (params, autosave, callback, replace_last) {
   var is_arr = $.isArray(autosave);
   if (typeof callback != 'function') callback = function(){};
   var count = 0, all = params.length; //Anzahl der Bearbeitungen
   var title = ''; //Titel der Seite, die evt. am Ende aufgerufen werden soll
   var cb = function (result) {
       if (result != 'success') return callback(result, count - 1);
       if (count == all) {
          if (result == 'success' && replace_last) {
             window.location.href = mw.util.getUrl(title);
             return;
          } else {
             return callback(result, count);
          }
       }
       if (!autoedit.edit(params)) callback('error', count - 1);
   };
   for (var i = 0; i < all; i++) {
       if ((is_arr && autosave) || (!is_arr && autosave)) {
          params.method = 'API';
          params.newwindow = 0;
          if (i == all - 1 && replace_last) {
             if (params.edit === undefined || params.edit == 'edit') {
                params.method = 'interface';
                params.action = 'wpSave';
             } else {
                switch (params.edit) {
                case 'block': case 'mail': title = 'User:' + (params.title || mw.config.get('wgTitle')); break;
                default: title = params.title || mw.config.get('wgPageName');
                }
             }
          }
       } else {
          params.method = 'interface';
          params.newwindow = (i == all - 1 && replace_last) ? 0 : 1;
          if (params.edit == 'revert' && params.revert.rollback) { //rollback über Interface beinhaltet automatische Speicherung
             var user = params.revert.rollback;
             params.revert = {fakerollback: user};
          }
       }
       params.callback = cb;
   }
   cb('success');
}

};

libs.autoedit = autoedit;
$(document).trigger('loadWikiScript', );

//autoedit.edit({title: 'Diskussion:Hauptseite', transcluded: true, change: 'drübersetzen\n', section: 'T-2', summary: 'Test',create: true, newwindow: 1, callback: function(status, titel, par){/*if (status!='succes')*/ alert(status+' (' + titel +'): ' + par);}});
//autoedit.massedit(, function(x){return {change:x, summary:x, create: true, method: 'interface', newwindow: 1, action:'wpPreview'};}, function(a,b,c,d){alert(a+'/'+b+': '+c+d);}, 5);
//autoedit.del({title: 'Diskussion:Hauptseite', summary: 'Ohne Grund', method: 'interface', newwindow: 1});
//autoedit.edit_series(, false, function(a, b){alert(a + b);});

})(jQuery, mw.libs);
//</nowiki>