holatuwol / Nikki's Info Add By Number

// ==UserScript==
// @name        Nikki's Info Add By Number
// @namespace   holatuwol
// @version     5.8
// @license     0BSD
// @downloadURL https://openuserjs.org/install/holatuwol/Nikkis_Info_Add_By_Number.user.js
// @updateURL   https://openuserjs.org/meta/holatuwol/Nikkis_Info_Add_By_Number.meta.js
// @match       https://ln.nikkis.info/*
// @grant       unsafeWindow
// @require     https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js
// ==/UserScript==
/**
 * Compiled from TypeScript
 * https://github.com/holatuwol/myni-userscript
 */ 
var styleElement = document.createElement('style');
styleElement.textContent = "\n.have-witem.safe-decompose {\n  background-color: #efe !important;\n}\n\n.secondary-content div {\n  text-align: right;\n}\n\n.insert-by-number {\n  margin-top: 2em;\n}\n\n.transitive-dependencies,\n.crafting-path > div {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n}\n\n.transitive-dependencies {\n  flex-wrap: wrap;\n  max-height: 50vh;\n  overflow-y: auto;\n}\n\n.crafting-path {\n  display: flex;\n  flex-direction: column;\n  max-height: 20vh;\n  overflow: auto;\n}\n\n.crafting-path .needed {\n  text-align: center;\n}\n\n.crafting-path .needed .arrow {\n  font-size: 3em;\n  line-height: 0.2em;\n}\n\n.transitive-dependencies .icon,\n.crafting-path .icon {\n  position: relative;\n}\n\n.transitive-dependencies .icon-room,\n.crafting-path .icon-room {\n  padding: 5px 26px 15px 5px;\n}\n\n.transitive-dependencies.show-path .witem {\n  opacity: 0.2;\n}\n\n.transitive-dependencies.show-path .witem.active {\n  opacity: 1.0;\n}\n\n.attr-metadata,\n.drop-metadata,\n.suit-metadata {\n  font-size: 0.8rem;\n  line-height: 1rem;\n}\n\n.stage-progress {\n  display: flex;\n  justify-content: flex-end;\n  margin-left: auto;\n}\n\n.stage-progress input[type=text] {\n  height: 2rem;\n  width: 5rem;\n  text-align: right;\n}\n\n.sitem .di img,\n.witem .di img,\n.drop-metadata img {\n  vertical-align: text-bottom;\n}\n\nimg.suit-dia.store:not(.have-suit-part) {\n  border: solid 1px;\n}\n\n.base-score .new-icon {\n  position: relative;\n  padding: 0.2em 1em 0.2em 1em;\n  width: auto;\n}\n";
var head = document.querySelector('head');
head.appendChild(styleElement);
var pathParts = document.location.pathname.split('/');
var checkSessionURL = 'https://my.nikkis.info/checksession';
var getWardrobeURL = 'https://my.nikkis.info/getwardrobe/ln';
var updateWardrobeURL = 'https://my.nikkis.info/updatewardrobe/ln';
/**
 * Creates a button.
 */
function createButton(text, callback) {
    var button = document.createElement('a');
    button.classList.add('waves-effect');
    button.classList.add('waves-light');
    button.classList.add('btn');
    button.classList.add('pink');
    button.classList.add('lighten-2');
    button.addEventListener('click', callback);
    button.textContent = text;
    return button;
}
/**
 * Function to retrieve the section with the specified header.
 */
function getSection(container, headerName, referenceElement) {
    if (referenceElement === void 0) { referenceElement = null; }
    var headers = Array.from(container.querySelectorAll('h5'));
    for (var i = 0; i < headers.length; i++) {
        var content = (headers[i].textContent || '').trim();
        if (content == headerName) {
            return headers[i].parentElement;
        }
    }
    if (!referenceElement) {
        return null;
    }
    var divider = document.createElement('div');
    divider.classList.add('divider');
    var section = document.createElement('div');
    section.classList.add('section');
    var header = document.createElement('h5');
    header.classList.add('item-section-head');
    header.textContent = headerName;
    section.appendChild(header);
    var referenceElementParentElement = referenceElement.parentElement;
    referenceElementParentElement.insertBefore(divider, referenceElement);
    referenceElementParentElement.insertBefore(section, referenceElement);
    return section;
}
/**
 * Retrieves the current wardrobe, and then invokes the provided
 * callback function, passing the current wardrobe.
 */
function getWardrobe(callback) {
    jQuery.getJSON(checkSessionURL, function (d1) {
        if (!d1['authenticated']) {
            return;
        }
        jQuery.getJSON(getWardrobeURL, function (d2) {
            callback(d2.wardrobe);
        });
    });
}
/**
 * Make an XMLHttpRequest, and then invoke the specified callback once you have
 * constructed a document from it.
 */
function processXMLHttpRequest(href, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', href);
    // https://stackoverflow.com/questions/20583396/queryselectorall-to-html-from-another-page
    xhr.onload = function () {
        var container = document.implementation.createHTMLDocument().documentElement;
        container.innerHTML = xhr.responseText;
        callback(container);
    };
    xhr.send(null);
}
/**
 * Prevent event propagation.
 */
function stopPropagation(e) {
    e.stopPropagation();
}
/**
 * Adds new items to the wardrobe, if action is '+',
 * or removes old items from the wardrobe, if action is '-'
 */
function updateWardrobe(selectItems, action, callback) {
    getWardrobe(function (oldWardrobe) {
        var oldWardrobeSet = new Set(oldWardrobe);
        var selectActions;
        var selectItemList = Array.from(selectItems);
        if (action == '+') {
            selectActions = selectItemList.filter(function (x) { return !oldWardrobeSet.has(x); }).map(function (x) { return action + x; });
        }
        else if (action == '-') {
            selectActions = selectItemList.filter(function (x) { return oldWardrobeSet.has(x); }).map(function (x) { return action + x; });
        }
        else {
            return;
        }
        jQuery.ajax({
            type: 'POST',
            contentType: 'application/json; charset=utf-8',
            url: updateWardrobeURL,
            data: JSON.stringify({ 'select_actions': selectActions })
        }).done(function () {
            if (callback) {
                callback(selectItemList, action);
            }
            Materialize.toast('Selections saved!', 4000);
        });
    });
}
var styleTags = [
    'gorgeous', 'simple', 'elegance', 'lively', 'mature', 'cute', 'sexy', 'pure', 'warm', 'cool'
];
var styleTagSet = new Set(styleTags);
/**
 * A comparator to make sure style tags are returned in order.
 */
function styleTagComparator(x, y) {
    return styleTags.indexOf(x) - styleTags.indexOf(y);
}
var FilterableElement = /** @class */ (function () {
    function FilterableElement(element) {
        this.element = element;
        var titleElement = element.querySelector('span.title.truncate');
        this.lowerCaseName = (titleElement && titleElement.textContent || '').trim().toLowerCase();
    }
    ;
    FilterableElement.prototype.filter = function (text) {
        if (!text || this.lowerCaseName.indexOf(text) != -1) {
            this.element.style.display = 'block';
            return true;
        }
        else {
            this.element.style.display = 'none';
            return false;
        }
    };
    ;
    return FilterableElement;
}());
var FilterableSection = /** @class */ (function () {
    function FilterableSection(section, selector) {
        this.section = section;
        this.header = section.querySelector('div.collapsible-header');
        this.body = section.querySelector('div.collapsible-body');
        var itemElements = Array.from(section.querySelectorAll(selector));
        this.items = itemElements.map(function (x) { return new FilterableElement(x); });
    }
    ;
    FilterableSection.prototype.filter = function (text) {
        var count = 0;
        for (var i = 0; i < this.items.length; i++) {
            if (this.items[i].filter(text)) {
                count++;
            }
        }
        if (!text) {
            this.section.classList.remove('active');
            this.header.classList.remove('active');
            this.body.style.display = 'none';
            this.section.style.display = 'block';
        }
        else if (count > 0) {
            this.section.classList.add('active');
            this.header.classList.add('active');
            this.body.style.display = 'block';
            this.section.style.display = 'block';
        }
        else {
            this.section.classList.remove('active');
            this.header.classList.remove('active');
            this.body.style.display = 'none';
            this.section.style.display = 'none';
        }
        return count > 0;
    };
    return FilterableSection;
}());
var FilterableAccordion = /** @class */ (function () {
    function FilterableAccordion(selector, placeholder) {
        var container = document.querySelector('ul[data-collapsible]');
        if (!container) {
            return;
        }
        var containerParentElement = container.parentElement;
        if (!containerParentElement) {
            return;
        }
        this.sections = Array.from(container.querySelectorAll('li')).map(function (x) { return new FilterableSection(x, selector); });
        var filterHolder = document.createElement('div');
        filterHolder.classList.add('filter-input');
        var inputHolder = document.createElement('div');
        inputHolder.classList.add('input-field');
        this.inputField = document.createElement('input');
        this.inputField.setAttribute('placeholder', placeholder);
        inputHolder.appendChild(this.inputField);
        this.inputField.onkeyup = _.debounce(FilterableAccordion.prototype.filter.bind(this), 500);
        filterHolder.appendChild(inputHolder);
        containerParentElement.insertBefore(filterHolder, container);
    }
    ;
    FilterableAccordion.prototype.filter = function () {
        var text = this.inputField.value.toLowerCase();
        for (var i = 0; i < this.sections.length; i++) {
            this.sections[i].filter(text);
        }
    };
    ;
    return FilterableAccordion;
}());
var itemMetadata = {};
/**
 * Look up the item metadata using the wardrobe ID.
 */
