awalon / Ingress Mission Authoring Tool - Sort, Count and Preview

// ==UserScript==
// @name         Ingress Mission Authoring Tool - Sort, Count and Preview
// @namespace    http://tampermonkey.net/
// @version      0.9.11
// @description  Ingress Mission Authoring Tool (IMAT): Sort and count all missions with mosaic preview
// @author       Awalon
// @coauthor     Hrw
// @licence      MIT
// @match        https://mission-author-dot-betaspike.appspot.com/
// @grant        none
// ==/UserScript==
// Source: https://openuserjs.org/scripts/awalon/Ingress_Mission_Authoring_Tool_-_Sort_and_Count
//
// Changelog
//
// 2018.03
// - Hrw wrote script which sorted missions
// - Greenbay added counting
//
// 2018.04
// - Awalon wrote own version of script based on older version
//
// 2018.05
// - Hrw took Awalon's version and
//   - added drafts of published missions   (merged into this version)
//   - added Polish words to mosaic regexps (merged into this version)
//

(function () {
    'use strict';
    var mtotal = 0;

    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    var pad = function(no, size) {
        var s = String(no);
        while (s.length < (size || 2)) {s = "0" + s;}
        return s;
    };

    function sortMissionList (draftFirst) {
        // get mission list: https://mission-author-dot-betaspike.appspot.com/api/author/getMissionsList
        var $published                  = $("div.mission-list-item-published");
        var $submitted                  = $("div.mission-list-item-submitted");
        var $submitted_published        = $("div.mission-list-item-submitted_and_published");
        var $draft_of_published_mission = $("div.mission-list-item-draft_of_published_mission");
        var $drafts                     = $("div.mission-list-item-draft");

        // build index with mission type (single or banner)
        var all_list  = new Array(
                                    new Array($published, "published", new Array()),
                                    new Array($submitted, "submitted", new Array()),
                                    new Array($submitted_published,        "submitted_and_published",    new Array()),
                                    new Array($draft_of_published_mission, "draft_of_published_mission", new Array()),
                                    new Array($drafts, "draft", new Array())
          );
        var mission_book_title = new Array();
        var mission_book_id    = new Array();

        var counter = $published.length + $submitted.length + $submitted_published.length + $draft_of_published_mission.length + $drafts.length;
        if (counter > 0) {

          // normalize mission name (extract numbers and move them to the end, for grouping)
          var readMissionTitle = function (m) {
              // - extract number
              var no_all = 0;
              var no_cur = 0;
              var no_re2 = /(?:(?:[:\-\s]+\s*)(\d+)(?:\s*?(?:(?:(?:[/\-\.])|(?:of|von|z|na))\s*?)+(\d+))?)\s*$/; // Number suffix
              var no_re1 = /^[#\s]*(?:(\d+)(?:\s*?(?:(?:(?:[/\-\.])|(?:of|von|z|na))\s*?)+(\d+))?(?:[:\-\.\s]+\s*))/; // Number prefix
              var no_re0 = /(?:(\d+)(?:\s*?(?:(?:(?:[/\-\.])|(?:of|von|z|na))\s*?)+(\d+))?)/; // Number somewhere
              var no_re  = no_re2;
              var note   = "### Suffix";
              var no     = m.match(no_re);
              if (no) {
                  no_all = no.length > 1 ? (no[2] ? no[2] : 0) : 0;
                  no_cur = no.length > 0 ? (no[1] ? no[1] : 0) : 0;
              } else {
                  note = "### Prefix";
                  no_re  = no_re1;
                  no     = m.match(no_re);
                  if (no) {
                      no_all = no.length > 1 ? (no[2] ? no[2] : 0) : 0;
                      no_cur = no.length > 0 ? (no[1] ? no[1] : 0) : 0;
                  } else {
                      note = "### Somewhere";
                      no_re  = no_re;
                      no     = m.match(no_re);
                      if (no) {
                          no_all = no.length > 1 ? (no[2] ? no[2] : 0) : 0;
                          no_cur = no.length > 0 ? (no[1] ? no[1] : 0) : 0;
                      }
                  }
              }
              // - remove number from name
              var name   = m.replace(no_re, "").toLowerCase().replace(/[\s-:?]*$/, '').replace(/^[\s-:?]*/, '');
              var idx    = pad(no_all, 4) + "|" + pad(no_cur, 4);
              var id     = name  + "#" + idx;
              //console.log ("'" + m + "'; '" + id + "'");
              var ret = new Array();
              ret["id"]    = id;
              ret["idx"]   = idx;
              ret["name"]  = name;
              ret["input"] = m;
              return ret;
          };


          for (var group_idx in all_list) {
            var group = all_list[group_idx];

            // get/normalize data
            $(group[0]).each (function(){
              var mission_item = $(this);
              var m_item    = $(mission_item).find(".mission-title-" + group[1]);
              var m_img     = $(mission_item).find(".mission-image");
              var m_img_src = $(m_img).attr('src');
              var m_title   = $(m_item).text(); //.trim();
              var m_info    = readMissionTitle(m_title);
              var m_id      = m_info["id"];
              var m_name    = m_info["name"];
              //console.log(m_id + "######" + m_title);
              mission_book_title[m_title] = new Array();
              mission_book_title[m_title]["info"]  = m_info;
              mission_book_title[m_title]["item"]  = $(mission_item);
              mission_book_title[m_title]["title"] = m_title;
              mission_book_title[m_title]["image"] = m_img_src;
              if (typeof mission_book_id[m_name] == "undefined") {mission_book_id[m_name] = new Array(); mission_book_id[m_name]["count"] = 0;}
              mission_book_id[m_name]["count"]++;
              // add back link to banner parts
              var banner_part = new Array();
              banner_part["idx"]  = m_info["idx"];
              banner_part["item"] = $(mission_item);
              banner_part["part"] = mission_book_title[m_title];
              if (typeof mission_book_id[m_name]["banner_parts"] == "undefined") {mission_book_id[m_name]["banner_parts"] = new Array();}
              mission_book_id[m_name]["banner_parts"][m_info["idx"]] = banner_part;
            });
          }


          // prepare mission type
          for (var m_title in mission_book_title) {
            var m_info = mission_book_title[m_title]["info"];
            var m_name = m_info["name"];

            if (mission_book_id[m_name]["count"] > 1) {
              mission_book_id[m_name]["type"] = 'banner';
              mission_book_title[m_title]["sortby"] = m_info["id"];;
            } else {
              mission_book_id[m_name]["type"] = 'single';
              mission_book_title[m_title]["sortby"] =  m_info["input"].trim();
            }
          }

          /*
          for (var m_title in mission_book_title) {
            var m_name = mission_book_title[m_title]["info"]["name"];
            console.log (m_name + " => " + m_title.trim() + " [[" + mission_book_id[m_name]["type"] + "]] sort by [" + mission_book_title[m_title]["sortby"] + "]");
          }
          */


          // Sort missions
          for (var group_idx in all_list) {
            var group = all_list[group_idx];
            group[2] = group[0].sort(function (a, b) {
              var aTitle = mission_book_title[$(a).find(".mission-title-" + group[1]).text()]["sortby"];
              var bTitle = mission_book_title[$(b).find(".mission-title-" + group[1]).text()]["sortby"];
              return aTitle.localeCompare(bTitle);
            });
          }


          // Apply sorted lists
          var draft_count = 0;
          var submitted_count = 0;
          var submitted_and_published_count = 0;
          var draft_of_published_mission_count = 0;
          var published_count = 0;

          var order_by = new Array ("published", "submitted_and_published", "draft_of_published_mission", "submitted", "draft");
          if (draftFirst) {
            order_by = new Array ("draft", "draft_of_published_mission", "submitted_and_published", "submitted", "published");
          }
          $(order_by).each (function(){
            var order_by_key = this;
            for (var group_idx in all_list) {
              var group = all_list[group_idx];

              if (order_by_key == group[1]) {
                group[2].appendTo(".missions-list");

                // counter for stats
                if (order_by_key == "draft")                           { eval(order_by_key + "_count = " + group[2].length + ";"); }
                else if (order_by_key == "submitted")                  { eval(order_by_key + "_count = " + group[2].length + ";"); }
                else if (order_by_key == "submitted_and_published")    { eval(order_by_key + "_count = " + group[2].length + ";"); }
                else if (order_by_key == "draft_of_published_mission") { eval(order_by_key + "_count = " + group[2].length + ";"); }
                else if (order_by_key == "published")                  { eval(order_by_key + "_count = " + group[2].length + ";"); }
              }

            }
          });


          // stats
          var mtotal  = draft_count + submitted_count + draft_of_published_mission_count + submitted_and_published_count + published_count;
          var remains = 150 - (submitted_count + draft_of_published_mission_count + submitted_and_published_count + published_count);
          var stats = mtotal + ' Missions (' + draft_count + " Draft, " + submitted_and_published_count + " Review, " + submitted_count + " Submitted, " + draft_of_published_mission_count + " Draft of Published, " + published_count + " Published), " + remains + " Missions left (without Drafts)";
          $("#mcounter").text(stats);


        // Add preview
        // - loop over missions
        for (var mission_name in mission_book_id) {
            var mission = mission_book_id[mission_name];
            var mission_no = new Array();
            for (var mission_key in mission.banner_parts) {
                mission_no.push(mission_key);
            }
            var $mission_no_sorted = $(mission_no).sort(function (a, b) {
                return b.localeCompare(a, undefined, {numeric: true, sensitivity: 'base'});
            });
            var $mp_html = "";
            // - loop over each mosaic mission
            if ($mission_no_sorted.length > 1) {
                // - collect images
                $($mission_no_sorted).each (function(){
                  var mission_no_idx = this;
                    //var m = mission_book_id[mission_name]["banner_parts"][mission_no_idx]; //mission[mission_no_sorted[mission_no_idx]];
                    var m_title = mission_book_id[mission_name]["banner_parts"][mission_no_idx]["part"]["title"];
                    var m_image = mission_book_id[mission_name]["banner_parts"][mission_no_idx]["part"]["image"];
                    $mp_html += "<img title='" + m_title + "' src='" + m_image + "' />";
                });
                // - append preview to all missions, which are part of mosaik
                $($mission_no_sorted).each (function(){
                    var mission_no_idx = this;
                    var m = mission_book_id[mission_name]["banner_parts"][mission_no_idx]["item"];
                    var m_prev = $(m).find(".banner-preview");
                    if ($(m_prev).length == 0) {
                        var mission_id = mission_name + "-" + mission_no_idx;
                        var mid_html = mission_id.replace(/[^a-z0-9]/g, '');
                        //console.log (mission_no_idx + ": " + mid_html);
                        $(m).find(".info").append("<div class='banner-preview'><button title='Preview' onclick=\"$('#" + mid_html + "').toggle()\">"
                                                  +  "<svg style='width: 10px;height:10px;vertical-align:middle;position:relative;top:-1px;' width='10' height='10'>"
                                                  + "<use xlink:href='#arrow-down-mini' /></svg>"
                                                  + "</button><div id='" + mid_html + "' class'banner-preview-image' style='display:none;'>" + $mp_html + "</div></div>");
                    } else {
                        m_prev = $(m).find(".banner-preview-image");
                        $(m_prev).html($mp_html);
                    }
                });
            }
        }

        }
    }

    // add SVG
    $("head").append("<svg xmlns='http://www.w3.org/2000/svg' width='0' height='0' display='none'/>");
    var my_svg = $("head").children(':last');
    my_svg.html('<symbol id="arrow-down-mini" viewBox="-122.9 121.1 105.9 61.9"><path d="M-63.2 180.3l43.5-43.5c1.7-1.7 2.7-4 2.7-6.5s-1-4.8-2.7-6.5c-1.7-1.7-4-2.7-6.5-2.7s-4.8 1-6.5 2.7L-69.9 161l-37.2-37.2c-1.7-1.7-4-2.7-6.5-2.7s-4.8 1-6.5 2.6c-1.9 1.8-2.8 4.2-2.8 6.6 0 2.3.9 4.6 2.6 6.5 11.4 11.5 41 41.2 43 43.3l.2.2c3.6 3.6 10.3 3.6 13.9 0z" fill="#5afbea"></path></symbol>');

    // add CSS classes
    $("head").append('<style type="text/css"></style>');
    var my_css = $("head").children(':last');
    my_css.html('button.sort{margin-top:6px;} button.stats{background-color:black;} .banner-preview{width:400px;text-align:right;top:-20px;height:4px;position:relative;display:inline-block;} .banner-preview button{padding:2px 7px;}');

    // navbar-header vs. navbar-fixed-top
    $(".navbar-header").append("<button class='sort'       id='sort_draft'    >Sort (Drafts first)</button>");
    $(".navbar-header").append("<button class='sort'       id='sort_published'>Sort (Drafts last)</button>");
    $(".navbar-header").append("<button class='sort stats' id='mcounter'      >Missions</button>");

    $('#sort_draft').on('click', function () { sortMissionList (true); });
    $('#sort_published').on('click', function () { sortMissionList (false); });

    for (var i = 0; i < 20; i++) {
        if (mtotal == 0) {
            sleep(2000).then( () => { sortMissionList (true); } );
        }
    }
})();