marcin348gmail.com / Ingress Mission Authoring Tool - Sort, Filter and Count with Mosaic Preview

// ==UserScript==
// @name         Ingress Mission Authoring Tool - Sort, Filter and Count with Mosaic Preview
// @namespace    http://tampermonkey.net/
// @version      0.10.11
// @description  Ingress Mission Authoring Tool (IMAT): Sort, filter and count all missions with mosaic preview
// @author       Awalon
// @coauthor     Hrw
// @licence      MIT
// @match        https://mission-author-dot-betaspike.appspot.com/*
// @grant        none
// ==/UserScript==

// Changelog
//
// 2018.03
// - Hrw wrote script which sorted missions
// - Greenbay added counting
//
// 2018.04
// - Hrw added filtering of missions
// - Hrw noticed that once you change list of missions buttons do not work
// - Awalon wrote own version of script based on older version
//
// 2018.05
// - Hrw took Awalon's version and added filtering
//   - fixed drafts of published missions
//   - added count of filtered entries
//   - added Polish words to mosaic regexps
//

(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=true, filter='') {
        // 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-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, "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 + $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;
            };

            var displayed = 0;
            var all_titles = '';

            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"];

                    if (m_title.toLowerCase().includes(filter.toLowerCase())) {
                        $(mission_item)[0].style.display = '';
                        displayed++;
                        //console.log(displayed + 'enable ' + m_id + ' - ' + m_title);
                    }
                    else
                    {
                        $(mission_item)[0].style.display = 'none';
                        //console.log('disable ' + m_id + ' - ' + m_title);
                    }

                    all_titles = all_titles + '~' + m_title;

                    //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;
                });
            }
            //console.log('for hrw: ' + all_titles);

            // 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 draft_of_published_mission_count = 0;
            var published_count = 0;

            var order_by = new Array ("published", "draft_of_published_mission", "submitted", "draft");
            if (draftFirst) {
                order_by = new Array ("draft", "draft_of_published_mission", "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 == "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 + published_count;
            var remains = 150 - (submitted_count + draft_of_published_mission_count + published_count);
            var stats = '';

            if (filter) {
                stats = '(' + displayed + '/' + mtotal + ')';
            }
            else
            {
                stats = mtotal;
            }
            stats = stats + ' Missions (' + draft_count + " Draft, " + draft_of_published_mission_count + " Review, " + submitted_count + " Submitted, " + 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("<span style='padding: 0 0.5em;' class='sort'>Filter  <input class='sort' type='text' id='filter_missions'></span>");
    $(".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); });

    $('#filter_missions').on('keypress', function (key) {
        if(key.which === 13){

            sortMissionList(true, this.value);
        }
    });

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