function getCraftingIngredient(wid) {
    if (wid == 'future') {
        return new CraftingIngredient(wid, '#', 'Future Item', 's-future-item', null);
    }
    if (!(wid in itemMetadata)) {
        return null;
    }
    return new CraftingIngredient(wid, '/wardrobe/' + itemMetadata[wid]['href'], itemMetadata[wid]['name'], 's-' + itemMetadata[wid]['href'].replace('/', '-'), null);
}
var CraftingMetadata = /** @class */ (function () {
    function CraftingMetadata() {
    }
    return CraftingMetadata;
}());
/**
 * Add a new prototype to describe a crafting path.
 */
var CraftingPath = /** @class */ (function () {
    function CraftingPath(pathElements) {
        if (pathElements === void 0) { pathElements = []; }
        this.pathElements = [];
        this.reversed = false;
        if (!pathElements || (pathElements.length == 0)) {
            return;
        }
        for (var i = 0; i < pathElements.length; i++) {
            var pathElement = pathElements[i];
            this.pathElements.push(new CraftingIngredient(pathElement.wid, pathElement.href, pathElement.name, pathElement.icon, pathElement.needed));
        }
    }
    ;
    CraftingPath.prototype.add = function (needed, next) {
        var newPath = new CraftingPath(this.pathElements);
        var pathElements = newPath.pathElements;
        if (pathElements.length > 0) {
            pathElements[pathElements.length - 1].needed = needed;
        }
        var ingredient = getCraftingIngredient(next);
        if (ingredient) {
            pathElements.push(ingredient);
        }
        return newPath;
    };
    ;
    CraftingPath.prototype.item = function () {
        return this.reversed ? this.pathElements[0] : this.pathElements[this.pathElements.length - 1];
    };
    ;
    CraftingPath.prototype.wid = function () {
        return this.item().wid;
    };
    ;
    CraftingPath.prototype.reverse = function () {
        var pathElements = this.pathElements;
        pathElements.reverse();
        for (var i = 0; i < pathElements.length - 1; i++) {
            pathElements[i].needed = pathElements[i + 1].needed;
        }
        pathElements[pathElements.length - 1].needed = null;
        this.reversed = true;
        return this;
    };
    ;
    return CraftingPath;
}());
function pathCompare(a, b) {
    var aId = a.wid();
    var bId = b.wid();
    var aType = aId.charAt(0);
    var bType = bId.charAt(0);
    if (aType != bType) {
        return aType > bType ? 1 : -1;
    }
    var aNumber = parseInt(aId.substring(1));
    var bNumber = parseInt(bId.substring(1));
    return aNumber - bNumber;
}
var CraftingIngredient = /** @class */ (function () {
    function CraftingIngredient(wid, href, name, icon, needed) {
        this.wid = wid;
        this.href = href;
        this.name = name;
        this.icon = icon;
        this.needed = needed;
    }
    ;
    CraftingIngredient.prototype.getCraftedFromPaths = function () {
        var root = new CraftingPath();
        var graph = new CraftingGraph();
        var reversePaths = [root.add(0, this.wid)];
        for (var i = 0; i < reversePaths.length; i++) {
            var path = reversePaths[i];
            var wid1 = path.wid();
            var crafting = graph.edges[wid1];
            if (!crafting) {
                continue;
            }
            var keys2 = Object.keys(crafting);
            for (var j = 0; j < keys2.length; j++) {
                var wid2 = keys2[j];
                var needed = crafting[wid2] || 0;
                reversePaths.push(path.add(needed, wid2));
            }
        }
        reversePaths.sort(pathCompare);
        return reversePaths.map(function (x) { return x.reverse(); });
    };
    ;
    CraftingIngredient.prototype.getUsedToCraftPaths = function () {
        var root = new CraftingPath();
        var paths = [root.add(0, this.wid)];
        for (var i = 0; i < paths.length; i++) {
            var path = paths[i];
            var wid1 = path.wid();
            var crafting = itemMetadata[wid1]['crafting'];
            if (!crafting) {
                continue;
            }
            var keys2 = Object.keys(crafting);
            for (var j = 0; j < keys2.length; j++) {
                var wid2 = keys2[j];
                var needed = crafting[wid2];
                paths.push(path.add(needed, wid2));
            }
        }
        paths.sort(pathCompare);
        var futureDesignMaterial = paths
            .map(function (x) { return itemMetadata[x.wid()]['crafting_tags']; })
            .filter(function (x) { return x && new Set(x).has('future_design_material'); }).length != 0;
        if (futureDesignMaterial) {
            paths.push(root.add(0, 'future'));
        }
        return paths;
    };
    ;
    return CraftingIngredient;
}());
var CraftingDrop = /** @class */ (function () {
    function CraftingDrop(material, other) {
        this.material = material;
        this.other = Array.from(new Set(other));
        if ((material.length == 0) && (other.length == 0)) {
            this.other.push('unknown');
        }
    }
    CraftingDrop.prototype.getTotalMaterialCost = function () {
        var totalMaterialCost = {};
        for (var j = 0; j < this.material.length; j++) {
            var dropItem = this.material[j];
            var cost = dropItem.split(' ');
            var costAmount = cost[0];
            var costType = cost[1];
            totalMaterialCost[costType] = (totalMaterialCost[costType] || 0) + parseInt(costAmount);
        }
        return totalMaterialCost;
    };
    return CraftingDrop;
}());
var CraftingGraph = /** @class */ (function () {
    function CraftingGraph() {
        this.edges = {};
        var keys1 = Object.keys(itemMetadata);
        for (var i = 0; i < keys1.length; i++) {
            var wid1 = keys1[i];
            var crafting = itemMetadata[wid1]['crafting'];
            if (!crafting) {
                continue;
            }
            var keys2 = Object.keys(crafting);
            for (var j = 0; j < keys2.length; j++) {
                var wid2 = keys2[j];
                var needed = crafting[wid2];
                var innerEdge = this.edges[wid2];
                if (!innerEdge) {
                    this.edges[wid2] = innerEdge = {};
                }
                innerEdge[wid1] = needed;
            }
        }
    }
    CraftingGraph.prototype.topologicalOrdering = function (wid, visited) {
        visited.add(wid);
        var ordering = [];
        var crafting = this.edges[wid] || {};
        var keys = Object.keys(crafting);
        for (var i = 0; i < keys.length; i++) {
            if (!visited.has(keys[i])) {
                Array.prototype.push.apply(ordering, this.topologicalOrdering(keys[i], visited));
            }
        }
        ordering.push(wid);
        return ordering;
    };
    CraftingGraph.prototype.getCraftingDrops = function (wid) {
        var ordering = this.topologicalOrdering(wid, new Set());
        var visited = {};
        for (var i = 0; i < ordering.length; i++) {
            var outerWid = ordering[i];
            var metadata = itemMetadata[outerWid];
            var dropInfo = metadata['drops'] || [];
            var materialCost = dropInfo.filter(function (x) { return /^[0-9]* [A-Z]*$/.exec(x); });
            var materialCostSet = new Set(materialCost);
            var dropLocations = dropInfo.filter(function (x) { return !materialCostSet.has(x); });
            var maiden = dropLocations.filter(function (x) { return x.indexOf('maiden') == 0; }).length > 0;
            var princess = dropLocations.filter(function (x) { return x.indexOf('princess') == 0; }).length > 0;
            var goldCost = materialCost.filter(function (x) { return x.indexOf(' G') != -1; });
            var otherCost = [];
            if (goldCost.length == 0) {
                if (maiden) {
                    otherCost.push('maiden');
                    materialCost = [];
                }
                else if (princess) {
                    otherCost.push('princess');
                    materialCost = [];
                }
                else {
                    otherCost = dropLocations;
                }
            }
            else {
                materialCost = goldCost;
            }
            var crafting = this.edges[outerWid] || {};
            var innerKeys = Object.keys(crafting);
            for (var j = 0; j < innerKeys.length; j++) {
                var innerWid = innerKeys[j];
                var subcost = visited[innerWid];
                var multiplier = crafting[innerWid] || 0;
                var subcostRecipe = subcost.material.filter(function (x) { return x.indexOf(' SC') != -1; });
                var subcostNonRecipe = subcost.material.filter(function (x) { return x.indexOf(' SC') == -1; });
                Array.prototype.push.apply(materialCost, subcostRecipe);
                for (var k = 0; k < multiplier; k++) {
                    Array.prototype.push.apply(materialCost, subcostNonRecipe);
                }
                Array.prototype.push.apply(otherCost, subcost.other);
            }
            visited[outerWid] = new CraftingDrop(materialCost, otherCost);
        }
        return visited[wid];
    };
    return CraftingGraph;
}());
var dropLabelIcons = {
    'G': { 'name': 'Gold', 'location': 'Clothes Store', 'icon': '1/10/Gold.png' },
    'D': { 'name': 'Diamond', 'location': 'Clothes Store', 'icon': 'e/ea/Diamond.png' },
    'SC': { 'name': 'Starlight Coin', 'location': 'Store of Starlight', 'icon': '7/72/Starlight_Coin.png' },
    'CR': { 'name': 'Crystal Rose', 'location': 'Crystal Garden', 'icon': '4/42/Crystal_Rose.png' },
    'AC': { 'name': 'Association Coin', 'location': 'Association Store', 'icon': '9/9a/Association_Coin.png' },
    'SE': { 'name': 'Starwish Earrings', 'location': 'Association Fantasy Workshop', 'icon': '1/10/Starwish_Earrings.png' },
    'SH': { 'name': 'Starwish Hairpin', 'location': 'Association Fantasy Workshop', 'icon': '2/28/Starwish_Hairpin.png' },
    'SP': { 'name': 'Starwish Pendant', 'location': 'Association Fantasy Workshop', 'icon': 'f/fb/Starwish_Pendant.png' },
    'HR': { 'name': 'Hope Ring', 'location': 'Reconstruction', 'icon': '0/08/HopeRingIcon.PNG' },
    'RE': { 'name': 'Rebirth Earring', 'location': 'Reconstruction', 'icon': 'c/ce/RebirthEarringsIcon.PNG' },
    'EN': { 'name': 'Eternal Necklace', 'location': 'Reconstruction', 'icon': '1/12/EternalNecklaceIcon.PNG' },
    'J': { 'name': 'Jade', 'location': 'Porch of Misty', 'icon': '7/7e/Jade.png' },
    'DH': { 'name': 'Destiny Hourglass', 'location': 'Corridor of Clock', 'icon': 'c/c7/Destiny_Hourglass.png' },
    'SB': { 'name': 'Suzaku Bell', 'location': 'Tower of Zen', 'icon': '3/36/Suzaku_Bell.png' },
    'KC': { 'name': 'Karma Crystal', 'location': 'Time Yard', 'icon': '1/19/Karma_Crystal.png' },
    'CS': { 'name': 'Crystal Shoes', 'location': 'Room of Cinderella', 'icon': '3/3a/Crystal_Shoe.png' }
};
/**
 * Replaces the drop information label with one that uses
 * an icon instead of letters, to make it easier to know
 * what it is visually.
 */
function updateDropLabelWithIcon(itemLabel, width) {
    var content = itemLabel.textContent || '';
    content = content.trim();
    var currencyMatcher = /^([0-9,]+)\s*([A-Z]+)$/.exec(content);
    if (!currencyMatcher) {
        return;
    }
    var currencyAmount = currencyMatcher[1];
    var currencyType = currencyMatcher[2];
    var currencyTitle = dropLabelIcons[currencyType]['name'] + ' (' + dropLabelIcons[currencyType]['location'] + ')';
    var wikiaImageURL = 'https://vignette.wikia.nocookie.net/lovenikki/images/' + dropLabelIcons[currencyType]['icon'] + '/revision/latest/scale-to-height-down/' + width;
    itemLabel.innerHTML = '<img title="' + currencyTitle + '" src="' + wikiaImageURL + '">&nbsp;' + currencyAmount;
}
/**
 * Creates a span with the given cost information.
 */
function createDropLabel(costType, amount) {
    if (amount === void 0) { amount = null; }
    var span = document.createElement('span');
    span.classList.add('di');
    if (amount) {
        span.textContent = new Intl.NumberFormat('en-US').format(parseInt(amount)) + ' ' + costType;
    }
    else {
        span.textContent = costType;
    }
    updateDropLabelWithIcon(span, 16);
    return span;
}
/**
 * Add a filter to make it easier to find specific suits.
 */
function addDropsHelper() {
    new FilterableAccordion('a.witem', 'Filter items by name');
    var itemLabels = Array.from(document.querySelectorAll('.witem .di'));
    for (var i = 0; i < itemLabels.length; i++) {
        updateDropLabelWithIcon(itemLabels[i], 20);
    }
}
var GuideItem = /** @class */ (function () {
    function GuideItem() {
    }
    return GuideItem;
}());
/**
 * Retrieve the stage base score.
 */
function getBaseScore(stage, callback) {
    jQuery.ajax({
        dataType: 'json',
        type: 'POST',
        contentType: 'application/json; charset=utf-8',
        url: 'https://my.nikkis.info/getguide/ln',
        data: JSON.stringify({ 'stage': stage }),
        success: function (data) {
            var guideItems = data['guide']['wardrobe'];
            callback(data['guide']['score'], guideItems);
        }
    });
}
/**
 * Show the provided base score on the provided cell.
 */
function showBaseScore(cell, baseScore, guideItems) {
    var hasNew = guideItems.filter(function (x) { return x["new"]; }).length > 0;
    var content = cell.querySelector('.card-content');
    var baseScoreContent = '';
    if (hasNew) {
        content.classList.add('have-witem');
        baseScoreContent = '<strong class="new-icon">New!</strong>&nbsp;';
    }
    baseScoreContent += 'Base Score: ' + baseScore;
    var baseScoreHolder = document.createElement('p');
    baseScoreHolder.classList.add('base-score');
    baseScoreHolder.innerHTML = baseScoreContent;
    content.appendChild(baseScoreHolder);
}
/**
 * Show the base score for the given stylist arena stage.
 */
function addArenaStageHelper() {
    var cells = Array.from(document.querySelectorAll('.col'));
    for (var i = 0; i < cells.length; i++) {
        var cell = cells[i];
        var guideElement = cell.querySelector('.card-action a');
        if (!guideElement) {
            continue;
        }
        var stage = guideElement.href.substring(guideElement.href.lastIndexOf('/') + 1);
        getBaseScore('arena_common_' + stage, showBaseScore.bind(null, cell));
    }
}
/**
 * Show the base score for the given commission stage.
 */
function showCommissionStageBaseScore(cell, stage) {
    if (!stage) {
        return;
    }
    if (cell.querySelector('.base-score')) {
        return;
    }
    getBaseScore('commission_common_' + stage, showBaseScore.bind(null, cell));
}
/**
 * Filter the list of stages based on the percentage.
 */
function filterCommission(actRequest, container, input) {
    container.classList.add('active');
    var body = container.querySelector('.collapsible-body');
    body.style.display = 'block';
    var cells = Array.from(body.querySelectorAll('.col'));
    var inputValue = parseInt(input.value || '0');
    var begin = 0;
    var increment = 100 / cells.length;
    var end = increment;
    for (var i = 0; i < cells.length; i++) {
        var cell = cells[i];
        if ((inputValue >= Math.floor(begin)) && (inputValue <= Math.floor(end))) {
            cell.style.display = '';
            var titleElement = cell.querySelector('.card-title');
            var stage = titleElement.textContent;
            showCommissionStageBaseScore(cell, stage);
        }
        else {
            cell.style.display = 'none';
        }
        begin += increment;
        end += increment;
    }
}
;
/**
 * Rather than having you click into each stage to figure out where you are at,
 * enter in all the percentages so you can know immediately, and see what your
 * base score is for the given stage.
 */
