您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Overhauls the new raffle page and enhances a few others
当前为
// ==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.995 // @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(); } }, //Export override functions to unsafe window //This needs to be run both as early as possible and after page load //We can't know when our script runs relative to the scripts on the page so we need to cover both eventualities exportOverrides: function() { console.log('[NoName::exportOverrides] Exporting overrides'); try { this.UI.exportOverrides(); this.Raffle.exportOverrides(); this.RaffleList.exportOverrides(); } catch (e) { console.error(e); } }, addStyles: function() { this.UI.addStyles(); this.NewRaffle.addStyles(); this.Settings.addStyles(); this.Profile.addStyles(); this.RaffleList.addStyles(); this.Raffle.addStyles(); }, }; 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(); NoName.Storage.listen('transitions', function(oldValue, newValue, url) { if(newValue) { $(document.body).addClass('transitions'); } else { $(document.body).removeClass('transitions'); } }); }, exportOverrides: function() { var that = this; //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); }, 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;\ }\ /* Replaces old jquery colour animations that broke a lot */\ .transitions .userfeedpost, .transitions .pentry, .transitions .pubrhead, .transitions .pubrcont {\ 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, $progress: null, init: function() { this.$progress = $('table tr:nth-child(2) > td > table tr:nth-child(2) > td:nth-child(2)'); 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;\ }\ #progress > div {\ padding: 0 !important;\ overflow: hidden;\ height: 23px !important;\ position: relative;\ border-radius: 3px;\ }\ #progress div div {\ position: absolute;\ top: 0 !important;\ left: 1px;\ bottom: 0;\ margin: auto;\ line-height: 21px;\ height: 21px;\ border-radius: 3px 0 0 3px;\ }\ #progress div div + div {\ right: 1px;\ }\ ') ); }, 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 this.$progress.prop('id', 'progress'); }, }; //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(); //Update item lists if show all setting changes NoName.Storage.listen('showallitems', function(oldValue, newValue, url) { unsafeWindow.getItems(); }); }, exportOverrides: function() { var that = this; //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); }, //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; } // data.message.newraf.push({ // rname: 'Test raffle', // name: 'Jim', // color: '#8650ac', // items: [ // {q:'q6'},{q:'q6'},{q:'q6'},{q:'q6'},{q:'q6'},{q:'q6'},{q:'q6'},{q:'q6'},{q:'q6'},{q:'q6'}, // ], // avatar: 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/3c/3ca4b5a14f243e6236463e6921db739ce6ae1e7f_medium.jpg', // rlink: 'http://tf2r.com/kiq2b8d.html', // link: 'http://tf2r.com/user/76561197994939255.html', // }); 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 += unsafeWindow.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;\ margin-bottom: 7px;\ }\ .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(); }, exportOverrides: function() { var that = this; //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; } }; //Export override functions window.NoName.exportOverrides(); //Add styles early //window.NoName.addStyles(); $(document).ready(function() { //Nu iframes pls if(window.top == window.self) { //Export override functions again to make sure window.NoName.exportOverrides(); //Lets get this party started window.NoName.init(); } });