Skip to content

Instantly share code, notes, and snippets.

@mephitrpg
Last active October 7, 2024 21:49
Show Gist options
  • Save mephitrpg/79770714eca8fcc407de90fa3c32b70b to your computer and use it in GitHub Desktop.
Save mephitrpg/79770714eca8fcc407de90fa3c32b70b to your computer and use it in GitHub Desktop.
Scryfall.com Edit Search - Tampermonkey UserScript that precompiles the Advanced Search form with current filters
// ==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>`);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment