Last active
October 7, 2024 21:49
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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