function addCommissionStageHelper() {
    var actRequests = [
        [4000, 4500, 5000, 5000, 5000, 5500, 6000],
        [4000, 4500, 4500, 5000, 5500, 5500, 6000],
        [4000, 4500, 4500, 5000, 5500, 5500, 6000],
        [4000, 4500, 4500, 5000, 5500, 5500, 6000],
        [4000, 4500, 4500, 5000, 5500, 5500, 6000],
        [4000, 4500, 4500, 5000, 5500, 5500, 6000],
        [4000, 4500, 4500, 5000, 5500, 5500, 6000],
        [4000, 4500, 4500, 5000, 5500, 5500, 6000],
        [4000, 4500, 4500, 5000, 5500, 5500, 6000],
        [4000, 4500, 4500, 5000, 5500, 5500, 6000]
    ];
    var headers = document.querySelectorAll('.collapsible .collapsible-header');
    for (var i = 0; i < headers.length; i++) {
        var container = document.createElement('div');
        container.classList.add('stage-progress');
        var input = document.createElement('input');
        input.setAttribute('type', 'text');
        input.addEventListener('click', stopPropagation);
        input.addEventListener('keypress', _.debounce(filterCommission.bind(null, actRequests[i], headers[i].parentElement, input), 500));
        container.appendChild(input);
        var percent = document.createElement('div');
        percent.innerHTML = '&nbsp;%';
        container.appendChild(percent);
        headers[i].appendChild(container);
    }
}
/**
 * Add a helper to the custom stage page.
 */
function addCustomStageHelper() {
    var selectWrappers = Array.from(document.querySelectorAll('div.select-wrapper'));
    if (selectWrappers.length == 0) {
        setTimeout(addCustomStageHelper, 100);
        return;
    }
    if (document.location.hash) {
        loadCustomGuide();
    }
}
/**
 * Updates the sort order for accessories.
 */
function updateMyNiGuideSortOrder(items) {
    var sortRemap = {
        1: 1101,
        2: 2101,
        3: 3101,
        4: 4101,
        5: 5101,
        6: 6101,
        7: 6201,
        8: 7001,
        10: 8101,
        20: 8102,
        28: 8103,
        29: 8104,
        11: 8201,
        12: 8301,
        13: 8302,
        14: 8401,
        15: 8402,
        16: 8403,
        17: 8501,
        18: 8502,
        33: 8503,
        19: 8601,
        21: 8701,
        22: 8702,
        23: 8703,
        24: 8704,
        25: 8705,
        26: 8706,
        27: 8707,
        30: 8708,
        31: 8709,
        32: 8801,
        9: 9101,
        34: 10101
    };
    for (var i = 0; i < items.length; i++) {
        var item = items[i];
        var subSortString = item.getAttribute('data-sortsub');
        var subSort = parseInt(subSortString);
        var gameSort = sortRemap[subSort] || 20000;
        item.setAttribute('data-sortgame', gameSort.toString());
    }
    var container = document.getElementById('myni-guide-sort');
    container.appendChild(document.createTextNode(' or '));
    var sortLink = document.createElement('a');
    sortLink.setAttribute('href', '#!');
    sortLink.onclick = guide_sort.bind(null, 'myni-guide', 'data-sortgame');
    sortLink.textContent = 'game order';
    container.appendChild(sortLink);
}
/**
 * Compare two different grades.
 */
function attachClothingAttributes(item, stageAttributes, container) {
    var table = container.querySelector('.attr-table');
    var attributes = Array.from(table.querySelectorAll('.cloth-attr')).map(function (x) { return (x.textContent || '').trim(); });
    var grades = Array.from(table.querySelectorAll('.cloth-grade')).map(function (x) { return (x.textContent || '').trim(); });
    var attributeMetadataElement = document.createElement('span');
    attributeMetadataElement.classList.add('attr-metadata');
    attributeMetadataElement.classList.add('grey-text');
    attributeMetadataElement.classList.add('text-darken1');
    for (var i = 0; i < attributes.length; i++) {
        if (!stageAttributes.has(attributes[i])) {
            continue;
        }
        var span = document.createElement('span');
        span.classList.add('di');
        span.textContent = attributes[i] + ': ' + grades[i];
        attributeMetadataElement.appendChild(span);
    }
    var annotation = item.querySelector('span.item-annot');
    if (!annotation) {
        annotation = document.createElement('span');
        annotation.classList.add('item-annot');
        item.appendChild(annotation);
    }
    annotation.appendChild(attributeMetadataElement);
}
/**
 * Adds usability improvements to MyNi guide.
 */
function addStageHelper() {
    var guide = document.getElementById('myni-guide');
    if (!guide) {
        setTimeout(addStageHelper, 1000);
        return;
    }
    var items = Array.from(guide.querySelectorAll('.witem'));
    if (items.length == 0) {
        setTimeout(addStageHelper, 1000);
        return;
    }
    updateMyNiGuideSortOrder(items);
    var checkAttributesButton = createButton('Check Stage-Matching Attributes', function () {
        checkAttributesButton.remove();
        var table = document.querySelector('.attr-table');
        var stageAttributes = new Set(Array.from(table.querySelectorAll('.cloth-attr')).map(function (x) { return (x.textContent || '').trim(); }));
        for (var i = 0; i < items.length; i++) {
            var item = items[i];
            var title = item.querySelector('.title');
            var href = title.getAttribute('href');
            processXMLHttpRequest(href, attachClothingAttributes.bind(null, item, stageAttributes));
        }
    });
    var guideParentElement = guide.parentElement;
    guideParentElement.insertBefore(checkAttributesButton, guide);
}
/**
 * Update suit selections.
 */
function updateSuitSelections(selectItemList) {
    var updatedSuits = {};
    for (var i = 0; i < selectItemList.length; i++) {
        var item = document.querySelector('img[wid="' + selectItemList[i] + '"]');
        if (!item) {
            continue;
        }
        item.classList.add('have-suit-part');
        item.setAttribute('src', '/img/suit_dia_has.png');
        var suit = item.closest('a');
        var href = suit.getAttribute('href');
        updatedSuits[href] = suit;
    }
    var keys = Object.keys(updatedSuits);
    for (var i = 0; i < keys.length; i++) {
        var suit = updatedSuits[keys[i]];
        suit.classList.remove('have-witem');
        suit.classList.add('have-sitem');
    }
}
/**
 * Adds an annotation to the suit describing one of its cost elements.
 */
function addSuitAnnotation(suit, costType) {
    var amount = suit.getAttribute('data-cost-' + costType);
    if (!amount) {
        return;
    }
    var annotation = suit.querySelector('.secondary-content .suit-metadata');
    if (!annotation) {
        annotation = document.createElement('div');
        annotation.classList.add('suit-metadata');
        var container = suit.querySelector('.secondary-content');
        container.appendChild(annotation);
    }
    annotation.appendChild(createDropLabel(costType, amount));
}
/**
 * Adds annotations to all suits describing all of its cost elements, using the
 * provided drops page content.
 */
function annotateStoreSuitItems(e) {
    getWardrobe(function (wardrobe) {
        var wardrobeSet = new Set(wardrobe);
        var pieces = Array.from(document.querySelectorAll('img[wid]'));
        for (var i = 0; i < pieces.length; i++) {
            var piece = pieces[i];
            var wid = piece.getAttribute('wid');
            var metadata = itemMetadata[wid];
            if (!metadata) {
                continue;
            }
            var dropItems = metadata['drops'];
            if (!dropItems) {
                continue;
            }
            var haveSuitPart = wardrobeSet.has(wid);
            if (haveSuitPart) {
                continue;
            }
            var suit = piece.closest('a');
            dropItems = dropItems.filter(function (x) { return /^[0-9]* [A-Z]*$/.exec(x); });
            for (var j = 0; j < dropItems.length; j++) {
                var dropItem = dropItems[j];
                var cost = dropItem.split(' ');
                var costAmount = cost[0];
                var costType = cost[1];
                if (costType == 'SC') {
                    continue;
                }
                piece.classList.add('store');
                var oldCost = parseInt(suit.getAttribute('data-cost-' + costType) || '0');
                var newCost = oldCost + parseInt(costAmount);
                suit.setAttribute('data-cost-' + costType, newCost.toString());
            }
        }
        var keys = Array.from(Object.keys(dropLabelIcons));
        var querySelector = Array.from(keys).map(function (x) { return 'a[data-cost-' + x + ']'; }).concat(['a[data-cost-tags]']).join(',');
        var dropSuits = Array.from(document.querySelectorAll(querySelector));
        for (var i = 0; i < dropSuits.length; i++) {
            for (var j = 0; j < keys.length; j++) {
                addSuitAnnotation(dropSuits[i], keys[j]);
            }
            addSuitAnnotation(dropSuits[i], 'tags');
        }
        var button = e.target;
        button.remove();
    });
}
/**
 * Update suit pieces selections (on the single suit page).
 */
