MTG Scryfall edit search

This Tampermonkey script adds an "Edit" link near the search field when you're looking the card list. Click it to automatically collect the data in the search field, open the Advanced Search and precompile its fields using the collected data.

// ==UserScript==
// @name         MTG Scryfall edit search
// @namespace    http://scryfall.com/
// @version      1.0
// @description  This Tampermonkey script adds an "Edit" link near the search field when you're looking the card list. Click it to automatically collect the data in the search field, open the Advanced Search and precompile its fields using the collected data.
// @author       [email protected]
// @match        https://scryfall.com/*
// @match        https://www.scryfall.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    if (location.pathname !== '/advanced') {

        const $searchForm = $('form.header-search');
        if (!$searchForm.length) return;

        const $editAnchor = $('<a href="#">Edit</a>').css({color: 'rgba(255,255,255,0.8)', marginLeft: '1em', marginRight: '1em'});
        $searchForm.after($editAnchor);

        $editAnchor.on('click', function(event){
            location.href = '/advanced' + location.search;

        });

        return;
    }

    const params = new URLSearchParams(location.search);
    const o = {};
    for (let [key, value] of params.entries()) o[key] = value;

    function match(str, regex) {

        const result = [];
        let m;

        while ((m = regex.exec(str)) !== null) {
            const res = [];
            // This is necessary to avoid infinite loops with zero-width matches
            if (m.index === regex.lastIndex) regex.lastIndex++;

            // The result ca be accessed through the `m`-variable.
            m.forEach(match => { res.push(match); });

            result.push(res);
        }

        return result.length ? result : null;

    }

    if (o.q) {

        const nameArray          = [];    const $nameField          = $('#name');
        const textArray          = [];    const $textField          = $('#oracle');
        const typeArray          = [];    const $typeField            = $('#type');
        let   typePartial        = false; const $typePartialField     = $('#type_partial');
        let   colorsPartial      = false; const $colorsPartialField   = $('#colors_partial');
//        let   colorsStrict       = false; const $colorsStrictField    = $('#colors_strict');
//        let   colorsMulti        = false; const $colorsMultiField     = $('#colors_multicolored');
        let   colorsCompare      = '';    const $colorsCompareField   = $('#color_comparison');
        let   setArray           = [];    const $setField             = $('#set');
        let   setNotArray        = [];
        let   blockArray         = [];    const $blockField           = $('#block');
        let   blockNotArray      = [];
        const colors             = {};
        const commander          = {};
        let   statsLen           = 0;
        const formatStatusArray  = [];
        const formatArray        = [];
        const criteriaArray      = [];    const $criteriaField        = $('#is');
        let   criteriaPartial    = false; const $criteriaPartialField = $('#is_partial');
        let   pricesLen          = 0;
        const artistArray        = [];    const $artistField          = $('#artist');
        const flavorArray        = [];    const $flavorField          = $('#flavor');
        const loreArray          = [];    const $loreField            = $('#lore');
        const langArray          = [];    const $langField            = $('#language');

        const groups = [[]];

        o.q.split(/[\s+]/).forEach(function(word){
            if (word.indexOf('(') === 0) {
                groups.push([]);
                groups[groups.length - 1].push(word.slice(1));
            } else if (word.indexOf(')') === word.length - 1) {
                groups[groups.length - 1].push(word.slice(0,-1));
            } else {
                groups[groups.length - 1].push(word);
            }
        });

        groups.forEach(function(group){
            group.forEach(function(word){
                if (word === 'OR') {
                    if (group[0].indexOf('type:') === 0) { typePartial = true; return; }
                    if (group[0].indexOf('color=') === 0) { colorsPartial = true; return; }
                    if (group[0].indexOf('color>=') === 0) { colorsPartial = true; return; }
                    if (group[0].indexOf('color<=') === 0) { colorsPartial = true; return; }
                    if (group[0].indexOf('rarity:') === 0) { return; }
                    if (group[0].indexOf('set:') === 0) { return; }
                    if (group[0].indexOf('block:') === 0) { return; }
                    if (group[0].indexOf('is:') === 0) { criteriaPartial = true; return; }
                }
                if (word === 'AND') {
                    if (group[0].indexOf('-color=') === 0) { return; }
                    if (group[0].indexOf('-color>=') === 0) { return; }
                    if (group[0].indexOf('-color<=') === 0) { return; }
                }

                const statsTerms = ['cmc','pow','tou','loy'];
                const pricesTerms = ['usd','eur','tix'];
                const regex = /^(cmc|pow|tou|loy|usd|eur|tix)([\!=<>]{1,2})(\d+)$/;
                let str = '', strLen = 0, matches = null;
                console.log('word',word);
                switch(true){
                    case (word.indexOf('oracle:') === 0):
                        textArray.push(word.slice('oracle:'.length));
                        break;
                    case (word.indexOf('-oracle:') === 0):
                        textArray.push('-' + word.slice('-oracle:'.length));
                        break;
                    case (word.indexOf('type:') === 0):
                        typeArray.push(word.slice('type:'.length));
                        break;
                    case (word.indexOf('-type:') === 0):
                        typeArray.push('-' + word.slice('-type:'.length));
                        break;
//                    case (word.indexOf('-type:') === 0):
//                        typeArray.push('-' + word.slice('-type:'.length));
//                        break;
                    case ((/^(-?)color(=|>=|<=)(.*)/g).test(word)):
                        matches = match(word, /^(-?)color(=|>=|<=)(.*)/g);
                        str = matches[0][3];
                        strLen = str.length;
                        for ( let i = 0; i < strLen; i++ ) {
                            const k = str[i];
                            colors[k] = !matches[0][1];
                        }
                        colorsCompare = matches[0][2];
                        break;
/*
                    case (word.indexOf('color=') === 0):
                        str = word.slice('color='.length);
                        strLen = str.length;
                        for ( let i = 0; i < strLen; i++ ) {
                            const k = str[i];
                            if (k === 'M') colorsMulti = true;
                            else colors[k] = true;
                        }
                        break;
                    case (word.indexOf('-color=') === 0):
                        str = word.slice('-color='.length);
                        strLen = str.length;
                        for ( let i = 0; i < strLen; i++ ) {
                            const k = str[i];
                            if (k === 'M') colorsMulti = false;
                            else colors[k] = false;
                        }
                        break;
*/
                    case (word.indexOf('commander:') === 0):
                        str = word.slice('commander:'.length);
                        strLen = str.length;
                        for ( let i = 0; i < strLen; i++ ) commander[str[i]] = true;
                        break;
                    case (word.indexOf('legal:') === 0):
                        formatArray.push(word.slice('legal:'.length));
                        formatStatusArray.push('legal');
                        break;
                    case (word.indexOf('restricted:') === 0):
                        formatArray.push(word.slice('restricted:'.length));
                        formatStatusArray.push('restricted');
                        break;
                    case (word.indexOf('banned:') === 0):
                        formatArray.push(word.slice('banned:'.length));
                        formatStatusArray.push('banned');
                        break;
                    case (word.indexOf('rarity:') === 0):
                        $('input[name="rarity[]"][value="'+word.slice('rarity:'.length)+'"]').prop('checked', true);
                        break;
                    case (word.indexOf('mana:') === 0):
                        $('#mana').val(word.slice('mana:'.length));
                        break;
                    case (word.indexOf('set:') === 0):
                        setArray.push(word.slice('set:'.length));
                        break;
//                    case (word.indexOf('-set:') === 0):
//                        setNotArray.push(word.slice('-set:'.length));
//                        break;
                    case (word.indexOf('block:') === 0):
                        blockArray.push(word.slice('block:'.length));
                        break;
                    case (word.indexOf('-block:') === 0):
                        blockNotArray.push(word.slice('-block:'.length));
                        break;
                    case (word.indexOf('unique:') === 0):
                        if (word.slice('unique:'.length) === 'prints') $('#unroll_search').prop('checked', true);
                        break;
                    case (word.indexOf('include:') === 0):
                        if (word.slice('include:'.length) === 'extras') $('#include_extras').prop('checked', true);
                        break;
                    case (word.indexOf('is:') === 0):
                        criteriaArray.push(word.slice('is:'.length));
                        break;
                    case (word.indexOf('artist:') === 0):
                        artistArray.push(word.slice('artist:'.length));
                        break;
                    case (word.indexOf('flavor:') === 0):
                        flavorArray.push(word.slice('flavor:'.length));
                        break;
                    case (word.indexOf('lore:') === 0):
                        loreArray.push(word.slice('lore:'.length));
                        break;
                    case (word.indexOf('lang:') === 0):
                        langArray.push(word.slice('lang:'.length));
                        break;
                    default:
                        matches = regex.exec(word);
                        if (matches) {
                            const [full, key, op, value] = matches;
                            if (statsTerms.includes(key)) {
                                const i = ++statsLen;
                                setTimeout(()=>{
                                    $('#stat_' + i).val(key);
                                    $('#stat_' + i + '_mode').val(op);
                                    $('#stat_' + i + '_value').val(value).trigger('change');
                                }, i * 10);
                            } else if (pricesTerms.includes(key)) {
                                const i = ++pricesLen;
                                setTimeout(()=>{
                                    $('#price_' + i).val(key);
                                    $('#price_' + i + '_mode').val(op);
                                    $('#price_' + i + '_value').val(value).trigger('change');
                                }, i * 10);
                            }
                        } else if (word.indexOf(':') === -1) {
                            nameArray.push(word); return;
                        }

                        break;
                }
            });
        });

//        let colorsYes = 0, colorsNo = 0;
        for(let [key, value] of Object.entries(colors)) {
            $('input[name="color[]"][value="'+key+'"]').prop('checked', value);
//            value ? colorsYes++ : colorsNo++;
        }
//        colorsStrict = colorsYes + colorsNo === 6;
        $colorsCompareField.val(colorsCompare);

        for(let [key, value] of Object.entries(commander)) {
            $('input[name="identity[]"][value="'+key+'"]').prop('checked', value);
        }

        formatStatusArray.forEach((value, i) => {
            setTimeout(()=>{$('#format_status_' + (i+ 1)).val(value);},5000);
        });

        formatArray.forEach((value, i) => {
            $('#format_' + (i+ 1)).val(value).trigger('change');
        });

        $nameField.val(nameArray.join(' '));
        $textField.val(textArray.join(' '));

        $typeField.val(typeArray.map( v => v.replace(/^-/,'') ));
        $typeField.trigger('change');
        typeArray.forEach( v => { if (v[0]==='-') $typeField.next().find(`.select2-polarity[data-item="${v.slice(1)}"]`).click() ; } );

        $typePartialField.prop('checked', typePartial);
        $colorsPartialField.prop('checked', colorsPartial);
//        $colorsMultiField.prop('checked', colorsMulti);
//        $colorsStrictField.prop('checked', colorsStrict);

        $setField.val(setArray);
        $setField.trigger('change');
//        setArray.forEach( v => { if (v[0]==='-') $setField.next().find(`.select2-polarity[data-item="${v.slice(1)}"]`).click() ; } );

        $blockField.val(blockArray);
        $blockField.trigger('change');
        blockArray.forEach( v => { if (v[0]==='-') $blockField.next().find(`.select2-polarity[data-item="${v.slice(1)}"]`).click() ; } );

        $criteriaField.val(criteriaArray);
        $criteriaField.trigger('change');
        criteriaArray.forEach( v => { if (v[0]==='-') $criteriaField.next().find(`.select2-polarity[data-item="${v.slice(1)}"]`).click() ; } );
        $criteriaPartialField.prop('checked', criteriaPartial);

        $artistField.val(artistArray.join(' '));
        $flavorField.val(flavorArray.join(' '));
        $loreField.val(loreArray.join(' '));
        $langField.val(langArray.join(' '));

    }

    if (!o.as) o.as = 'full';
    $('#as').val(o.as);

    if (!o.order) o.order = 'cmc';
    $('#order').val(o.order).after(`<select class="form-input auto" name="dir" id="dir">
        <option value="asc">Asc (not working yet)</option>
        <option value="desc">Desc (not working yet)</option>
    </select>`);


})();