Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
/* THIS IS NOT A GADGET
 * I’m not sure where this page came from, but as of 2020-07-08,
 * it’s not in the gagdet definitions,
 * it doesn’t seem to have been mentioned in the gadget definitions’ history / edit summaries either,
 * nor does it seem to be referenced in other MediaWiki: or User: pages.
 * --Lucas Werkmeister 19:55, 8 July 2020 (UTC)
 */
//<nowiki>

$(document).ready( function() {
  if ( mw.config.get('wgAction') == 'view' ) mmtags_init() ;
} ) ;

var mmtags_api_url ;
var mmtags_taglist ;
var mmtags_selected ;
t
function mmtags_ucfirst( s ) {
  return s.substr(0,1).toUpperCase() + s.substr(1,s.length);
}

function mmtags_init () {
  mmtags_taglist = new Array() ;
  mmtags_selected = new Array() ;
  mmtags_api_url = mw.config.get('wgServer')+wgScript ;
  mmtags_api_url= mmtags_api_url.replace ( "/index.php" , "/api.php" ) ;

  var html = "<h5>Tags</h5>" ;
  html += "<div id='div_tags_standby' style='text-align:center;color:white;background:red;display:none'>Standby...</div>" ;
  html += "<div class='' style='font-size:80%' id='div_tags_body'><ul id='ul_tags'>" ;

  $("#catlinks a").each ( function () {
    var target = $(this).html() ;
    if ( target.substr ( 0 , 4 ) != 'TAG:' ) return ;
    var the_tag = target.substr ( 4 ) ;
    mmtags_taglist.push ( the_tag ) ;
    html += mmtags_get_tag_html ( the_tag ) ;
    var obj = $(this).parent() ;
    if ( obj.next() ) obj.next().remove() ; // FIXME : This is supposed to remove the "|", but doesn't work for some reason
    obj.remove() ;
  } ) ;

  html += "</ul>" ;

  html += "<div id='tags_cb_actions' style='display:none;font-size:80%;padding-top:5px'>All: " ;
  html += "<a href='#' onclick='mmtags_remove_multiple(); return false'>remove</a> | " ;
  html += "<a href='#' onclick='mmtags_on_subset(); return false'>subset</a>" ;
  html += "</div>" ;

  html += "<a href='#' id='tag_add_span_link' onclick='mmtags_toggle_add(); return false'>+</a>" ;
  html += "<div id='tag_add_span' style='display:none;font-size:80%'>" ;
  html += mmtags_get_blank_add () ;
  html += "</div>" ;

  html = "<div id='p-tags' class='portal'>" + html + "</div>" ;
  $('#mw-panel').append(html) ;
}

function mmtags_on_subset () {
  // tools.wmflabs.org link not updated for toolforge.org because catscan2 is dead --Lucas Werkmeister 19:55, 8 July 2020 (UTC)
  var url = "//tools.wmflabs.org/catscan2/catscan2.php?language=commons&project=wikimedia&categories=&ns%5B" + mw.config.get('wgNamespaceNumber') ;
  url += "%5D=1&ext_image_data=1&doit=1&categories=" ;
  var t = [] ;
  $.each ( mmtags_selected , function () { t.push ( "TAG:" + this ) ; } ) ;
  url += escape ( t.join ( "\n" ) ) ;
  url += "#results" ;
  window.open(url) ;
}

function mmtags_get_blank_add () {
  var ret = "<form><div id='tags_add_div'><input type='text' size='12' /></div>" ;
  ret += "<a href='#' onclick='mmtags_add_new_add(); return false'>+</a>" ;
  ret += "<br/><input type='submit' value='Add' onclick='mmtags_add_new(); return false;' />" ;
  ret += " <a href='#' onclick='mmtags_toggle_add(); return false'>Never mind</a></div></form>" ;
  return ret ;
}

function mmtags_add_new_add () {
  $("#tags_add_div").append ( "<br/><input type='text' size='10' />" ) ;
}

function mmtags_get_tag_html ( tag ) {
  var ret = "<li id='li_tag_"+mmtags_tag2id(tag)+"' style='padding-top:0px;padding-bottom:0px;spacing-top:0px;spacing-bottom:0px'>" ;
  ret += "<input type='checkbox' tagname=\"" + tag + "\" id='cb_tag_" + mmtags_tag2id(tag) + "' onclick='mmtags_checkbox_changed(); return true' />" ;
  ret += "<span class='tag_sidebar_name'><a style='text-decoration:none' href='"+wgScript+"?title=Category:TAG:"+tag+"' text='Other items tagged with \""+tag+"\"'>" + tag + "</a></span>" ;
  ret += "<a href='#' style='text-decoration:none;padding-left:10px' onclick='mmtags_on_remove_tag(\""+tag+"\");return false;' text='Remove tag \""+tag+"\"'>&times;</a>" ;
  ret += "</li>" ;
  return ret ;
}

function mmtags_tag2id ( tag ) {
  tag = tag.replace ( ' ' , '_' ) ;
  tag = tag.replace ( "'" , '_' ) ;
  tag = tag.replace ( '"' , '_' ) ;
  return tag ;
}