function updateSuitPiecesSelections(selectItemList, action) {
    updateWardrobeSelections(null, null, selectItemList, action);
    var suit = document.querySelector('div.sitem');
    if (!suit) {
        return;
    }
    if (action == '+') {
        suit.classList.add('have-sitem');
    }
    else if (action == '-') {
        suit.classList.remove('have-sitem');
    }
}
/**
 * Adds the suit to the wardrobe.
 */
function addSuitToWardrobe() {
    var suitItems = Array.from(document.querySelectorAll('div.witem'));
    var suitPieces = suitItems.map(function (x) { return x.getAttribute('wid'); });
    updateWardrobe(new Set(suitPieces), '+', updateSuitPiecesSelections);
}
/**
 * Adds the suit to the wardrobe.
 */
function removeSuitFromWardrobe() {
    var suitItems = Array.from(document.querySelectorAll('div.witem'));
    var suitPieces = suitItems.map(function (x) { return x.getAttribute('wid'); });
    updateWardrobe(new Set(suitPieces), '-', updateSuitPiecesSelections);
}
/**
 * Adds the crafting cost to all items.
 */
function annotateSuitMaterialCost(graph, suitContainer, e) {
    var items = Array.from(suitContainer.querySelectorAll('.witem'));
    var totalSuitMaterialCost = {};
    var totalSuitOtherCost = new Set();
    var itemWids = items.map(function (x) { return x.getAttribute('wid'); });
    for (var i = 0; i < items.length; i++) {
        var wid = items[i].getAttribute('wid');
        var dropInfo = graph.getCraftingDrops(wid);
        var otherCost = dropInfo.other;
        var totalMaterialCost = dropInfo.getTotalMaterialCost();
        var container = items[i].querySelector('p');
        container.classList.add('drop-metadata');
        for (var j = container.childNodes.length - 1; j >= 0; j--) {
            container.childNodes[j].remove();
        }
        var costTypes = Object.keys(totalMaterialCost).sort();
        for (var j = 0; j < costTypes.length; j++) {
            var costType = costTypes[j];
            var amount = totalMaterialCost[costType];
            container.appendChild(createDropLabel(costType, amount.toString()));
            totalSuitMaterialCost[costType] = (totalSuitMaterialCost[costType] || 0) + amount;
        }
        for (var j = 0; j < otherCost.length; j++) {
            container.appendChild(createDropLabel(otherCost[j]));
            totalSuitOtherCost.add(otherCost[j]);
        }
    }
    var container = document.createElement('div');
    container.classList.add('drop-metadata');
    var label = document.createElement('strong');
    label.textContent = 'Approximate Value';
    container.appendChild(label);
    container.appendChild(document.createTextNode(' (excluding dyes): '));
    var costTypes = Object.keys(totalSuitMaterialCost).sort();
    for (var j = 0; j < costTypes.length; j++) {
        var costType = costTypes[j];
        var amount = totalSuitMaterialCost[costType];
        container.appendChild(createDropLabel(costType, amount.toString()));
    }
    var totalSuitOtherCostArray = Array.from(totalSuitOtherCost);
    for (var j = 0; j < totalSuitOtherCostArray.length; j++) {
        container.appendChild(createDropLabel(totalSuitOtherCostArray[j]));
    }
    var button = e.target;
    var buttonParentElement = button.parentElement;
    buttonParentElement.replaceChild(container, button);
}
/**
 * Add a helper to the page to make it easier to add a whole suit to your wardrobe.
 */
function addSuitHelper() {
    getWardrobe(function (wardrobe) {
        var wardrobeSet = new Set(wardrobe);
        var submitHolder = document.createElement('div');
        var available = Array.from(document.querySelectorAll('div.witem'));
        var current = available.filter(function (x) { return wardrobeSet.has(x.getAttribute('wid')); });
        submitHolder.appendChild(createButton('Add to Wardrobe', addSuitToWardrobe));
        submitHolder.appendChild(document.createTextNode(' '));
        submitHolder.appendChild(createButton('Remove from Wardrobe', removeSuitFromWardrobe));
        var checkHolder = document.createElement('div');
        checkHolder.appendChild(createButton('Approximate Value', annotateSuitMaterialCost.bind(null, new CraftingGraph(), document.documentElement)));
        var headerContainer = document.querySelector('.container > .collection');
        var headerContainerParentElement = headerContainer.parentElement;
        headerContainerParentElement.insertBefore(submitHolder, headerContainer);
        var collectionContainer = document.querySelector('.section > .collection');
        var collectionContainerParentElement = collectionContainer.parentElement;
        collectionContainerParentElement.insertBefore(checkHolder, collectionContainer);
    });
}
/**
 * Adds selected suits to the wardrobe.
 */
function addSuitsToWardrobe() {
    var newSuits = Array.from(document.querySelectorAll('a[href].sitem.have-witem'));
    var newItems = Array.from(newSuits)
        .map(function (x) { return Array.from(x.querySelectorAll('.witem')).map(function (y) { return y.getAttribute('wid'); }); })
        .reduce(function (array, x) { return array.concat(x); }, []);
    updateWardrobe(new Set(newItems), '+', updateSuitSelections);
}
/**
 * Add a marker to remember that the user wanted to add the selected suit
 * to their wardrobe.
 */
function markSuitInWardrobe(e) {
    e.preventDefault();
    var target = e.target;
    if ((target.tagName.toUpperCase() != 'A') || !target.href || (!target.classList.contains('sitem'))) {
        target = target.closest('a[href].sitem');
    }
    if (target.classList.contains('have-sitem')) {
        return;
    }
    if (target.classList.contains('have-witem')) {
        target.classList.remove('have-witem');
    }
    else {
        target.classList.add('have-witem');
    }
}
/**
 * Add a filter to make it easier to find specific suits.
 */
function addSuitsHelper() {
    new FilterableAccordion('a.sitem', 'Filter suits by name');
    var modeContainer = document.createElement('div');
    modeContainer.classList.add('fixed-action-btn');
    var modeToggle = document.createElement('a');
    modeToggle.setAttribute('id', 'save-button');
    modeToggle.classList.add('btn-floating');
    modeToggle.classList.add('btn-large');
    modeToggle.classList.add('waves-effect');
    modeToggle.classList.add('waves-light');
    modeToggle.classList.add('scale-transition');
    var modeToggleText = document.createElement('i');
    modeToggleText.setAttribute('id', 'save-button-text');
    modeToggleText.classList.add('material-icons');
    modeToggleText.textContent = 'turned_in_not';
    modeToggle.addEventListener('click', function (x) {
        var suits = document.querySelectorAll('a[href].sitem');
        if (modeToggleText.textContent == 'turned_in_not') {
            modeToggleText.textContent = 'save';
            modeToggle.classList.add('pink');
            modeToggle.classList.add('lighten-2');
            for (var i = 0; i < suits.length; i++) {
                suits[i].addEventListener('click', markSuitInWardrobe);
            }
        }
        else {
            addSuitsToWardrobe();
        }
    });
    modeToggle.appendChild(modeToggleText);
    modeContainer.appendChild(modeToggle);
    var modeContainerContainer = document.querySelector('.container');
    modeContainerContainer.appendChild(modeContainer);
    var checkHolder = document.createElement('div');
    checkHolder.appendChild(createButton('Store Completion', annotateStoreSuitItems));
    var collectionContainer = document.querySelector('.container > ul.collapsible');
    var collectionContainerParentElement = collectionContainer.parentElement;
    collectionContainerParentElement.insertBefore(checkHolder, collectionContainer);
}
function exportItemAsCSVRow(container) {
    var wid = container.getAttribute('wid');
    var metadata = window.tag_data[wid];
    var name = metadata.name;
    var type = metadata.type;
    var tags = metadata.tags;
    var rarity = tags.filter(function (x) { return x.indexOf('rare ') == 0; })[0];
    var style = tags.filter(function (x) { return styleTagSet.has(x); }).sort(styleTagComparator);
    return [wid, name, type, rarity].concat(style).join(',');
}
/**
 * Export the visible items as a CSV file
 */
function exportAsCSV() {
    var csvHeader = 'id,name,type,rarity,style,style,style,style,style';
    var csvContent = Array.from(document.querySelectorAll('#item-list div[wid]')).map(exportItemAsCSVRow).join('\n');
    var blob = new Blob([csvHeader + '\n' + csvContent], { type: 'text/csv' });
    var link = document.createElement('a');
    link.href = URL.createObjectURL(blob);
    var hash = document.location.hash || '#';
    link.download = hash.substring(1) + '.csv';
    link.style.display = 'none';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}
