// ==UserScript==
// @name Userscript with no name
// @namespace NiGHTS
// @author Jim [U:1:34673527]
// @description Overhauls the new raffle page and enhances a few others
// @include http://tf2r.com/newraf.html*
// @include http://tf2r.com/raffles.html*
// @include http://tf2r.com/settings.html*
// @include http://tf2r.com/k*.html*
// @include http://tf2r.com/user/*.html*
// @version 0.994
// @grant GM_xmlhttpRequest
// @require http://code.jquery.com/jquery-1.12.0.min.js
// @run-at document-start
// @connect steamcommunity.com
// @noframes
// ==/UserScript==
window.NoName = {
init: function() {
console.log('---Userscript with no name for TF2r. Made with <3 by Jim :NiGHTS:---');
this.Storage.init();
this.Steam.init();
this.UI.init();
this.ScrapTF.init();
if(!window.location.pathname.indexOf('/newraf.html')) {
this.NewRaffle.init();
}
if(!window.location.pathname.indexOf('/settings.html')) {
this.Settings.init();
}
if(!window.location.pathname.indexOf('/user/')) {
this.Profile.init();
}
if(!window.location.pathname.indexOf('/raffles.html')) {
this.RaffleList.init();
} else if($('.participants').length) {
this.Raffle.init();
}
}
};
window.NoName.Storage = {
available: false,
callbacks: {},
init: function() {
var that = this;
if(!localStorage && localStorage.getItem) {
console.warn('[Storage::init] localStorage not available. Settings will not be saved.');
return;
}
this.available = true;
window.addEventListener('storage', function(e) {
if(that.callbacks[e.key]) {
for(var i = 0; i < that.callbacks[e.key].length; i++) {
that.callbacks[e.key][i](e.oldValue, e.newValue, e.url);
}
}
});
},
get: function(key, defaultValue) {
if(!this.available) {
return defaultValue;
}
return (typeof localStorage[key] === 'undefined') ? defaultValue : localStorage[key];
},
set: function(key, value) {
if(!this.available) {
return false;
}
localStorage[key] = value;
return true;
},
listen: function(key, callback) {
this.callbacks[key] = this.callbacks[key] || [];
this.callbacks[key].push(callback);
}
};
//Generic ui changes
window.NoName.UI = {
missingImage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAAMFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABaPxwLAAAAEHRSTlMAzF8Ovno2lrEpG6NtUohEM5nYxgAAAURJREFUSMdjGIHALElJSS0Bp/SkEkEwaL2MVZrrhCAchGGTDxREApUY8sxgeQRQQFfwRRAVSBugyrMKooPNhBRIoznREUPFAVQVihgKpFAVcGAoEEVVwCaIAdD88RAi2q6kDnPOB1QFC0Fi4reALCZHrGHFBLJ2AZg5Eas3mBsFxRegBEoAWmB/FLkAczD26GDfyYBfAZcBmoIJOFMWL1heAlUQM1SFcCuApB1nnPKsGG5ECxBIoIvjNGA2xIBmXPKckJgQwenJKRADPHG6oBHiAgMCXjiA04mJYHk3FDHMhCW9AKc8M4FAZOAHK0jArYAHJC/DgBswJxWCbMALkkoOMBAABgwDC2yfv8Urz9kIKnzwABPMwgdLQXMBjwJwcnmARwE4rgrwKHCE5Hvc4CCB2GawhhVv+LKVAt6Q4lK6zDAcAQAdIEKHGzsRJwAAAABJRU5ErkJggg==',
init: function() {
var that = this;
this.removeExistingUI();
this.addStyles();
this.addNewUI();
this.fixMissingItems();
//Override raffle list getItems() function to handle items with no schema information
unsafeWindow.getItem = exportFunction(function(item) {
return that.getItemOverride(item);
}, unsafeWindow);
//Remove slDown, message transitions are done in css now
unsafeWindow.slDown = exportFunction(function() {}, unsafeWindow);
NoName.Storage.listen('transitions', function(oldValue, newValue, url) {
if(newValue) {
$(document.body).addClass('transitions');
} else {
$(document.body).removeClass('transitions');
}
});
},
removeExistingUI: function() {
unsafeWindow.$('.item').unbind('hover');
//$('#content > .indent').children().unwrap();
},
getItemOverride: function(item) {
var element = document.createElement('div');
element.setAttribute('ilevel', item.level);
element.setAttribute('iname', item.name || 'Unknown item');
element.setAttribute('iu1', item.iu1);
element.className = 'item ' + item.q;
element.style.backgroundImage = 'url(' + (item.image || this.missingImage) + ')';
return element.outerHTML;
},
//Fix other missing items that arent added via getItems()
fixMissingItems: function() {
var that = this;
$('.item').each(function() {
var $img = $(this).children('img');
if($img.attr('src') && $img.attr('src') !== 'null') {
this.style.backgroundImage = 'url(' + $img.attr('src') + ')';
} else {
$(this).attr('iname', 'Unknown item');
this.style.backgroundImage = 'url(' + that.missingImage + ')';
}
$img.remove();
this.style.width = '';
this.style.height = '';
});
},
addStyles: function() {
$(document.head).append(
$('<style></style>').text(
'@keyframes fadeInDown {\
0% {\
transform: translateY(-50px);\
opacity: 0;\
}\
100% {\
transform: translateY(0);\
opacity: 1;\
}\
}\
@keyframes RestoreColour {\
100% {\
background-color: #2A2725;\
}\
}\
html, body {\
height: auto;\
}\
body {\
font-size: 14px;\
overflow: hidden;\
}\
h1 {\
margin-top: 0;\
font-family: tf2build;\
color: #EBE2CA;\
}\
table {\
/*border-collapse: collapse;*/\
/*border-spacing: 0;*/\
}\
td {\
border-top: 1px solid transparent;\
border-bottom: 1px solid transparent;\
}\
.indent {\
left: auto;\
margin-left: 0;\
padding: 0 12px;\
}\
.item { /*Scale background image to tile size*/\
background-size: 100% 100%;\
border-width: 0;\
width: 68px;\
height: 68px;\
/*transition: all 0.1s;*/\
position: relative;\
overflow: hidden;\
}\
.item.q15 { /*Add decorated quality colour*/\
background-color: rgba(250, 250, 250, 0.6);\
border-color: #fafafa;\
}\
.item.gelite { /*Add grade colours*/\
color: #eb4b4b;\
}\
.item.gassassin {\
color: #d32ce6;\
}\
.item.gcommando {\
color: #8847ff;\
}\
.item.gmercenary {\
color: #4b69ff;\
}\
.item.gfreelance {\
color: #5e98d9;\
}\
.item.gcivilian {\
color: #b0c3d9;\
}\
.item.hasgrade:before {/*Show grade colours in top right corner */\
content: \'\';\
position: absolute;\
top: -20px;\
right: 0;\
border: 20px solid transparent;\
border-right-color: initial;\
color: inherit;\
}\
.item:hover {\
transform: scale(1.1);\
box-shadow: none;\
}\
/* Not risking messing with the enter button */\
input:not(#enbut), textarea, select, button:not(#enbut) {\
background-color: #4d4d4d;\
border: 1px solid #4d4d4d;\
box-sizing: border-box;\
border-radius: 2px;\
margin: 3px 0;\
height: 32px;\
font-size: 14px;\
box-shadow: none;\
color: #dddddd;\
}\
input.full-width, select.full-width, textarea.full-width, button.full-width {\
width: 100%;\
}\
/*Specificity issues*/\
input[type=checkbox]:not(#enbut), input[type=radio]:not(#enbut) {\
height: auto;\
width: auto;\
}\
input:invalid, textarea:invalid, select:invalid {\
border-color: red;\
}\
select{\
line-height: 32px;\
padding-top: 3px;\
}\
input[type=submit], input[type=button]:not(#enbut), button {\
/*width: auto*/\
min-width: 128px;\
cursor: pointer;\
}\
textarea {\
resize: vertical;\
min-height: 75px !important;\
font-family: inherit;\
}\
option {\
background-color: inherit;\
border: none;\
border-radius: 0;\
}\
.text_holder { /*Make container elements wide enough to fit 10 columns of items*/\
width: 746px;\
padding: 10px;\
color: #dddddd;\
}\
.ncbutton {\
float: none;\
display: inline-block;\
text-align: right;\
margin-bottom: 5px;\
}\
.switch-field { /*Fancy toggles*/\
padding: 10px 0;\
overflow: hidden;\
}\
.switch-title {\
margin-bottom: 6px;\
}\
.switch-field input {\
display: none;\
}\
.switch-field label {\
display: inline-block;\
min-width: 100px;\
background-color: #4d4d4d;\
color: rgba(255, 255, 255, 0.6);\
font-size: 14px;\
font-weight: normal;\
text-align: center;\
text-shadow: none;\
padding: 6px 14px;\
cursor: pointer;\
}\
.switch-field input:checked + label {\
background-color: #CF6A32;\
color: rgba(0, 0, 0, 0.6);\
}\
.switch-field input[disabled] + label {\
opacity: 0.5;\
}\
.switch-field label:first-of-type {\
border-radius: 4px 0 0 4px;\
}\
.switch-field label:last-of-type {\
border-radius: 0 4px 4px 0;\
}\
td:first-child > .raffle_infomation {\
display: block;\
}\
.infitem {\
max-width: 250px;\
position: absolute;\
}\
.infitem > .infname {\
background-color: transparent;\
white-space: normal;\
}\
.infitem > .infdesc {\
padding: 0;\
list-style: none;\
white-space: normal;\
}\
.userfeedpost { /* Replaces old js based height calculation */ \
max-height: 400px;\
}\
.transitions .userfeedpost, .transitions .pentry { /* Replaces old jquery colour animations that broke a lot */\
animation: fadeInDown 0.3s ease-out, RestoreColour 3.0s ease-out;\
animation-fill-mode: forwards;\
}\
.transitions .userfeedpost:hover {/* Fix feedback hover background colours */\
animation-fill-mode: none;\
animation-duration: 0s;\
}\
.transitions .switch-field label {\
transition: all 0.1s ease-in-out;\
}\
'
)
);
},
addNewUI: function() {
if(NoName.Storage.get('transitions', true)) {
$(document.body).addClass('transitions');
}
try {
//ScrapTF mode cooldown
unsafeWindow.$('#sendfeed').bind('click', function(e) {
if(!NoName.ScrapTF.canComment()) {
e.stopImmediatePropagation();
return false;
}
});
} catch (e) {
console.warn('[Raffle:addNewUI] Unable to add unsafeWindow event handler');
}
},
//Creates a toggle switch that can replace radio buttons
createSwitch: function(label, options) {
var $container = $('<div></div>').addClass('switch-field'),
children = [];
children.push($('<div></div>').addClass('switch-title').text(label));
options.forEach(function(option) {
children.push(
$('<input />')
.prop({
type: 'radio',
name: option.name,
id: option.id,
checked: !!option.checked
}).val(option.value)
);
children.push(
$('<label></label>')
.prop('for', option.id)
.text(option.label)
);
});
$container.append(children);
return $container;
},
};
//Profile pages
window.NoName.Profile = {
$feedbackType: null,
init: function() {
this.removeExistingUI();
this.addStyles();
this.addNewUI();
},
removeExistingUI: function() {
this.$feedbackType = $('#type1').parent();
this.$feedbackType.empty();
},
addStyles: function() {
$(document.head).append(
$('<style></style>').text(
'.nfbutton {\
margin-top: 0;\
margin-bottom: 10px;\
}\
.userfeed, .newfeed {\
clear: both;\
}\
.newfeed .switch-field {\
padding: 0;\
margin: 3px 0;\
}\
')
);
},
addNewUI: function() {
//Replace current type radio buttons with switch
$feedbackSwitch = NoName.UI.createSwitch('', [
{
name: 'type',
id: 'type1',
label: 'Positive',
value: '1',
},
{
name: 'type',
id: 'type2',
label: 'Negative',
value: '2',
},
{
name: 'type',
id: 'type0',
label: 'Neutral',
value: '0',
checked: true,
},
]);
this.$feedbackType.append($feedbackSwitch);
this.$feedbackType.prev().text('Type:'); //Add : for consistency
},
};
//Settings page
window.NoName.Settings = {
$settings: null,
init: function() {
this.removeExistingUI();
this.addStyles();
this.addNewUI();
this.addSettingsContainer();
this.addScriptSettings();
},
removeExistingUI: function() {
//Remove old radio buttons for raffle icon position
$('#fselec').next().nextAll().remove();
$('#fselec').prev().remove();
},
addStyles: function() {
$(document.head).append(
$('<style></style>').text(
'table {\
border-collapse: collapse;\
}\
.raffle_infomation form {\
position: relative;\
}\
.raffle_infomation table {\
width: 100%;\
}\
.raffle_infomation td { /* Make column widths consistent */\
width: 49%; /* Whitespace pls */\
display: inline-block;\
}\
.raffle_infomation h1 + strong {\
display: block;\
margin-bottom: 20px;\
margin-top: -10px;\
}\
.raffle_infomation td > img { /* Tweak positioning and size of sidepic preview */\
display: block;\
max-width: 100%;\
height: auto;\
margin: auto;\
}\
#fselec { /* Hide shitty unstylable file input and position it over the placeholder */\
position: absolute;\
left: 0;\
right: 0;\
width: 100%;\
height: 40px;\
top: 0;\
cursor: pointer;\
opacity: 0;\
z-index: 10;\
}\
#fname { /* Nicer looking placeholder for file input */\
font-family: TF2Build;\
color: #EBE2CA;\
font-size: 14px;\
line-height: 40px;\
max-width: 300px;\
overflow: hidden;\
white-space: no-wrap;\
text-overflow: ellipsis;\
display: block;\
}\
#tl + label {\
border-radius: 4px 0 0;\
}\
#tr + label {\
border-radius: 0 4px 0 0;\
}\
#bl + label {\
border-radius: 0 0 0 4px;\
}\
#br + label {\
border-radius: 0 0 4px;\
}\
#noname-settings .switch-field {\
display: inline-block;\
width: 49%;\
}\
'
)
);
},
addNewUI: function() {
var $iconWarning = $('#fselec').closest('.raffle_infomation').find('h3'),
$position = NoName.UI.createSwitch('Icon position:', [
{
name: 'position',
id: 'tl',
label: 'Top-left',
value: 'tl',
checked: true,
},
{
name: 'position',
id: 'tr',
label: 'Top-right',
value: 'tr',
},
{
name: 'position',
id: 'bl',
label: 'Bottom-left',
value: 'bl',
},
{
name: 'position',
id: 'br',
label: 'Bottom-right',
value: 'br',
}
]),
$fileName = $('<span></span>').prop('id', 'fname').text('Click to choose file');
//Add filename placeholder and position switch
$('#fselec').after($position.hide()).after($fileName);
//Update placeholder text when hidden input changes
$('#fselec').on('change', function() {
var file = this.files[0];
if(file) {
$position.show();
$fileName.text(file.name);
} else {
$position.hide();
$fileName.text('Click to choose file');
}
});
//Reformat sidepic warning in a nicer looking way
$iconWarning.replaceWith(
$('<strong></strong>').html($iconWarning.text().replace(' Use', '<br />Use'))
);
},
//Add a new section for our own settings
addSettingsContainer: function() {
this.$settings = $('<div></div>').addClass('raffle_infomation').prop('id', 'noname-settings')
.append(
$('<h1></h1>').text('Userscript with no name')
)
.append(
$('<strong></strong>').text('Settings for Userscript with no name')
)
.append(
$('<table></table>')
);
$('#content > .indent').append('<br />').append(this.$settings);
},
//Add script settings to new section
addScriptSettings: function() {
var storage = NoName.Storage,
transitionsEnabled = storage.get('transitions', true),
showAllItemsEnabled = storage.get('showallitems', false),
scrapEnabled = storage.get('scrap:enabled', false),
//Transitions
$transitions = NoName.UI.createSwitch('Animations: ', [
{
name: 'transitions',
id: 'transitions-disable',
label: 'Disabled',
value: '',
checked: !transitionsEnabled,
},
{
name: 'transitions',
id: 'transitions-enable',
label: 'Enabled',
value: true,
checked: transitionsEnabled,
}
]);
//Show all items in raffle list
$showAllItems = NoName.UI.createSwitch('Raffle list - Show all items: ', [
{
name: 'show-all-items',
id: 'show-all-items-disable',
label: 'Disabled',
value: '',
checked: !showAllItemsEnabled,
},
{
name: 'show-all-items',
id: 'show-all-items-enable',
label: 'Enabled',
value: true,
checked: showAllItemsEnabled,
}
]);
//ScrapTF mode
$scrap = NoName.UI.createSwitch('ScrapTF Mode (Chrome only)', [
{
name: 'scraptfmode',
id: 'scraptf-disable',
label: 'Disabled',
value: '',
checked: !scrapEnabled,
},
{
name: 'scraptfmode',
id: 'scraptf-enable',
label: 'Enabled',
value: true,
checked: scrapEnabled,
}
]),
//Update body class to toggle transitions
$transitions.on('change', function(e) {
//TODO: maybe change storage.listen to fire in the same tab
//Would save duplication like this
if(e.target.value) {
$(document.body).addClass('transitions');
} else {
$(document.body).removeClass('transitions');
}
storage.set('transitions', e.target.value);
});
$showAllItems.on('change', function(e) {
storage.set('showallitems', e.target.value);
});
//Enable scrapTF mode or check if it can be disabled
$scrap.on('change', function(e) {
if(e.target.value) {
NoName.ScrapTF.enable();
} else if(!NoName.ScrapTF.disable()) {
$('#scraptf-enable').prop('checked', true);
}
});
this.$settings.append($transitions).append($showAllItems).append($scrap);
}
};
//Emulates the... interesting choice to add sizeable cooldowns to everything in scrapTF
//Would be a good idea to not take this seriously, just saying, let me have my fun.
//TODO: Auctions?
window.NoName.ScrapTF = {
enabled: 0,
lastEntry: 0,
lastComment: 0,
COOLDOWN_RAFFLE: 10,
COOLDOWN_COMMENT: 30,
COOLDOWN_DISABLE: 30,
init: function() {
var that = this;
this.enabled = NoName.Storage.get('scrap:enabled', 0);
this.lastEntry = NoName.Storage.get('scrap:lastentry', 0);
this.lastComment = NoName.Storage.get('scrap:lastcomment', 0);
//Opening multiple tabs will not save you
NoName.Storage.listen('scrap:enabled', function(oldValue, newValue, url) {
that.enabled = newValue;
});
NoName.Storage.listen('scrap:lastentry', function(oldValue, newValue, url) {
that.lastEntry = newValue;
});
NoName.Storage.listen('scrap:lastcomment', function(oldValue, newValue, url) {
that.lastComment = newValue;
});
},
isEnabled: function() {
return !!this.enabled;
},
enable: function() {
var now = new Date().getTime();
if(this.enabled) {
return false;
}
NoName.Storage.set('scrap:enabled', now);
this.enabled = now;
alert('ScrapTF mode enabled');
},
//Dunno why you would want to turn it off but here you go
disable: function() {
var now = new Date().getTime(),
difference = (now - this.enabled) / 1000,
remaining = this.COOLDOWN_DISABLE - difference;
//No quick escape for you
if(remaining > 0) {
alert('Please wait ' + remaining.toFixed(0) + ' seconds to disable ScrapTF mode.');
return false;
} else {
this.enabled = 0;
NoName.Storage.set('scrap:enabled', '');
alert('ScrapTF mode disabled');
return true;
}
},
//Recent development
//Luckily we aren't a bot or we would be so screwed here!
canEnterRaffle: function() {
var now = new Date().getTime(),
difference = (now - this.lastEntry) / 1000,
remaining = this.COOLDOWN_RAFFLE - difference;
if(!this.enabled) {
return true;
}
if(remaining > 0) {
console.warn('[ScrapTF::canEnterRaffle] Blocking raffle entry');
alert('Please wait ' + remaining.toFixed(0) + ' seconds to enter this raffle.');
return false;
}
this.lastEntry = now;
NoName.Storage.set('scrap:lastentry', now);
return true;
},
//Stop spamming pls
canComment: function() {
var now = new Date().getTime(),
difference = (now - this.lastComment) / 1000,
remaining = this.COOLDOWN_DISABLE - difference;
if(!this.enabled) {
return true;
}
if(remaining > 0) {
console.warn('[ScrapTF::canComment] Blocking comment');
alert('Please don\'t spam');
return false;
}
NoName.Storage.set('scrap:lastcomment', now);
this.lastComment = now;
return true;
}
};
//New raffle page
window.NoName.NewRaffle = {
$itemList: null,
$selectedItemList: null,
$oldItems: null,
$banWarning: null,
$visibility: null,
$entry: null,
$type: null,
$start: null,
$submit: null,
backpack: null,
levelData: {},
init: function() {
var that = this;
this.$itemList = $('#allitems');
this.$selectedItemList = $('#selitems');
this.$banWarning = $('.ban_warning');
this.$visibility = $('#ptype1').parent();
this.$entry = $('#ptype2').parent();
this.$type = $('#af1').parent();
this.$start = $('#af2').parent();
this.$submit = $('#rafBut').parent();
this.removeExistingUI();
this.addStyles();
this.addSwitches();
this.addNewUI();
this.backpack = new NoName.Backpack({
container: this.$itemList,
selectedContainer: this.$selectedItemList,
autoRender: true,
autoLoad: true,
selectableItems: true,
});
},
addStyles: function() {
$(document.head).append(
$('<style></style>').text(
'@keyframes popin {\
0% {\
transform: scale(0);\
}\
100% {\
transform: scale(1);\
}\
}\
.indent {/*Make container elements wide enough to fit 10 columns of items*/\
width: 786px;\
padding: 0;\
}\
#content {\
width: 786px;\
padding: 5px 0;\
}\
#nav_holder {\
width: 786px;\
}\
.text_holder table { /*Make table full width in firefox*/\
width: 100%;\
}\
.itemtable { /*Fix weird position:relative behaviour*/\
position: static;\
text-align: center;\
padding-bottom: 15px;\
}\
.itemtable ol:empty, .itemtable.loading ol, .itemtable.error ol {/*Add minimum height when itemtables dont contain items*/\
height: 75px;\
}\
.itemtable.minimised ol {\
height: 100px;\
overflow: hidden;\
}\
.itemtable.minimised ol:after {\
content: "Show all items";\
display: block;\
width: 100%;\
bottom: 0;\
height: 40px;\
line-height: 40px;\
background-color: #2A2725;\
clear: both;\
}\
.itemtable.minimised ol:hover:after {\
text-decoration: underline;\
cursor: pointer;\
}\
.itemtable a {\
cursor: pointer;\
}\
.itemtable ol { /*Remove default list style*/\
padding: 10px 0 15px;\
list-style: none;\
margin: 0;\
overflow: auto;\
/*position: relative;*/\
}\
.itemtable ol:before {\
display: inline-block;\
width: 100%;\
text-align: center;\
}\
#allitems.loading ol:empty:before {\
content: "Loading backpack...";\
}\
#allitems ol:empty:before {\
content: "No selectable items in backpack";\
}\
#selitems ol:empty:before {\
content: "No items selected";\
}\
#mess {\
width: 100% !important; /*Override styles added by "New raffle page enhanced" script*/\
max-width: none !important;\
}\
#raffle-button {\
margin-top: 25px;\
}\
.text_holder > table tr:nth-child(9) > td {/*Add a bit of space above the toggles*/\
padding-top: 15px;\
}\
.text_holder > table tr:nth-child(7) > td:nth-child(3),\
.text_holder > table tr:nth-child(8) > td:nth-child(2),\
.text_holder > table tr:nth-child(9) > td:nth-child(2),\
.text_holder > table tr:nth-child(10) > td:nth-child(2) {\
padding-left: 10px;\
}\
.transitions .itemtable .item {\
animation: popin 0.15s cubic-bezier(.17,.67,.57,1.42);\
}\
'
)
);
},
addSwitches: function() {
var $visibility = NoName.UI.createSwitch('Raffle visiblity:', [
{
name: 'rafflepub',
id: 'ptype1',
label: 'Public',
value: 'public',
checked: true,
},
{
name: 'rafflepub',
id: 'ptype2',
label: 'Private',
value: 'private',
}
]);
var $entry = NoName.UI.createSwitch('Entry type:', [
{
name: 'invo',
label: 'Open',
id: 'af1',
value: 'false',
checked: true,
},
{
name: 'invo',
id: 'af2',
label: 'Invite only',
value: 'true',
}
]);
var $type = NoName.UI.createSwitch('Prize distribution:', [
{
name: 'split',
id: 'isplit1',
label: 'A21',
value: 'alltoone',
},
{
name: 'split',
id: 'isplit2',
label: '121',
value: 'onerperson',
checked: true
}
]);
var $start = NoName.UI.createSwitch('Start timer:', [
{
name: 'stype',
id: 'stype1',
label: 'Instantly',
value: 'instantly',
},
{
name: 'stype',
id: 'stype2',
label: 'After first entry',
value: 'afterjoin',
checked: true
}
]);
//Add radio button replacement toggles
this.$visibility.append($visibility);
this.$entry.append($entry);
this.$type.append($type);
this.$start.append($start);
},
addNewUI: function() {
//Detach entries and referer, also add :s for consistency
var that = this,
$entries = [
$('#maxentry').parent().prev().text('Maximum entries:').detach(),
$('#maxentry').parent().detach()
],
$referer = [
$('<td></td>').prop('colspan', 2),
$('#reffil').parent().prev().text('Referal filter:').detach(),
$('#reffil').parent().detach()
];
//Move entries after duration
$('#durr').parent().after($entries);
//Move referer to a new tr after duration/entries
$('#durr').closest('tr').after(
$('<tr></tr>').append($referer)
);
//Change defaults and other attributes to more sensible values
$('#rtitle').prop({
placeholder: 'Raffle title',
maxlength: 32,
onclick: null,
}).val('');
$('#mess').parent().prop('colspan', 3);
$('#mess').prop({
maxlength: 2048,
});
$('#durr').addClass('full-width').val(3600);
$('#maxentry').addClass('full-width').prop({
type: 'number',
}).val(1000);
$('#reffil').addClass('full-width').prop('placeholder', '*').val('');
//Make selected items style consistent with backpack items
this.$selectedItemList.addClass('itemtable');
//Add <colgroup> to form table to make column widths consistent
$('.text_holder table').prepend(
$('<colgroup></colgroup>').append([
$('<col />').css('width', '19%'), //Account for padding on 3rd column. Not nice but calc() doesn't work properly here.
$('<col />').css('width', '30%'),
$('<col />').css('width', '20%'),
$('<col />').css('width', '30%'),
])
);
//TODO: perhaps just overwrite the existing create function via unsafeWindow?
this.$submit.append(
$('<button></button>').prop({
type: 'button',
id: 'raffle-button' //Different id to prevent old event handler triggering
}).addClass('full-width')
.text('Raffle it!')
.on('click', function() {
that.createRaffle();
})
);
//Lock visibility to private when invite only is selected
$('#af1, #af2').on('change', function() {
if(this.value === 'true') {
$('#ptype2').prop({
checked: true,
});
}
$('#ptype1, #ptype2').prop('disabled', this.value === 'true');
});
},
removeExistingUI: function() {
//Remove games selection
$('#allgames').parent().prev().remove();
$('#allgames').parent().remove();
//Remove remaining unneeded radio button <tr>s
$('#isplit1').closest('tr').remove();
$('#stype1').closest('tr').remove();
//I'm sure anyone using this already knows the rules
this.$banWarning.remove();
//Empty things we are going to replace
this.$visibility.empty();
this.$entry.empty();
this.$type.empty();
this.$start.empty();
//Remove existing button so I can readd it again without existing event handlers
$('#rafBut').remove();
$('.infitem').remove();
},
createRaffle: function() {
var items = [];
$('#raffle-button').prop('disabled', true).val('Please wait...');
this.backpack.getSelected().forEach(function(item) {
data = [
item.getDefIndex(),
item.getQuality(),
item.getLevel(),
''//item.getSeries() //This is always empty in the original inventory apparently
];
items.push(data.join(':'));
});
$.ajax({
type: 'POST',
url: 'http://tf2r.com/job.php',
dataType: 'json',
data: {
postraffle: 'true',
title: $('#rtitle').val(),
message: $('#mess').val(),
maxentry: $('#maxentry').val(),
duration: $('#durr').val(),
filter: $('#reffil').val() || '*',
split: $('input[name=split]:checked').val(),
pub: $('input[name=rafflepub]:checked').val(),
stype: $('input[name=stype]:checked').val(),
invo: $('input[name=invo]:checked').val(),
items: items,
games: [],
}
}).done(function(data){
if(data.status == 'fail') {
alert(data.message);
$('#raffle-button').prop('disabled', false).val('Raffle it!');
} else if(data.status == 'ok') {
//TODO: track raffled items here
window.location.href = 'http://tf2r.com/k' + data.key;
}
});
},
};
window.NoName.RaffleList = {
init: function() {
var that = this;
this.removeExistingUI();
this.addStyles();
this.addNewUI();
//Override getItems function to add +x overflow and optional showing of all items
unsafeWindow.getItems = exportFunction(function() {
return that.getItemsOverride();
}, unsafeWindow);
//Override check raffles function to remove display: none from raffle header
unsafeWindow.checkraffles = exportFunction(function() {
return that.checkrafflesOverride();
}, unsafeWindow);
//Update item lists if show all setting changes
NoName.Storage.listen('showallitems', function(oldValue, newValue, url) {
unsafeWindow.getItems();
});
},
//TODO: This repeats a lot of what getitems() does
//Can they be merged?
checkrafflesOverride: function() {
var that = this;
console.log('here');
if(!unsafeWindow.focused) {
setTimeout(unsafeWindow.checkraffles, 5000);
return;
}
$.ajax({
type: 'post',
url: 'http://tf2r.com/job.php',
dataType: 'json',
data: {
'checkpublicraffles': 'true',
'lastpraffle': unsafeWindow.lpr,
},
success: function(data) {
if(data.status != 'ok') {
alert(data.message);
return;
}
if(data.message.newraf.length) {
that.populateRaffles(data.message.newraf);
}
unsafeWindow.ih();
}
});
setTimeout(unsafeWindow.checkraffles, 5000);
},
getItemsOverride: function() {
var list = [],
that = this;
$('.jqueryitemsgather').each(function(index, object) {
list[index] = $(object).attr('rqitems');
});
$.ajax({
type: 'post',
url: 'http://tf2r.com/job.php',
dataType: 'json',
data: {
'getitems': 'true',
'list': list.join(';'),
},
success: function(data){
if(data.status != 'ok') {
alert(data.message);
return;
}
if(data.message.items) {
that.populateRaffleItems(data.message.items);
}
}
});
},
populateRaffles: function(raffles) {
var showAll = NoName.Storage.get('showallitems', false);
for(id in raffles) {
var ent = raffles[id],
itemlist = '',
remaining = 0,
wid = 644,
$header,
$content;
for(var iid in ent.items) {
var eent = ent.items[iid];
//Leave space for "+x" if show all items is disabled
if(!showAll && (wid -= eent.wid) <= 74) {
remaining++;
continue;
}
itemlist += getItem(eent);
}
if(unsafeWindow.lpr < ent.id) {
unsafeWindow.lpr = ent.id;
}
//Not proud of this but theres a lot of html to build
//Raffle header
$header = $('<div></div>').addClass('pubrhead').append(
$('<div></div>').addClass('pubrhead-text-left').append(
$('<a></a>') //Username
.prop('href', ent.link)
.css('color', '#' + ent.color)
.text(ent.name)
).append(
$('<div></div>').addClass('pubrhead-text-right').append(
$('<a></a>').prop('href', ent.rlink).text(ent.rname) //Raffle name
)
).append(
$('<div></div>').addClass('pubrhead-arrow-border')
).append(
$('<div></div>').addClass('pubrhead-arrow')
)
);
//Raffle content
$content = $('<div></div>').addClass('pubrcont').append(
$('<div></div>').addClass('pubrleft').append(
$('<div></div>').addClass('pubrav').append(
$('<a></a>').prop('href', ent.link).append(
$('<img />').prop('src', ent.avatar).css({ //User avatar
width: '64px',
height: '64px',
})
)
)
).append(
$('<div></div>').addClass('pubrarro')
)
).append(
$('<div></div>').addClass('pubrright').html(itemlist) //Items
.attr('data-overflow', (remaining && !showAll) ? ('+' + remaining) : undefined)
);
$('.participants').prepend('<div class="clear"></div>').prepend($content).prepend($header);
}
},
populateRaffleItems: function(items) {
var showAll = NoName.Storage.get('showallitems', false);
$('.jqueryitemsgather').empty().each(function() {
var width = $(this).width() - 74,
raffle = $(this).attr('rqitems'),
remaining = 0;
$(this).removeAttr('data-overflow');
for(var id in items) {
var ent = items[id];
if(ent.rkey != raffle) {
continue;
}
//Leave space for "+x" if show all items is disabled
if(!showAll && (width -= ent.wid) <= 74) {
remaining++;
continue;
}
$(this).append(unsafeWindow.getItem(ent));
}
//Add +x if there are any undisplayed items
if(remaining && !showAll) {
$(this).attr('data-overflow', '+' + remaining);
}
});
},
removeExistingUI: function() {
},
addStyles: function() {
$(document.head).append(
$('<style></style>').text(
'.pubrright{\
max-width: 86%;\
width: 86%;\
position: relative;\
background-color: inherit;\
}\
.pubrright[data-overflow]:after {\
content: attr(data-overflow);\
position: absolute;\
top: 0;\
right: 0;\
display: block;\
width: 74px;\
background-color: inherit;\
margin: 3px;\
height: 68px;\
line-height: 68px;\
font-family: TF2Build;\
text-align: center;\
font-size: 32px;\
color: #837768;\
cursor: pointer;\
}\
.pubrright[data-overflow]:hover:after {\
text-shadow: 1px 1px 1px #FF6407;\
}\
.pubrarro {\
left: 80px;\
}\
')
);
},
addNewUI: function() {
},
};
window.NoName.Raffle = {
raffleID: '',
init: function() {
var that = this;
this.raffleID = window.location.pathname.substring(2, 8);
this.removeExistingUI();
this.addStyles();
this.addNewUI();
//Override checkraffle to fix some things
unsafeWindow.checkraffle = exportFunction(function() {
return that.checkRaffleOverride();
}, unsafeWindow);
},
checkRaffleOverride: function() {
$.ajax({
type: 'post',
url: 'http://tf2r.com/job.php',
dataType: 'json',
data: {
'checkraffle': 'true',
'rid': this.raffleID,
'lastentrys': unsafeWindow.entryc,
'lastchat': unsafeWindow.lastchat
}
}).done(function(data) {
var ent;
if(data.status != 'ok') {
alert(data.message);
return;
}
if(data.message.ended && !unsafeWindow.ended) {
window.location.reload();
}
$('#entry').html(data.message.cur_entry + '/' + data.message.max_entry);
unsafeWindow.entryc = data.message.entry;
unsafeWindow.lastchat = data.message.chatmax;
unsafeWindow.tleft = data.message.timeleft;
unsafeWindow.nwc = data.message.wc;
if(!unsafeWindow.started && data.message.started) {
unsafeWindow.started = true;
unsafeWindow.updateTimer();
}
//Replaced comment container height calculation with css max-height
//Removed display: none here, it isnt needed now we have css transitions
//TODO: Make this less terrible
for(var id in data.message.chaten) {
ent = data.message.chaten[id];
$('.userfeed').prepend('<div class="userfeedpost" style="background-color:#' + ent.color + ';"><div class="ufinf"><div class="ufname"><a href="' + ent.url + '" style="color:#' + ent.color + ';">' + ent.name + '</a></div><div class="ufavatar"><a href="' + ent.url + '"><img src="' + ent.avatar + '"></a></div></div><div class="ufmes">' + ent.message + '</div></div>');
}
for(id in data.message.newentry) {
ent = data.message.newentry[id];
if(unsafeWindow.lastname != ent.name) {
$('#pholder').prepend('<div class="pentry"><div class="pavatar"><a href="' + ent.link + '"><img src="' + ent.avatar + '" width="64px" height="64px" /></a></div><div class="pname"><a href="' + ent.link + '" style="color:#' + ent.color + ';">' + ent.name + '</a></div></div>');
}
unsafeWindow.lastname = ent.name;
}
});
setTimeout(unsafeWindow.checkraffle, (unsafeWindow.ended) ? 5000 : 3500);
},
removeExistingUI: function() {
},
addStyles: function() {
$(document.head).append(
$('<style></style>').text(
'.newfeed {\
padding: 10px;\
}\
div[style="height:30px;"] {\
height: auto !important;\
margin-bottom: 5px;\
text-align: right;\
}\
.newfeed td[width="70px"] {\
width: 100px;\
padding-right: 10px;\
}\
.newfeed td[width="660px"] {\
width: 650px;\
}\
.raffle_infomation td[colspan="3"] { /* Show newlines in descriptions */\
white-space: pre-line;\
}\
tr:nth-child(7) > .raffle_infomation { /* Ensure raffle prizes container fits 10 items per row */\
min-width: 740px;\
}\
')
);
},
addNewUI: function() {
//ScrapTF mode cooldown
try {
unsafeWindow.$('#enbut').bind('click', function(e) {
if(!NoName.ScrapTF.canEnterRaffle()) {
e.stopImmediatePropagation();
return false;
}
});
} catch (e) {
console.warn('[Raffle:addNewUI] Unable to add unsafeWindow event handler');
}
},
};
window.NoName.Steam = {
steamID: '',
JSON_URL: '',
init: function() {
this.getSteamID();
this.JSON_URL = 'http://steamcommunity.com/profiles/' + this.steamID + '/inventory/json/440/2/';
},
getSteamID: function() {
try {
var result = $('#avatar > a').first().prop('href').match(/https?:\/\/tf2r.com\/user\/(\d+)\.html/);
this.steamID = result[1];
} catch(e) {
console.warn('[Steam::getSteamID] Unable to determine steamID');
}
},
fetchInventoryJSON: function() {
var defer = jQuery.Deferred();
if(!this.steamID) {
defer.reject();
return defer;
}
GM_xmlhttpRequest({
method: 'GET',
url: this.JSON_URL,
onload: function(response) {
defer.resolve(response.responseText);
},
onerror: function(response) {
console.error('[Steam::fetchBackpack] Failed to retrieve inventory JSON: ' + response.textStatus);
defer.reject();
}
});
return defer;
}
};
//Object that handles parsing and displaying of user's backpack
window.NoName.Backpack = function(options) {
var that = this;
this.selectableItems = !!options.selectableItems;
this.autoRender = !!options.autoRender;
this.autoLoad = !!options.autoLoad;
this.$container = options.container;
this.$selectedContainer = options.selectedContainer;
this.$info = null;
this.items = [];
this.selectedItems = [];
this.levelData = {};
if(!this.$selectedContainer || !this.$selectedContainer.length) {
console.error('[Backpack] $container does not exist');
return false;
}
function _initElements() {
that.$container.empty().append($('<ol></ol>'));
if(that.selectableItems) {
if(!that.$selectedContainer || !that.$selectedContainer.length) {
console.error('[Backpack] $selectedContainer must exist for items to be selectable');
return false;
}
that.$selectedContainer.empty().append($('<ol></ol>'));
that.$container.on('click', 'li', function() {
that.select(this);
});
that.$selectedContainer.on('click', 'li', function() {
that.deselect(this);
});
}
that.$info = $('<div></div>').addClass('infitem').append([
$('<strong></strong>').addClass('infname'),
$('<ul></ul>').addClass('infdesc'),
]);
that.$container.append(that.$info);
}
function _initEvents() {
that.$container.on('ei:backpackfailed', function() {
that.$container.addClass('error');
that.$container.append(
$('<a></a>').text('Failed to load backpack. Click to retry.')
.click(function() {
that.backpack.load();
})
);
});
that.$container.on('click', 'ol', function(event) {
if(event.target == this) {
$(event.delegateTarget).removeClass('minimised');
that.render(false, 10);
}
});
that.$container.on('mouseover', '.item', function(event) {
_handleHover(event);
}).on('mouseout', '.item', function() {
that.$info.hide();
});
that.$selectedContainer.on('mouseover', '.item', function(event) {
_handleHover(event);
}).on('mouseout', '.item', function() {
that.$info.hide();
});
}
function _handleHover(event) {
var item = event.target.item,
descriptions = item.getDescriptions(),
$list = [],
pos = $(event.target).offset(),
height = $(event.target).height(),
width = $(event.target).width();
//Item name
that.$info.children('.infname')
.removeClass()
.addClass('infname q' + item.getQuality())
.text(item.getName());
//Item level and type
that.$info.children('.infdesc').empty().append(
$('<li></li>').text(item.type)
);
//Other description strings
descriptions.forEach(function(description) {
$list.push(
$('<li></li>').text(description.value).css({
color: (description.color) ? '#' + description.color : '#ffffff',
})
);
});
that.$info.children('.infdesc').append($list);
that.$info.show();
//Position popup properly
that.$info.css({
'left': pos.left + (width / 2) - (that.$info.width() / 2) + 'px',
'top': pos.top + height + 'px'
});
}
//Parse default item list to get levels of items that do not have a level in the steam inventory json
function _getLevelData() {
that.$container.find('.item').each(function() {
var defindex = $(this).attr('iid'),
level = $(this).attr('ilevel'),
quality = $(this).attr('iqual');
//Uniques always have levels and skins never do, so no need to include them
//Items of other qualities may be strangified, so need to include them just in case
//Removed for now, seen some cases of unique items having no level
// if(quality == 6 || quality == 15) {
// return;
// }
if(!level) {
return;
}
that.levelData[defindex] = that.levelData[defindex] || {};
that.levelData[defindex][quality] = that.levelData[defindex][quality] || [];
that.levelData[defindex][quality].push(level);
});
}
//Populate items array with item objects created from parsed data
function _populateItems(items) {
for(var item in items) {
item = items[item];
that.items.push(new NoName.Item(item, that.levelData));
}
}
//Use a webworker to parse the json and clean up data
//Doing this on the main thread causes noticable lag
this.parseJSON = function(json) {
var that = this,
defer = jQuery.Deferred(),
//Create a blob from the below workerParse function to allow its use in the worker
work = URL.createObjectURL(
new Blob([
'(',
this.workerParse.toString(),
')()'
], {
type: 'application/javascript'
})
),
//Create worker
worker = new Worker(work);
//Listen for worker response
worker.addEventListener('message', function(e) {
if(e.data.success) {
console.info('[Backpack::parseJSON] Backpack parsed');
_populateItems(e.data.items);
defer.resolve();
} else {
console.error('[Backpack::parseJSON] Failed to parse backpack: ' + e.data.error);
defer.reject();
}
}, false);
//Send the worker the json to parse
worker.postMessage(json);
URL.revokeObjectURL(work);
return defer;
};
//Used by web worker to parse the json and then restructure the parsed data
this.workerParse = function() {
self.addEventListener('message', function(event) {
try {
var data = JSON.parse(event.data),
items = parseItems(data);
if(items) {
self.postMessage({success: true, items: items});
} else {
throw new Error('Item parsing failed');
}
} catch (e) {
console.error('[Steam::workerParse] Error while parsing JSON : ' + e);
self.postMessage({success: false, error: e.toString()});
}
});
//Checks the parsed json is a valid response
//Merges item and description arrays into a single item array
//Removes unneeded item data
function parseItems(data) {
var items = data.rgInventory,
descriptions = data.rgDescriptions,
parsedItems = [];
if(!data.success) {
console.error('[Backpack::workerParse] Success property is false');
return false;
}
if(!items) {
console.error('[Backpack::workerParse] Inventory array missing');
return false;
}
if(!descriptions) {
console.error('[Backpack::workerParse] Descriptions array missing');
return false;
}
for(var item in items) {
item = items[item];
var classInstanceId = item.classid + '_' + item.instanceid,
description = descriptions[classInstanceId];
parsedItems.push(parseItem(item, description));
}
parsedItems.sort(function(item1, item2) {
return item1.position - item2.position;
});
return parsedItems;
}
//Parses a single item
//Extracts level and series data where possible
//Loops over descriptions to determine which ones to item.descriptions
//Moved to worker as it is very slow
function parseItem(item, description) {
var level = description.type.match(/.*Level (\d+).*/),
series = description.name.match(/.*Series #(\d+).*/);
level = (level) ? level[1] : 0;
series = (series) ? series[1] : 0;
var parsedItem = {
id: item.id,
position: item.pos,
defindex: description.app_data.def_index,
quality: description.app_data.quality,
name: description.name,
type: description.type,
level: level,
series: series,
tradable: description.tradable,
descriptions: [],
tags: [],
thumbnail: description.icon_url,
image: description.icon_url_large,
classes: [],
};
parseDescriptions(parsedItem, description.descriptions || []);
parseTags(parsedItem, description.tags || []);
return parsedItem;
}
//Loops over the description strings for an item and returns the ones we care about
//Moved to the worker as it is slow enough to cause noticeable ui lag
function parseDescriptions(item, descriptions) {
descriptions.forEach(function(description) {
//Basic killstreak
if(description.value === 'Killstreaks Active') {
item.descriptions.push(description);
return;
}
//Specialized killstreak, Professional killstreak,
//Gifts, Paint, Crafted, Unusual effects, Stat-clock, Festivised
if(!description.value.indexOf('Sheen: ') ||
!description.value.indexOf('Killstreaker: ') ||
!description.value.indexOf('\nGift from: ') ||
!description.value.indexOf('Paint Color: ') ||
!description.value.indexOf('Crafted by ') ||
!description.value.indexOf('Festivized') ||
!description.value.indexOf('Strange Stat Clock Attached') ||
!description.value.indexOf('★ Unusual Effect')) {
item.descriptions.push(description);
return;
}
//Spells
if(!description.value.indexOf('Halloween: ')) {
description.value = description.value.replace('(spell only active during event)', '');
item.descriptions.push(description);
return;
}
//Custom descriptions, strange parts
if(description.value.match(/^\'\'.*\'\'$/) ||
description.value.match(/^\(.*:.*\)$/)) {
item.descriptions.push(description);
return;
}
//Collection grades
if((grade = description.value.match(/^(\w+) Grade (.*)$/)) && description.color) {
item.descriptions.push(description);
return;
}
});
return item;
}
//Parse item tags that we care about
//Not yet implemented
function parseTags(item, tags) {
tags.forEach(function(tag) {
switch(tag.category) {
case 'Rarity':
item.grade = tag.name.toLowerCase();
return;
case 'Class':
item.classes.push(tag.name.toLowerCase());
return;
case 'Exterior':
item.wear = tag.name;
return;
case 'Type' :
item.slot = tag.name;
return;
}
});
return item;
}
};
_getLevelData();
_initElements();
_initEvents();
if(this.autoLoad) {
this.load();
}
};
window.NoName.Backpack.prototype = {
render: function(empty, fromPos, toPos) {
var that = this,
items = document.createDocumentFragment();
if(toPos) {
this.$container.addClass('minimised');
}
if(empty) {
this.$container.children('ol').empty();
}
this.items.slice(fromPos, toPos).forEach(function(item, index) {
if(item.isTradable()) {
items.appendChild(that.renderItem(item));
}
});
this.$container.children('ol').append(items);
},
//Using standard javascript, need all the performance I can get here
renderItem: function(item) {
var element = document.createElement('li');
element.className = 'item q' + item.getQuality();
if(item.getGrade()) {
element.className += ' hasgrade g' + item.getGrade();
}
element.item = item;
element.style.backgroundImage = 'url(' + item.getThumbnail() + ')';
return element;
},
load: function(force) {
var that = this;
jsonLoad = NoName.Steam.fetchInventoryJSON();
this.$container.trigger('ei:backpackloading');
this.$container.addClass('loading');
jsonLoad.done(function(json) {
that.parseJSON(json).done(function() {
that.$container.trigger('ei:backpackloaded');
if(that.autoRender) {
that.render(true, 0, 10);
}
}).fail(function() {
that.$container.trigger('ei:backpackfailed');
});
}).fail(function() {
console.error('[Backpack::load] Failed to load backpack');
this.$container.trigger('ei:backpackfailed');
}).always(function() {
that.$container.removeClass('loading');
});
},
select: function(element) {
var item = element.item;
if($(element).parents().index(this.$container) === -1) {
console.error('[Backpack::select] item is not a descendant of item container');
return false;
}
if(this.items.indexOf(item) < 0) {
console.error('[Backpack::select] Item does not exist in backpack');
return false;
}
this.selectedItems.push(item);
this.$selectedContainer.children('ol').append(element);
return true;
},
deselect: function(element) {
var item = element.item,
index = this.selectedItems.indexOf(item);
if(index < 0) {
console.error('[Backpack::deselect] Item does not exist in backpack');
return false;
}
if($(element).parents().index(this.$selectedContainer) === -1) {
console.error('[Backpack::deselect] item is not a descendant of selected item container');
return false;
}
this.selectedItems.splice(index, 1);
this.$container.children('ol').append(element);
return true;
},
isSelected: function(item) {
return !!this.selectedItems.indexOf(item) > -1;
},
getSelected: function() {
return this.selectedItems;
}
};
//Object that represents a single item
window.NoName.Item = function(data, levelData) {
var that = this;
//General stuff
this.id = data.id;
this.defindex = parseInt(data.defindex);
this.quality = parseInt(data.quality);
this.tradable = !!data.tradable;
this.name = data.name || '';
this.type = data.type || '';
this.level = data.level;
this.grade = data.grade || null;
this.classes = data.classes || [];
//Fallback to level data found in the default item list if the json api didn't give us one
//This should only be needed for stranges, as they don't show levels in the steam inventory
//Using this data is a guess, but it will usually be correct unless the user has multiple copies of the same strange
//which differ in a noticeable way such as parts
if(data.level) {
this.level = data.level;
} else if(levelData && levelData[this.defindex] && levelData[this.defindex][this.quality]) {
this.level = levelData[this.defindex][this.quality];
} else {
this.level = 1; //The site uses 1 for items that don't have a level
}
this.series = data.series || 0;
this.thumbnail = this.IMAGE_URL + data.thumbnail;
this.image = this.IMAGE_URL + data.image;
this.position = data.position;
this.descriptions = data.descriptions;
this.tags = data.tags;
};
window.NoName.Item.prototype = {
IMAGE_URL: 'https://steamcommunity-a.akamaihd.net/economy/image/',
getDefIndex: function() {
return this.defindex;
},
getName: function() {
return this.name;
},
getThumbnail: function() {
return this.thumbnail;
},
getQuality: function() {
return this.quality;
},
getGrade: function() {
return this.grade;
},
getLevel: function() {
return this.level;
},
getSeries: function() {
return this.series;
},
getPosition: function() {
return this.position;
},
isTradable: function() {
return this.tradable;
},
getDescriptions: function() {
return this.descriptions;
},
//Determines if item matches search filters
//Not currently used
matchesFilters: function(filters) {
if(filters.quality) {
if(filters.quality.isArray() && filters.quality.indexOf(this.quality) == -1) {
return false;
} else if(filters.quality != this.quality) {
return false;
}
}
if(filters.classes) {
if(filters.classes.isArray()) {
var match = false;
for(var classes in filters.classes) {
if(this.classes.indexOf(filters.classes[classes])) {
match = true;
break;
}
}
if(!match) {
return false;
}
} else if(this.classes.indexOf(filters.classes) == -1) {
return false;
}
}
if(filters.text) {
if(this.name.indexOf(text) !== 0) {
return false;
}
}
return true;
}
};
$(document).ready(function () {
//Nu iframes pls
if(window.top == window.self) {
window.NoName.init();
}
});