function mmtags_remove_multiple () {
  mmtags_on_remove_tags ( mmtags_selected ) ;
}

function mmtags_on_remove_tag ( tag ) {
  var tags = [ tag ] ;
  mmtags_on_remove_tags ( tags ) ;
}

function mmtags_on_remove_tags ( tags ) {
  mmtags_lock () ;
  var rnd = (new Date()).getTime() ; // Hack around broken caching
  $.get( mw.config.get('wgServer')+wgScript , { action : 'raw' , title : mw.config.get('wgPageName') , rand : rnd } , function (data) {
    var n = " " + $.trim ( data ) + " " ;
    var success_n = true ;
    var failtag ;
    $.each ( tags , function ( tag_id ) {
      var tag = tags[tag_id] ;
      if ( false == success_n ) return ;
      n = n.split("[[Category:TAG:"+tag+"]]") ;
      if ( n.length != 2 ) {
        success_n = false ;
        failtag = tag ;
        return ;
      }
      n = n.join ( '' ) ;
    } ) ;

    if ( false == success_n ) {
      alert ( "Unable to find tag " + failtag + " in wiki source - no tag removed!" ) ;
      mmtags_unlock () ;
      mmtags_checkbox_changed () ;
      return ;
    }

    n = $.trim ( n ) ;

    $.getJSON ( mmtags_api_url , { titles : mw.config.get('wgPageName') , action : 'query' , prop : 'info' , intoken : 'edit' , format : 'json' } , function ( data ) {
      var et ;
      $.each ( data.query.pages , function () { et = this.edittoken ; } ) ;
      // TODO check "et" to be defined

      $.post( mmtags_api_url , { action : 'edit' , title : mw.config.get('wgPageName') , text : n , summary : "Removing tag(s) "+tags.join(', ') , minor : 1 , token : et } , function ( data ) {
        // TODO check success
        $.each ( tags , function () {
          var tag = this ;
          mmtags_taglist = $.grep(mmtags_taglist, function(value){ return value != tag; });
          $("#li_tag_"+mmtags_tag2id(tag)).remove() ;
        } ) ;
        mmtags_unlock() ;
      } ) ;
    } ) ;
  } ) ;
  return false ;
}

function mmtags_lock () {
  $('#p-tags :input').prop('disabled', true);
//  $('#div_tags_body').hide() ;
  $('#div_tags_standby').show() ;
}

function mmtags_unlock () {
  $('#p-tags :input').prop('disabled', false);
  $('#div_tags_standby').hide() ;
//  $('#div_tags_body').show() ;
}

function mmtags_toggle_add () {
  $('#tag_add_span_link').toggle() ;
  $('#tag_add_span').toggle() ;
  if ( $('#tag_add').css("display") != 'none' ) {
    $("#tag_add_span").html ( mmtags_get_blank_add() ) ;
    $('#tags_add_div :first-child').focus() ;
  }
}

function mmtags_add_new () {
  var add_text = "" ;
  var summary_text = "" ;
  var tl = mmtags_taglist ;
  var new_tags = [] ;
  $('#tags_add_div input').each ( function () {
    var nt = $(this).val() ;
    nt = nt.replace ( '_' , ' ' ) ;
    nt = nt.replace ( '"' , '' ) ;
    nt = $.trim ( nt ) ;
    nt = mmtags_ucfirst ( nt ) ;
    if ( nt == '' ) return ;
    if ( -1 != $.inArray ( nt , tl ) ) return ;
    tl.push ( nt ) ;
    new_tags.push ( nt ) ;
    add_text += "\n[[Category:TAG:" + nt + "]]" ;
    if ( summary_text != "" ) summary_text += ", " ;
    summary_text += nt ;
  } ) ;

  if ( add_text == '' ) {
    mmtags_toggle_add () ;
    return ;
  }

  mmtags_taglist = tl ;

  summary_text = "Adding tag(s) " + summary_text ;

  mmtags_lock () ;
  $.getJSON ( mmtags_api_url , { titles : mw.config.get('wgPageName') , action : 'query' , prop : 'info' , intoken : 'edit' , format : 'json' } , function ( data ) {
    var et ;
    $.each ( data.query.pages , function () { et = this.edittoken ; } ) ;
    // TODO check "et" to be defined
    
    $.post( mmtags_api_url , { action : 'edit' , title : mw.config.get('wgPageName') , appendtext: add_text , summary : summary_text , minor : 1 , token : et } , function ( data ) {
      // TODO check for error
      $.each ( new_tags , function () {
        mmtags_taglist.push ( this ) ;
        var html = mmtags_get_tag_html ( this ) ;
        $('#ul_tags').append ( html ) ;
      } ) ;
      mmtags_unlock() ;
      mmtags_toggle_add () ;
    } ) ;
  } ) ;
}

function mmtags_checkbox_changed () {
  mmtags_selected = [] ;
  $('#ul_tags input:checked').each ( function () {
    mmtags_selected.push ( $(this).attr('tagname') ) ;
  } ) ;
  if ( 0 == mmtags_selected.length ) {
    $("#tags_cb_actions").hide() ;
    return ;
  }
  $("#tags_cb_actions").show() ;
}
//</nowiki>