/**
 * Update auto complete with the new set of autocomplete options.
 */
function updateAutocomplete(autocompletes) {
    var hash = document.location.hash || '#';
    var autoCompleteOptionsData = Array.from(autocompletes).reduce(function (options, x) {
        options[x] = null;
        return options;
    }, {});
    var chipData = hash.substring(1).split(/\+|\%20/gi).filter(function (x) { return x; }).map(function (x) {
        return { 'tag': x.replace(/_/gi, ' ').trim() };
    });
    jQuery('.chips-autocomplete').material_chip({
        autocompleteOptions: {
            data: autoCompleteOptionsData,
            limit: Infinity,
            minLength: 1
        },
        placeholder: 'Enter a tag',
        secondaryPlaceholder: '+tag',
        data: chipData
    });
    window.filterTags(true);
}
/**
 * Add drop metadata to the visible items.
 */
function addDropMetadata() {
    var items = window.tag_data;
    var keys = Object.keys(items);
    var containers = document.querySelectorAll('div[wid]');
    for (var i = 0; i < containers.length; i++) {
        var container = containers[i];
        var key = container.getAttribute('wid');
        if (!(key in items)) {
            continue;
        }
        var dropMetadataElement = document.createElement('span');
        dropMetadataElement.classList.add('drop-metadata');
        dropMetadataElement.classList.add('grey-text');
        dropMetadataElement.classList.add('text-darken1');
        if (items[key].drop_metadata) {
            for (var j = 0; j < items[key].drop_metadata.length; j++) {
                dropMetadataElement.appendChild(createDropLabel(items[key].drop_metadata[j]));
            }
        }
        var annotation = container.querySelector('span.item-annot');
        if (!annotation) {
            annotation = document.createElement('span');
            annotation.classList.add('item-annot');
            container.appendChild(annotation);
        }
        annotation.appendChild(dropMetadataElement);
    }
}
/**
 * Add additional tags to use for search.
 */
function addTagsHelper() {
    var items = window.tag_data;
    var metadataKeys = Object.keys(itemMetadata);
    for (var i = 0; i < metadataKeys.length; i++) {
        var wid = metadataKeys[i];
        if (!(wid in items)) {
            continue;
        }
        Array.prototype.push.apply(items[wid].tags, itemMetadata[wid]['drop_tags']);
        items[wid]['drop_metadata'] = itemMetadata[wid].drops || [];
    }
    var tagKeys = Object.keys(items);
    var autocompletes = [];
    for (var i = 0; i < tagKeys.length; i++) {
        var wid = tagKeys[i];
        if (items[wid].tags.indexOf('future design material') == -1) {
            items[wid].tags.push('not future design material');
        }
        Array.prototype.push.apply(autocompletes, items[wid].tags);
    }
    setTimeout(updateAutocomplete.bind(null, new Set(autocompletes)), 1000);
    var exportButton = createButton('Export as CSV', exportAsCSV);
    var itemList = document.getElementById('item-list');
    var itemListParentElement = itemList.parentElement;
    itemListParentElement.insertBefore(exportButton, itemList);
}
/**
 * Returns the numbers typed into the input field.
 */
function getSelectedNumbers(insertByNumberInputField) {
    var typedNumbers = insertByNumberInputField.value.split(/[, ]+/gi);
    var selectedNumbers = [];
    for (var i = 0; i < typedNumbers.length; i++) {
        var numberString = typedNumbers[i];
        if (numberString == '') {
            continue;
        }
        var pos = numberString.indexOf('-');
        if (pos != -1) {
            var start = parseInt(numberString.substring(0, pos));
            var end = parseInt(numberString.substring(pos + 1));
            for (var j = start; j <= end; j++) {
                selectedNumbers.push(j);
            }
        }
        else {
            selectedNumbers.push(parseInt(numberString));
        }
    }
    return selectedNumbers;
}
/**
 * Updates the margins around wardrobe items so you can scan through
 * the list 12 items at a time, instead of 2 items at a time.
 */
function updateWardrobeMargin(ownedFilterElement, notOwnedFilterElement) {
    var visibleItems = null;
    if (ownedFilterElement.classList.contains('filter-selected')) {
        visibleItems = Array.from(document.querySelectorAll('.have-witem, .have-sitem'));
    }
    else if (notOwnedFilterElement.classList.contains('filter-selected')) {
        visibleItems = Array.from(document.querySelectorAll('.witem:not(.have-witem):not(.have-sitem)'));
    }
    else {
        visibleItems = Array.from(document.querySelectorAll('.witem'));
    }
    for (var i = 0; i < visibleItems.length; i++) {
        visibleItems[i].style.marginBottom = (i % 12 > 9) ? '2em' : '0px';
    }
}
/**
 * Make it easier to add items by unhiding anything that you list.
 */
function markItemInWardrobe(ownedFilterElement, notOwnedFilterElement, insertByNumberInputField, event) {
    if (event && event.keyCode != 188) {
        return;
    }
    var owned = ownedFilterElement.classList.contains('filter-selected');
    var notOwned = notOwnedFilterElement.classList.contains('filter-selected');
    var type = pathParts[2];
    var selectedNumbers = getSelectedNumbers(insertByNumberInputField);
    var checkSelectedNumbers = new Set(selectedNumbers);
    var selector = owned ? 'a.witem.filtered-out' : notOwned ? 'a.witem:not(.filtered-out)' : 'a.witem';
    var checkItems = Array.from(document.querySelectorAll(selector));
    for (var i = 0; i < checkItems.length; i++) {
        var href = checkItems[i].getAttribute('href');
        if (href == null) {
            continue;
        }
        var itemId = parseInt(href.substring('/wardrobe/'.length + type.length + 1));
        if (notOwned) {
            if (checkSelectedNumbers.has(itemId)) {
                checkItems[i].style.display = 'none';
                checkItems[i].classList.add('have-sitem');
            }
            else {
                checkItems[i].style.display = '';
                checkItems[i].classList.remove('have-sitem');
            }
        }
        else {
            if (checkSelectedNumbers.has(itemId)) {
                if (owned) {
                    checkItems[i].style.display = '';
                }
                checkItems[i].classList.add('have-sitem');
            }
            else {
                if (owned) {
                    checkItems[i].style.display = 'none';
                }
                checkItems[i].classList.remove('have-sitem');
            }
        }
    }
    updateWardrobeMargin(ownedFilterElement, notOwnedFilterElement);
    if (owned || notOwned) {
        var visibleItem = null;
        var reverseSortedItemIds = selectedNumbers.sort(function (a, b) { return b - a; });
        for (var j = 0; j < reverseSortedItemIds.length; j++) {
            var newItemId = reverseSortedItemIds[j];
            var newItem = document.querySelector('a[href="/wardrobe/' + type + '/' + newItemId + '"]');
            if (newItem) {
                visibleItem = newItem;
                break;
            }
        }
        if (visibleItem) {
            var visibleItems = Array.from(document.querySelectorAll(owned ? '.have-witem, .have-sitem' : '.witem:not(.have-witem):not(.have-sitem)'));
            for (var i = 0; i < visibleItems.length; i++) {
                if (visibleItems[i] == visibleItem) {
                    visibleItems[i - (i % 12)].scrollIntoView();
                    var navbar = document.querySelector('.navbar-fixed');
                    window.scrollTo(window.scrollX, window.scrollY - navbar.clientHeight);
                    break;
                }
            }
        }
    }
}
/**
 * Update wardrobe selections.
 */
function updateWardrobeSelections(ownedFilterElement, notOwnedFilterElement, selectItemList, action) {
    var owned = !ownedFilterElement || ownedFilterElement.classList.contains('filter-selected');
    var notOwned = notOwnedFilterElement && notOwnedFilterElement.classList.contains('filter-selected');
    for (var i = 0; i < selectItemList.length; i++) {
        var attributeSelector = '[wid="' + selectItemList[i] + '"]';
        var items = document.querySelectorAll('a' + attributeSelector + ', div' + attributeSelector);
        for (var j = 0; j < items.length; j++) {
            var item = items[j];
            item.classList.remove('have-sitem');
            if (action == '+') {
                item.classList.add('have-witem');
                item.classList.remove('filtered-out');
                if (owned) {
                    item.style.display = '';
                }
                else if (notOwned) {
                    item.style.display = 'none';
                }
            }
            else if (action == '-') {
                item.classList.remove('have-witem');
                item.classList.add('filtered-out');
                if (owned) {
                    item.style.display = 'none';
                }
                else if (notOwned) {
                    item.style.display = '';
                }
            }
        }
    }
    var currentCountContainer = document.querySelector('.current-count');
    if (currentCountContainer) {
        var wardrobeCount = document.querySelectorAll('.have-witem').length;
        currentCountContainer.textContent = wardrobeCount + ' matching item' + (wardrobeCount == 1 ? '' : 's');
    }
    if (ownedFilterElement && notOwnedFilterElement) {
        updateWardrobeMargin(ownedFilterElement, notOwnedFilterElement);
    }
}
/**
 * Checks the wardrobe against the specified text area, and then
 * update with the new values.
 */
function checkWardrobe(ownedFilterElement, notOwnedFilterElement, insertByNumberInputField, action) {
    var type = pathParts[2];
    var selectedNumbers = getSelectedNumbers(insertByNumberInputField);
    var selectedWIDs = selectedNumbers
        .map(function (x) { return document.querySelector('a[href="/wardrobe/' + type + '/' + x + '"]'); })
        .filter(function (x) { return x; })
        .map(function (x) { return x.getAttribute('wid'); });
    var selectValues = new Set(selectedWIDs);
    if (selectValues.size == 0) {
        return;
    }
    updateWardrobe(selectValues, action, updateWardrobeSelections.bind(null, ownedFilterElement, notOwnedFilterElement));
}
;
/**
 * Get the wardrobe as a string.
 */
function getWardrobeShorthand(current, available) {
    if (current.length == 0) {
        return '';
    }
    var matchIndex = -1;
    var matchStart = -1;
    var matches = [];
    for (var i = 0; i < available.length; i++) {
        if (available[i] == current[matchIndex + 1]) {
            matchIndex++;
            if (matchStart == -1) {
                matchStart = matchIndex;
            }
        }
        else if (matchStart != -1) {
            var newMatch = [current[matchStart].toString()];
            if (matchIndex != matchStart) {
                newMatch.push(matchIndex == matchStart + 1 ? ',' : '-');
                newMatch.push(current[matchIndex].toString());
            }
            matches.push(newMatch.join(''));
            matchStart = -1;
        }
    }
    return matches.join(',');
}
/**
 * Annotate
 */
function checkSafeDecompose() {
    filter_Owned();
    getWardrobe(function (wardrobe) {
        var wardrobeSet = new Set(wardrobe);
        var itemElements = Array.from(document.querySelectorAll('.have-witem'));
        for (var i = 0; i < itemElements.length; i++) {
            var itemElement = itemElements[i];
            var wid = itemElement.getAttribute('wid');
            var item = getCraftingIngredient(wid);
            var annotation;
            if (item == null) {
                annotation = 'N/A';
                itemElement.classList.add('safe-decompose');
            }
            else {
                var paths = item.getUsedToCraftPaths();
                if (paths.length == 1) {
                    annotation = 'N/A';
                    itemElement.classList.add('safe-decompose');
                }
                else {
                    var haveCount = 0;
                    var futureDesignMaterial = false;
                    for (var j = 1; j < paths.length; j++) {
                        if (paths[j].wid() == 'future') {
                            futureDesignMaterial = true;
                        }
                        else if (wardrobeSet.has(paths[j].wid())) {
                            haveCount++;
                        }
                    }
                    if (haveCount > 0) {
                        --haveCount;
                    }
                    if (futureDesignMaterial) {
                        annotation = 'future';
                    }
                    else {
                        annotation = haveCount + '/' + (paths.length - 1);
                        if (haveCount == (paths.length - 1)) {
                            itemElement.classList.add('safe-decompose');
                        }
                    }
                }
            }
            var annotationElement = document.createElement('div');
            annotationElement.textContent = annotation;
            var annotationHolder = itemElement.querySelector('.secondary-content');
            annotationHolder.appendChild(annotationElement);
        }
    });
}
/**
 * Add a helper to the page to make it easier to update your wardrobe by number.
 */
function addWardrobeHelper() {
    var wardrobeManager = document.getElementById('wardrobe-manager');
    if (!wardrobeManager) {
        return;
    }
    var wardrobeManagerParentNode = wardrobeManager.parentNode;
    if (!wardrobeManagerParentNode) {
        return;
    }
    var ownedFilterElement = document.getElementById('filter-owned');
    var notOwnedFilterElement = document.getElementById('filter-notowned');
    if (!ownedFilterElement || !notOwnedFilterElement) {
        return;
    }
    getWardrobe(function (wardrobe) {
        var currentWardrobe = wardrobe
            .map(function (x) { return document.querySelector('a[wid="' + x + '"]'); })
            .filter(function (x) { return x; })
            .map(function (x) { return parseInt(x.href.split('/')[5]); })
            .sort(function (a, b) { return a - b; });
        var itemElements = Array.from(document.querySelectorAll('a[wid]'));
        var availableWardrobe = itemElements.map(function (x) { return parseInt(x.href.split('/')[5]); }).sort(function (a, b) { return a - b; });
        var currentWardrobeShorthand = getWardrobeShorthand(currentWardrobe, availableWardrobe);
        var insertByNumberContainer = document.createElement('div');
        insertByNumberContainer.classList.add('insert-by-number');
        var oldWardrobeInputField = document.createElement('input');
        oldWardrobeInputField.setAttribute('type', 'hidden');
        oldWardrobeInputField.value = currentWardrobeShorthand;
        insertByNumberContainer.appendChild(oldWardrobeInputField);
        var currentCountContainer = document.createElement('div');
        currentCountContainer.classList.add('current-count');
        currentCountContainer.textContent = currentWardrobe.length + ' matching item' + (currentWardrobe.length == 1 ? '' : 's');
        insertByNumberContainer.appendChild(currentCountContainer);
        var insertByNumberInputField = document.createElement('textarea');
        var boundMarkItemInWardrobe = markItemInWardrobe.bind(null, ownedFilterElement, notOwnedFilterElement, insertByNumberInputField);
        insertByNumberInputField.onkeyup = _.debounce(boundMarkItemInWardrobe, 500);
        insertByNumberInputField.setAttribute('placeholder', 'Enter numbers (1,2,3) and number ranges (1-3,4-7)');
        insertByNumberContainer.appendChild(insertByNumberInputField);
        var submitHolder = document.createElement('div');
        submitHolder.appendChild(createButton('Add to Wardrobe', checkWardrobe.bind(null, ownedFilterElement, notOwnedFilterElement, insertByNumberInputField, '+')));
        submitHolder.appendChild(document.createTextNode(' '));
        submitHolder.appendChild(createButton('Remove from Wardrobe', checkWardrobe.bind(null, ownedFilterElement, notOwnedFilterElement, insertByNumberInputField, '-')));
        insertByNumberContainer.appendChild(submitHolder);
        wardrobeManagerParentNode.insertBefore(insertByNumberContainer, wardrobeManager);
        var updateMarginAndFocus = function (e) {
            boundMarkItemInWardrobe();
            updateWardrobeMargin(ownedFilterElement, notOwnedFilterElement);
            insertByNumberInputField.focus();
        };
        ownedFilterElement.addEventListener('click', updateMarginAndFocus);
        notOwnedFilterElement.addEventListener('click', updateMarginAndFocus);
        var safeDecomposeButton = createButton('Check for Safe Decompose (Experimental)', checkSafeDecompose);
        wardrobeManagerParentNode.insertBefore(safeDecomposeButton, insertByNumberContainer);
    });
}
/**
 * Combine paths leading to the same item into one multi path.
 */
function combinePaths(multiPaths, path) {
    var lastMultiPath = (multiPaths.length > 0) ? multiPaths[multiPaths.length - 1] : null;
    if (lastMultiPath && (path.wid() == lastMultiPath[0].wid())) {
        lastMultiPath.push(path);
    }
    else {
        multiPaths.push([path]);
    }
    return multiPaths;
}
/**
 * Convert crafting item metadata into an HTML list.
 */
function showCraftingPath(link, list, item, multiPath, e) {
    if (link.classList.contains('active')) {
        return true;
    }
    if (!list.classList.contains('show-path')) {
        list.classList.add('show-path');
    }
    var actives = list.querySelectorAll('.active');
    for (var i = 0; i < actives.length; i++) {
        actives[i].classList.remove('active');
    }
    link.classList.add('active');
    var listParentElement = list.parentElement;
    var placeholder = listParentElement.querySelector('.crafting-path');
    var containers = document.createElement('div');
    containers.classList.add('crafting-path');
    for (var i = 0; i < multiPath.length; i++) {
        var path = multiPath[i];
        var container = document.createElement('div');
        var pathElements = path.pathElements;
        for (var j = 0; j < pathElements.length; j++) {
            if (j > 0) {
                var span = document.createElement('span');
                span.classList.add('needed');
                var needed = pathElements[j - 1].needed;
                span.innerHTML = '× ' + needed + '<br/><span class="arrow">&rarr;</span>';
                container.appendChild(span);
            }
            var item = pathElements[j];
            container.appendChild(createItemLink(item.href, item.name, item.wid, item.icon, false));
        }
        containers.appendChild(container);
    }
    var placeHolderParentElement = placeholder.parentElement;
    placeHolderParentElement.replaceChild(containers, placeholder);
    e.stopPropagation();
    return false;
}
/**
 * Generates a link for the given item.
 */
function createItemLink(href, name, wid, icon, hasOnClick) {
    var link = document.createElement('a');
    link.classList.add('witem');
    link.classList.add('icon-room');
    if (hasOnClick) {
        link.setAttribute('href', '#!');
        link.setAttribute('data-href', href);
    }
    else {
        link.setAttribute('href', href);
    }
    link.setAttribute('title', name);
    link.setAttribute('alt', name);
    var outerIcon = document.createElement('div');
    outerIcon.classList.add('icon');
    if (wid == 'future') {
        outerIcon.textContent = name;
    }
    else {
        link.setAttribute('wid', wid);
    }
    var innerIcon = document.createElement('div');
    innerIcon.classList.add('inner-icon');
    innerIcon.classList.add(icon);
    outerIcon.appendChild(innerIcon);
    link.appendChild(outerIcon);
    return link;
}
/**
 * Convert crafting item metadata into an HTML list.
 */
function formatCraftingMetadata(list, multiPath, wardrobeSet) {
    var item = multiPath[0].item();
    var link = createItemLink(item.href, item.name, item.wid, item.icon, true);
    if (wardrobeSet.has(item.wid)) {
        link.classList.add('have-witem');
    }
    if (item.wid != 'future') {
        link.onclick = showCraftingPath.bind(null, link, list, item, multiPath);
    }
    list.appendChild(link);
}
/**
 * Add a helper to the page to search the crafting tree in the forward
 * direction (everything this item can be used to craft).
 */
function addWardrobeItemCraftingSection(sectionName, item, pathGenerator) {
    var referenceElement = getSection(document.documentElement, 'Obtained from');
    if (!referenceElement) {
        referenceElement = document.querySelector('.fixed-action-btn');
    }
    var section = getSection(document.documentElement, sectionName, referenceElement);
    var checkDependenciesButton = createButton('Identify All', function () {
        var container = document.createElement('div');
        container.classList.add('crafting-tree');
        var loading = document.createElement('div');
        loading.textContent = 'Identifying...';
        container.appendChild(loading);
        section.replaceChild(container, checkDependenciesButton);
        for (var i = section.childNodes.length - 1; i >= 0; i--) {
            var childNode = section.childNodes[i];
            if (childNode.nodeType == 3) {
                childNode.remove();
                continue;
            }
            if (childNode.nodeType != 1) {
                continue;
            }
            var childElement = childNode;
            if (childElement.classList.contains('crafting-tree')) {
                continue;
            }
            else if ('H5' == childElement.tagName.toUpperCase()) {
                continue;
            }
            else {
                childElement.style.display = 'none';
            }
        }
        var placeholder1 = document.createElement('div');
        placeholder1.classList.add('transitive-dependencies');
        container.appendChild(placeholder1);
        var placeholder2 = document.createElement('div');
        placeholder2.classList.add('crafting-path');
        container.appendChild(placeholder2);
        getWardrobe(function (wardrobe) {
            var wardrobeSet = new Set(wardrobe);
            var list = document.createElement('div');
            list.classList.add('transitive-dependencies');
            var paths = pathGenerator();
            var multiPaths = paths.reduce(combinePaths, []);
            for (var i = 0; i < multiPaths.length; i++) {
                if (multiPaths[i][0].wid() != item.wid) {
                    formatCraftingMetadata(list, multiPaths[i], wardrobeSet);
                }
            }
            if (list.childNodes.length == 0) {
                loading.innerHTML = 'Nothing';
            }
            else {
                var descriptor = null;
                if (sectionName == 'Used to craft') {
                    loading.innerHTML = 'Touch an item below to see the crafting path, which will show why <strong>' + item.name + '</strong> is needed to make it!';
                }
                else {
                    loading.innerHTML = 'Touch an item below to see the crafting path, which will show how it is used to make <strong>' + item.name + '</strong>!';
                }
            }
            container.replaceChild(list, placeholder1);
        });
    });
    var collection = section.querySelector('.collection');
    if (collection) {
        section.insertBefore(checkDependenciesButton, collection);
    }
    else {
        section.appendChild(checkDependenciesButton);
    }
}
/**
 * Add a helper to the page to search the crafting tree.
 */
function addWardrobeItemHelper() {
    var itemElement = document.querySelector('.collection .witem');
    var wid = itemElement.getAttribute('wid');
    var title = itemElement.querySelector('.title');
    var href = null, name = null;
    if (title) {
        href = title.getAttribute('href') || '';
        name = (title.textContent || '').trim();
    }
    else {
        href = document.location.pathname;
        var headerContainer = itemElement.closest('.container');
        var header = headerContainer.querySelector('h4');
        name = (header.childNodes[0].textContent || '').trim();
    }
    var iconElement = itemElement.querySelector('.inner-icon');
    var icon = Array.from(iconElement.classList).filter(function (x) { return x.indexOf('s-') == 0; })[0];
    var metadata = itemMetadata[wid];
    if (!metadata) {
        return;
    }
    var item = new CraftingIngredient(wid, href, name, icon, 1);
    var section = getSection(document.documentElement, 'Tags');
    if (section) {
        var dropTags = metadata['drop_tags'] || [];
        for (var i = 0; i < dropTags.length; i++) {
            var anchor = document.createElement('a');
            anchor.classList.add('chip');
            anchor.setAttribute('href', '/tags/#' + dropTags[i].replace(' ', '_'));
            anchor.textContent = dropTags[i];
            section.appendChild(anchor);
        }
    }
    section = getSection(document.documentElement, 'Obtained from');
    var collectionItems = section ? section.querySelectorAll('.collection-item') : [];
    for (var i = 0; i < collectionItems.length; i++) {
        var collectionItem = collectionItems[i];
        var obtainType = (collectionItem.textContent || '').trim().toLowerCase();
        if (obtainType == 'clothes shop') {
            var container = document.createElement('div');
            container.classList.add('secondary-content');
            var drops = metadata['drops'] || [];
            container.appendChild(createDropLabel(drops.filter(function (x) { return x.endsWith(' G') || x.endsWith(' D'); })[0]));
            collectionItem.appendChild(container);
        }
    }
    addWardrobeItemCraftingSection('Used to craft', item, CraftingIngredient.prototype.getUsedToCraftPaths.bind(item));
    addWardrobeItemCraftingSection('Crafted from', item, CraftingIngredient.prototype.getCraftedFromPaths.bind(item));
}
/**
 * Helper to load all crafting and drop metadata, so we don't have to crawl MyNI pages,
 * before calling a function.
 */
function fetchMetadata(callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://holatuwol.s3-us-west-2.amazonaws.com/crafting.json');
    xhr.onload = function () {
        itemMetadata = JSON.parse(xhr.responseText);
        callback();
    };
    xhr.send(null);
}
/**
 * Figure out which helper to add to the page.
 */
function addHelper() {
    if (pathParts[1] == 'drops') {
        addDropsHelper();
    }
    else if (pathParts[1] == 'stages') {
        if ((pathParts.length >= 4) && (pathParts[3] != '')) {
            addStageHelper();
        }
        else if (pathParts[2] == 'commission') {
            addCommissionStageHelper();
        }
        else if (pathParts[2] == 'custom') {
            addCustomStageHelper();
        }
        else if (pathParts[2] == 'stylistsarena') {
            addArenaStageHelper();
        }
    }
    else if (pathParts[2] == 'suit') {
        if (pathParts[3]) {
            fetchMetadata(addSuitHelper);
        }
        else {
            fetchMetadata(addSuitsHelper);
        }
    }
    else if (pathParts[1] == 'tags') {
        fetchMetadata(addTagsHelper);
    }
    else if (pathParts[1] == 'wardrobe') {
        if (((pathParts.length == 3) && (pathParts[2] != '')) || ((pathParts.length == 4) && (pathParts[3] == ''))) {
            fetchMetadata(addWardrobeHelper);
        }
        else if (pathParts.length >= 4) {
            fetchMetadata(addWardrobeItemHelper);
        }
    }
}
addHelper();