您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
try to take over the world!
// ==UserScript== // @name GSJ TOP // @namespace http://tampermonkey.net/ // @version 8.2 // @description try to take over the world! // @author DK, Shikokuchuo // @include https://*.the-west.*/game.php* // @icon https://www.google.com/s2/favicons?sz=64&domain=the-west.pl // @grant none // ==/UserScript== (function() { 'use strict'; (function (fn) { setTimeout(function() { var script = document.createElement('script'); script.setAttribute('type', 'application/javascript'); script.textContent = '(' + fn + ')();'; document.body.appendChild(script); document.body.removeChild(script); }, 5000); // 5000 ms = 5 sekund })(function () { // Inicjalizacja głównego obiektu GSJTOP GSJTOP = { version: '8.2', name: 'GSJ TOP', author: 'Shiko', minGame: '2.01', maxGame: Game.version.toString(), toLoad: 0, loaded: 0, xMax: 181, yMax: 79, blockMaxLength: 300, dataLoaded: false, silverJobBbColor: '#708090', goldJobBbColor: '#AB9930', refreshInterval: null, preferences: { showSilver: localStorage.getItem('GSJTOP_showSilver') !== 'false', showGold: localStorage.getItem('GSJTOP_showGold') !== 'false', enabled: localStorage.getItem('GSJTOP_enabled') !== 'false', hideLowIdJobs: localStorage.getItem('GSJTOP_hideLowIdJobs') === 'true', hideHighIdJobs: localStorage.getItem('GSJTOP_hideHighIdJobs') === 'true', showTooltips: localStorage.getItem('GSJTOP_showTooltips') !== 'false', hideFilters: localStorage.getItem('GSJTOP_hideFilters') === 'true', currentPage: 0, jobsPerPage: parseInt(localStorage.getItem('GSJTOP_jobsPerPage')) || 8, hiddenJobs: JSON.parse(localStorage.getItem('GSJTOP_hiddenJobs') || '{}') }, hiddenImageOpacity: 0.35, shownImageOpacity: 1, bestJobTime: 0, position: { top: 0, right: 0 }, langs: { pl: { language: 'Polish (polski)', ApiGui: 'Ten skrypt automatycznie wyszukuje złote i srebrne prace na mapie.', title: 'Złote i srebrne prace', loading: 'Ładowanie... To zajmie moment', next: '→', prev: '←', enableScript: 'Włącz skrypt', visibilityTitle: 'Widoczność prac', toggleVisibilityTooltip: 'Pokaż/Ukryj Mapę i Widoczność Prac', searchPlaceholder: 'Szukaj pracy...', clearSearch: 'X', unselectAll: 'Odznacz wszystkie', selectAll: 'Zaznacz wszystkie', silverJobsTitle: 'Srebrne prace', goldJobsTitle: 'Złote prace', showHighIdJobs: 'POKAŻ PRACE POWYŻEJ 150 POZIOMU', hideHighIdJobs: 'UKRYJ PRACE POWYŻEJ 150 POZIOMU', showLowIdJobs: 'POKAŻ PRACE PONIŻEJ 100 POZIOMU', hideLowIdJobs: 'UKRYJ PRACE PONIŻEJ 100 POZIOMU', showInfo: 'POKAŻ INFORMACJE', hideInfo: 'UKRYJ INFORMACJE', jobWordSingular: 'praca', jobWordPluralFew: 'prace', jobWordPluralMany: 'prac', tooltipExperience: 'DOŚWIADCZENIE', tooltipMoney: 'DOLARY', tooltipMotivation: 'MOTYWACJA W PRACY', tooltipTime: 'CZAS DOTARCIA' }, en: { // Dodane tłumaczenia angielskie language: 'English', ApiGui: 'This script automatically searches for gold and silver jobs on the map.', title: 'Gold and Silver Jobs', loading: 'Loading... This will take a moment', next: '→', prev: '←', enableScript: 'Enable Script', visibilityTitle: 'Job Visibility', toggleVisibilityTooltip: 'Show/Hide Map and Job Visibility', searchPlaceholder: 'Search job...', clearSearch: 'X', unselectAll: 'Unselect all', selectAll: 'Select all', silverJobsTitle: 'Silver Jobs', goldJobsTitle: 'Gold Jobs', showHighIdJobs: 'SHOW JOBS ABOVE LEVEL 150', hideHighIdJobs: 'HIDE JOBS ABOVE LEVEL 150', showLowIdJobs: 'SHOW JOBS BELOW LEVEL 100', hideLowIdJobs: 'HIDE JOBS BELOW LEVEL 100', showInfo: 'SHOW INFO', hideInfo: 'HIDE INFO', jobWordSingular: 'job', jobWordPluralFew: 'jobs', jobWordPluralMany: 'jobs', tooltipExperience: 'EXPERIENCE', tooltipMoney: 'DOLLARS', tooltipMotivation: 'MOTIVATION IN JOB', tooltipTime: 'TRAVEL TIME' } }, updateLang: function () { var lg = GSJTOP.langs; GSJTOP.lang = lg[localStorage.getItem('scriptsLang')] ? localStorage.getItem('scriptsLang') : lg[Game.locale.substr(0, 2)] ? Game.locale.substr(0, 2) : 'en'; GSJTOPlang = lg[GSJTOP.lang]; } }; GSJTOP.updateLang(); // Funkcja tworząca przełączniki (checkboxy) w interfejsie GSJTOP.createToggleCheckbox = function() { var container = $('<div/>', { id: 'gj-toggle', css: { position: 'relative', // Zmieniono z 'static' na 'relative' top: '30px', // Dodano, aby obniżyć element o 30px display: 'inline-block', float: 'right', marginRight: '-30px', zIndex: 10002, backgroundColor: 'rgba(255, 255, 255, 0.0)', padding: '5px', borderRadius: '3px' } }); // Główny checkbox włączający skrypt var checkbox = $('<input/>', { type: 'checkbox', id: 'gj-checkbox', checked: this.preferences.enabled }); // Checkbox do ukrywania filtrów - poprawiona inicjalizacja var hideFiltersCheckbox = $('<input/>', { type: 'checkbox', id: 'gj-hide-filters', checked: !this.preferences.hideFilters, css: { marginLeft: '5px' } }); checkbox.on('change', function() { GSJTOP.preferences.enabled = this.checked; localStorage.setItem('GSJTOP_enabled', this.checked.toString()); if (this.checked) { GSJTOP.init(); } else { GSJTOP.closeWindow(); // DODANE: Zamknij okno mapy i panel widoczności, gdy skrypt jest wyłączony $('#custom-map-window').remove(); $('#job-visibility-window').remove(); } }); hideFiltersCheckbox.on('change', function() { GSJTOP.preferences.hideFilters = !this.checked; localStorage.setItem('GSJTOP_hideFilters', GSJTOP.preferences.hideFilters.toString()); GSJTOP.refreshWindow(); }); container.append(checkbox, hideFiltersCheckbox); $('#ui_topbar').append(container); }; // Funkcja pobierająca dane o pracy GSJTOP.getJobData = function(job, isSilver) { try { var jobData = JobList.getJobById(job.jobId); if (!jobData) return null; var jobModelData = JobsModel.Jobs.find(j => j.id === parseInt(job.jobId)); if (!jobModelData) return null; var xp = jobModelData.basis.short.experience; var money = jobModelData.basis.short.money; var motivation = Math.round(jobModelData.jobmotivation * 100); if (isSilver) { xp = Math.ceil(xp * 1.5); money = Math.ceil(money * 1.5); } else if (job.gold) { xp = Math.ceil(xp * 2); money = Math.ceil(money * 2); } return { experience: xp, money: money, motivation: motivation, distance: job.distance }; } catch (e) { return { experience: 0, money: 0, motivation: 0, distance: job.distance }; } }; // Funkcja zamykająca okno GSJTOP.closeWindow = function() { if (this.refreshInterval) { clearTimeout(this.refreshInterval); this.refreshInterval = null; } $('#goldJobs-bar').remove(); }; // Funkcja do zarządzania automatycznym odświeżaniem GSJTOP.startAutoRefresh = function() { if (this.refreshInterval) { clearTimeout(this.refreshInterval); } const getRandomDelay = () => Math.floor(Math.random() * (20000 - 7000 + 1) + 7000); const scheduleNextRefresh = () => { if (!this.preferences.enabled) return; this.refreshWindow(); const nextDelay = getRandomDelay(); this.refreshInterval = setTimeout(scheduleNextRefresh, nextDelay); }; scheduleNextRefresh(); }; // Funkcja obliczająca odległość GSJTOP.calculateDistance = function (jobX, jobY) { if (!Character || !Character.position) return 0; var to = { x: parseInt(jobX), y: parseInt(jobY) }; return GameMap.calcWayTime(Character.position, to); }; GSJTOP.parseWholeMap = function (tiles, onLoad) { this.loaded = 0; var x, y; var arr = []; var currentBlock = 0; var currentBlockLength = 0; for (x in tiles) { for (y in tiles[x]) { if (isNaN(x) || isNaN(y)) { continue; } if (currentBlockLength === 0) { arr[currentBlock] = []; } arr[currentBlock].push([parseInt(x), parseInt(y)]); if (++currentBlockLength == this.blockMaxLength) { currentBlock++; currentBlockLength = 0; } } } var i, to = arr.length; this.toLoad = to; for (i = 0; i < to; i++) { GameMap.Data.Loader.load(arr[i], function () { GSJTOP.loaded++; if (GSJTOP.loaded == GSJTOP.toLoad) { onLoad(); } }); } }; GSJTOP.createVisibilityWindow = function() { // Sprawdź, czy okno już istnieje, aby uniknąć duplikatów if ($('#job-visibility-window').length > 0) { // Opcjonalnie: przenieś istniejące okno na wierzch $('#job-visibility-window').css('z-index', 10002); return; } var hiddenJobs = JSON.parse(localStorage.getItem('GSJTOP_hiddenJobs') || '{}'); var data = GSJTOP.getFilteredData(true, true); var goldJobs = data.filter(job => job.gold); var silverJobs = data.filter(job => job.silver); // --- POCZĄTEK EDYCJI: Odczyt zapisanej pozycji --- var storageKey = 'GSJTOP_job-visibility-window_Pos'; var savedPosition = null; try { var savedPosString = localStorage.getItem(storageKey); if (savedPosString) { savedPosition = JSON.parse(savedPosString); } } catch (err) { console.error("GSJTOP: Nie udało się odczytać pozycji okna widoczności z localStorage.", err); localStorage.removeItem(storageKey); // Usuń uszkodzone dane } // --- KONIEC EDYCJI: Odczyt zapisanej pozycji --- var panelCss = { 'position': 'fixed', // Nowe pozycjonowanie - wyśrodkowane poziomo, dolna krawędź 20px nad środkiem ekranu 'top': 'calc(50% - 270px)', 'left': '50%', 'transform': 'translateX(-50%)', // ... (reszta stylów: background, padding, border, etc.) ... 'background': 'linear-gradient(to bottom, #4d351b 0%, #2a1608 100%)', 'padding': '15px', 'border-radius': '5px', 'border': '2px solid #1b0a00', 'border-top-color': '#150800', 'border-left-color': '#150800', 'box-shadow': '0 4px 15px rgba(0, 0, 0, 0.7), inset 0 0 5px rgba(0,0,0,0.2)', 'z-index': 10002, 'height': 'auto', 'min-height': '340px', 'max-height': '80vh', 'overflow': 'hidden', 'min-width': '440px', 'width': '490px', 'cursor': 'move' }; // --- POCZĄTEK EDYCJI: Zastosowanie zapisanej pozycji --- if (savedPosition && savedPosition.top && savedPosition.left) { panelCss.top = savedPosition.top; panelCss.left = savedPosition.left; panelCss.transform = 'none'; // Usuwamy transformację, bo mamy pozycję w px } // --- KONIEC EDYCJI: Zastosowanie zapisanej pozycji --- var visibilityPanel = $('<div/>', { id: 'job-visibility-window', css: panelCss // Używamy przygotowanego obiektu CSS }); var title = $('<div/>', { text: GSJTOPlang.visibilityTitle, css: { 'font-weight': 'bold', 'margin-bottom': '10px', 'text-align': 'center', 'padding-bottom': '5px', 'border-bottom': '1px solid rgba(255, 255, 255, 0.2)', // Jaśniejsza linia na ciemnym tle 'color': '#fdf7e5', // Jaśniejszy kolor tekstu tytułu 'cursor': 'default' // Przywracamy domyślny kursor dla tytułu } }); // Dodajemy pole wyszukiwania var searchContainer = $('<div/>', { css: { 'margin-bottom': '10px', 'display': 'flex', 'align-items': 'center', 'width': '100%', 'cursor': 'default' } }); var searchInput = $('<input/>', { type: 'text', placeholder: GSJTOPlang.searchPlaceholder, css: { 'flex': '1', 'padding': '5px', 'border-radius': '3px', 'border': '1px solid rgba(0, 0, 0, 0.2)', 'margin-right': '5px', 'cursor': 'text' } }).on('input', function(e) { e.stopPropagation(); var searchText = $(this).val().toLowerCase(); filterJobs(searchText); }).on('mousedown', function(e) { e.stopPropagation(); // Zapobiegamy rozpoczęciu przeciągania przy kliknięciu w pole wyszukiwania }); var clearButton = $('<button/>', { text: GSJTOPlang.clearSearch, css: { 'padding': '5px 8px', 'border-radius': '3px', 'border': '1px solid rgba(0, 0, 0, 0.2)', 'background-color': '#f0f0f0', 'cursor': 'pointer' } }).on('click', function(e) { e.stopPropagation(); searchInput.val('').trigger('input'); }).on('mousedown', function(e) { e.stopPropagation(); }); searchContainer.append(searchInput, clearButton); var toggleButton = $('<button/>', { text: GSJTOPlang.unselectAll, css: { 'margin-bottom': '10px', 'padding': '5px 10px', 'border-radius': '3px', 'border': '1px solid rgba(0, 0, 0, 0.2)', 'background-color': '#f0f0f0', 'cursor': 'pointer', // Kursor 'pointer' dla przycisku 'width': '100%', 'transition': 'opacity 0.3s ease' } }).on('click', function(e) { // Dodajemy e (event) e.stopPropagation(); // Zapobiegamy propagacji kliknięcia do visibilityPanel var button = $(this); var checkboxes = checkboxContainer.find('input[type="checkbox"]'); var allChecked = checkboxes.length === checkboxes.filter(':checked').length; button.prop('disabled', true).css('opacity', '0.5'); function processCheckboxes(startIndex) { var batchSize = 1; var endIndex = Math.min(startIndex + batchSize, checkboxes.length); for(var i = startIndex; i < endIndex; i++) { $(checkboxes[i]).prop('checked', !allChecked).trigger('change'); } if(endIndex < checkboxes.length) { setTimeout(() => processCheckboxes(endIndex), 200); } else { setTimeout(() => { button.prop('disabled', false).css('opacity', '1'); button.text(allChecked ? GSJTOPlang.selectAll : GSJTOPlang.unselectAll); }, 300); } } processCheckboxes(0); }); var checkboxContainer = $('<div/>', { id: 'gsjtop-visibility-container-' + Date.now(), // Unikalne ID dla selektorów CSS css: { 'display': 'flex', 'flex-direction': 'column', 'gap': '5px', 'margin-bottom': '10px', 'height': '223px', // Zmiana z max-height na height 'overflow-y': 'auto', 'padding-right': '10px', 'scrollbar-width': 'thin', 'scrollbar-color': '#888 #f0f0f0', 'width': '480px', 'background-color': '#ffffff', 'border-radius': '3px', 'border': '1px solid #b89b6d', 'cursor': 'default' } }); var jobVisibilityContainer = $('<div/>', { css: { 'display': 'flex', 'flex-wrap': 'wrap', 'gap': '5px', 'justify-content': 'center', 'margin-top': '-20px', 'background-color': 'rgba(255, 255, 255, 0.7)', 'padding': '5px', 'border-radius': '3px', 'position': 'relative', 'z-index': '10001', 'cursor': 'default' // Domyślny kursor } }); function createJobWrapper(job, jobData) { var wrapper = $('<div/>', { css: { 'display': 'grid', 'grid-template-columns': '30px 220px 50px 50px 80px', 'align-items': 'center', 'gap': '10px', 'padding': '5px', 'border-bottom': '1px solid rgba(0,0,0,0.1)', 'width': '430px', 'cursor': 'default' // Domyślny kursor } }); var checkbox = $('<input/>', { type: 'checkbox', id: 'job-visibility-' + job.jobId, checked: !hiddenJobs[job.jobId], css: { 'cursor': 'pointer' } // Kursor pointer dla checkboxa }).on('change', function(e) { // Dodajemy e (event) e.stopPropagation(); // Zapobiegamy propagacji hiddenJobs[job.jobId] = !this.checked; localStorage.setItem('GSJTOP_hiddenJobs', JSON.stringify(hiddenJobs)); // Aktualizuj stan ukrycia pracy w danych data.forEach(function(dataJob) { if (dataJob.jobId == job.jobId) { dataJob.hidden = !this.checked; } }); // Odśwież mapę GSJTOP.refreshMap() GSJTOP.refreshWindow(); }).on('mousedown', function(e) { // Dodajemy mousedown e.stopPropagation(); // Zapobiegamy rozpoczęciu przeciągania przy kliknięciu checkboxa }); var label = $('<label/>', { for: 'job-visibility-' + job.jobId, text: job.shortname, css: { 'font-size': '12px', 'white-space': 'nowrap', 'overflow': 'hidden', 'text-overflow': 'ellipsis', 'cursor': 'pointer' // Kursor pointer dla etykiety } }).on('mousedown', function(e) { // Dodajemy mousedown e.stopPropagation(); // Zapobiegamy rozpoczęciu przeciągania przy kliknięciu etykiety }); var xpDiv = $('<div/>', { html: '<img src="images/window/job/bigicon_xp.png" style="width: 14px; height: 14px; vertical-align: middle;"> ' + '<span style="margin-left: 2px;">' + jobData.experience + '</span>', css: { 'white-space': 'nowrap', 'text-align': 'left', 'font-size': '11px', 'cursor': 'default' // Domyślny kursor } }); var moneyDiv = $('<div/>', { html: '<img src="images/window/job/bigicon_money.png" style="width: 14px; height: 14px; vertical-align: middle;"> ' + '<span style="margin-left: 2px;">' + jobData.money + '</span>', css: { 'white-space': 'nowrap', 'text-align': 'left', 'font-size': '11px', 'cursor': 'default' // Domyślny kursor } }); var timeDiv = $('<div/>', { text: GSJTOP.formatTime(job.distance), css: { 'white-space': 'nowrap', 'text-align': 'left', 'font-size': '11px', 'cursor': 'default' // Domyślny kursor } }); wrapper.append(checkbox, label, xpDiv, moneyDiv, timeDiv); return wrapper; } function createSortButton(text, sortFn, jobsType) { return $('<button/>', { text: text, css: { 'padding': '2px 5px', 'border': '1px solid rgba(0, 0, 0, 0.2)', 'border-radius': '3px', 'background': '#f0f0f0', 'cursor': 'pointer', // Kursor pointer 'font-size': '11px', 'min-width': '25px', 'transition': 'background-color 0.3s ease' } }).on('click', function(e) { // Dodajemy e (event) e.stopPropagation(); // Zapobiegamy propagacji var $button = $(this); if($button.prop('disabled')) return; $button.prop('disabled', true); var isAscending = !$button.data('ascending'); $button.data('ascending', isAscending); $button.siblings('button').removeData('ascending'); $button.siblings('button').css('background', '#f0f0f0'); $button.css('background', isAscending ? '#e0ffe0' : '#ffe0e0'); var jobs = jobsType === 'goldJobs' ? goldJobs : silverJobs; setTimeout(() => { jobs.sort((a, b) => { var valueA = sortFn(a); var valueB = sortFn(b); return isAscending ? valueA - valueB : valueB - valueA; }); refreshJobList(jobs, jobsType); setTimeout(() => { $button.prop('disabled', false); }, 200); }, 100); }).on('mousedown', function(e) { // Dodajemy mousedown e.stopPropagation(); // Zapobiegamy rozpoczęciu przeciągania przy kliknięciu przycisku sortowania }); } // Funkcja do filtrowania prac na podstawie wyszukiwanego tekstu // Funkcja do filtrowania prac na podstawie wyszukiwanego tekstu // Funkcja do filtrowania prac na podstawie wyszukiwanego tekstu // Funkcja do filtrowania prac na podstawie wyszukiwanego tekstu function filterJobs(searchText) { console.log("Filtrowanie prac, tekst:", searchText); if (!searchText) { // Jeśli pole wyszukiwania jest puste, pokaż wszystkie prace checkboxContainer.find('.silverJobs > div, .goldJobs > div').show(); // Przywróć oryginalny stan ukrytych prac na podstawie checkboxów checkboxContainer.find('input[type="checkbox"]').each(function() { var jobId = $(this).attr('id').replace('job-visibility-', ''); var isChecked = $(this).prop('checked'); data.forEach(function(job) { if (job.jobId == jobId) { job.hidden = !isChecked; } }); }); console.log("Pole wyszukiwania puste, liczba widocznych prac:", data.filter(function(job) { return !job.hidden; }).length); // Odśwież mapę z aktualnymi danymi GSJTOP.refreshMap(data); return; } searchText = searchText.toLowerCase(); // Tymczasowo ukryj wszystkie prace na mapie data.forEach(function(job) { // Domyślnie ukryj wszystkie prace job.hidden = true; }); // Filtruj prace srebrne checkboxContainer.find('.silverJobs > div').each(function() { var jobElement = $(this); var jobName = jobElement.find('label').text().toLowerCase(); var jobId = jobElement.find('input[type="checkbox"]').attr('id').replace('job-visibility-', ''); if (jobName.includes(searchText)) { jobElement.show(); // Pokaż pracę na mapie tylko jeśli checkbox jest zaznaczony var isChecked = jobElement.find('input[type="checkbox"]').prop('checked'); if (isChecked) { // Znajdź odpowiadającą pracę w danych data.forEach(function(job) { if (job.jobId == jobId) { job.hidden = false; } }); } } else { jobElement.hide(); } }); // Filtruj prace złote checkboxContainer.find('.goldJobs > div').each(function() { var jobElement = $(this); var jobName = jobElement.find('label').text().toLowerCase(); var jobId = jobElement.find('input[type="checkbox"]').attr('id').replace('job-visibility-', ''); if (jobName.includes(searchText)) { jobElement.show(); // Pokaż pracę na mapie tylko jeśli checkbox jest zaznaczony var isChecked = jobElement.find('input[type="checkbox"]').prop('checked'); if (isChecked) { // Znajdź odpowiadającą pracę w danych data.forEach(function(job) { if (job.jobId == jobId) { job.hidden = false; } }); } } else { jobElement.hide(); } }); console.log("Po filtrowaniu, liczba widocznych prac:", data.filter(function(job) { return !job.hidden; }).length); // Odśwież mapę z aktualnymi danymi GSJTOP.refreshMap(data); } // Dodaj funkcję refreshMap do obiektu GSJTOP GSJTOP.refreshMap = function(customData) { // Sprawdź czy okno mapy istnieje var mapWindow = $('#custom-map-window'); if (mapWindow.length === 0) return; // Użyj przekazanych danych lub pobierz domyślne var data = customData || this.getFilteredData(this.preferences.showSilver, this.preferences.showGold); // Debugowanie console.log("Odświeżanie mapy, liczba prac:", data.length); console.log("Liczba widocznych prac:", data.filter(function(job) { return !job.hidden; }).length); // Znajdź kontener mapy var mapContent = mapWindow.find('div').first(); // WAŻNE: Usuń tylko markery prac, a nie całą zawartość mapy mapContent.find('div').remove(); // Usuń tylko divy (markery), a nie obrazy (kafelki mapy) // Ustawienia mapy i markerów var maxX = 46592; var maxY = 20480; var mapWindowWidth = 770; var mapWindowHeight = 338; var markerSize = 24; var markerOffset = markerSize / 2; // Zastosuj filtry do danych var filteredData = data.filter(function(job) { // Filtruj prace na podstawie preferencji if (this.preferences.hideHighIdJobs && job.jobId > 150) return false; if (this.preferences.hideLowIdJobs && job.jobId < 100) return false; // Sprawdź czy praca jest ukryta przez użytkownika if (job.hidden) return false; return true; }.bind(this)); console.log("Po zastosowaniu filtrów, liczba widocznych prac:", filteredData.length); // Dodaj markery prac na mapie filteredData.forEach(function(job) { var markerLeft = (job.x / maxX * mapWindowWidth) - markerOffset; var markerTop = (job.y / maxY * mapWindowHeight) - markerOffset; // Utwórz element markera (div) var marker = $('<div/>', { title: job.name, css: { 'position': 'absolute', 'left': markerLeft + 'px', 'top': markerTop + 'px', 'width': markerSize + 'px', 'height': markerSize + 'px', 'cursor': 'pointer', 'z-index': 10004, 'border-radius': '50%', } }).append($('<img/>', { src: 'https://westpl.innogamescdn.com/images/jobs/' + job.shortname + '.png', css: { 'width': '100%', 'height': '100%' } })); // Dodaj obramowanie/cień w zależności od typu pracy if (job.gold) { marker.css('box-shadow', '0 0 5px 2px gold'); } else if (job.silver) { marker.css('box-shadow', '0 0 5px 2px silver'); } else { marker.css('box-shadow', '0 0 3px rgba(0,0,0,0.5)'); } // Dodaj akcję kliknięcia marker.click(function(e) { e.stopPropagation(); if (typeof GameMap !== 'undefined' && GameMap.center) { GameMap.center(job.x, job.y); } // Zamknij okno mapy mapWindow.remove(); // Zamknij okno widoczności, jeśli istnieje $('#job-visibility-window').remove(); // Opcjonalnie: Zaktualizuj wygląd przycisku przełączającego widoczność var visibilityButton = $('#gsjtop-visibility-toggle'); if (visibilityButton.length > 0) { visibilityButton.css('background-color', '#4a90e2'); } }); // Dodaj marker do kontenera mapy mapContent.append(marker); }); }; function refreshJobList(jobs, jobsType) { var container = checkboxContainer.find('.' + jobsType); container.empty(); const translations = {}; JobList.getSortedJobs().forEach(job => { translations[job.id] = job.name; }); // Definiujemy kolor naprzemienny w zależności od typu pracy let alternatingColor; if (jobsType === 'silverJobs') { alternatingColor = '#D3D3D3'; // Jasny srebrny (LightGray) } else { // Zakładamy, że to 'goldJobs' alternatingColor = '#FAFAD2'; // Jasny złoty (LightGoldenrodYellow) } jobs.forEach(function(job, index) { // Dodajemy 'index' do pętli var jobData = GSJTOP.getJobData(job, jobsType === 'silverJobs'); if (jobData) { // Ustawiamy kolor tła na podstawie indeksu i typu pracy var backgroundColor = index % 2 === 0 ? alternatingColor : '#ffffff'; // Kolor naprzemienny lub biały var wrapper = $('<div/>', { css: { 'display': 'grid', 'grid-template-columns': '30px 180px 80px 80px 80px', 'align-items': 'center', 'gap': '5px', 'padding': '5px', 'border-bottom': '1px solid rgba(0,0,0,0.1)', 'width': '460px', 'background-color': backgroundColor, // Ustawiamy odpowiedni kolor tła 'cursor': 'default' // Domyślny kursor dla wiersza } }); var checkbox = $('<input/>', { type: 'checkbox', id: 'job-visibility-' + job.jobId, checked: !hiddenJobs[job.jobId], css: { 'cursor': 'pointer' } }).on('change', function(e) { e.stopPropagation(); var isChecked = this.checked; var jobId = $(this).attr('id').replace('job-visibility-', ''); // Debugowanie console.log("Zmiana stanu checkboxa dla pracy:", jobId, "isChecked:", isChecked); hiddenJobs[jobId] = !isChecked; localStorage.setItem('GSJTOP_hiddenJobs', JSON.stringify(hiddenJobs)); // Aktualizuj stan ukrycia pracy w danych data.forEach(function(dataJob) { if (dataJob.jobId == jobId) { dataJob.hidden = !isChecked; } }); // Odśwież mapę z aktualnymi danymi GSJTOP.refreshMap(data); GSJTOP.refreshWindow(); }).on('mousedown', function(e) { e.stopPropagation(); }); var jobName = translations[job.jobId] || job.shortname; var label = $('<label/>', { for: 'job-visibility-' + job.jobId, text: jobName, css: { 'font-size': '11px', 'white-space': 'nowrap', 'overflow': 'hidden', 'text-overflow': 'ellipsis', 'cursor': 'pointer' // Kursor pointer dla etykiety } }).on('mousedown', function(e) { // Dodajemy mousedown e.stopPropagation(); // Zapobiegamy rozpoczęciu przeciągania przy kliknięciu etykiety }); var xpDiv = $('<div/>', { html: '<img src="images/window/job/bigicon_xp.png" style="width: 14px; height: 14px; vertical-align: middle;"> ' + '<span style="margin-left: 2px;">' + jobData.experience + '</span>', css: { 'white-space': 'nowrap', 'text-align': 'left', 'font-size': '11px', 'cursor': 'default' // Domyślny kursor } }); var moneyDiv = $('<div/>', { html: '<img src="images/window/job/bigicon_money.png" style="width: 14px; height: 14px; vertical-align: middle;"> ' + '<span style="margin-left: 2px;">' + jobData.money + '</span>', css: { 'white-space': 'nowrap', 'text-align': 'left', 'font-size': '11px', 'cursor': 'default' // Domyślny kursor } }); var timeDiv = $('<div/>', { text: GSJTOP.formatTime(job.distance), css: { 'white-space': 'nowrap', 'text-align': 'left', 'font-size': '11px', 'cursor': 'default' // Domyślny kursor } }); wrapper.append(checkbox, label, xpDiv, moneyDiv, timeDiv); container.append(wrapper); } }); } var silverHeader = $('<div/>', { css: { 'display': 'flex', 'justify-content': 'space-between', 'align-items': 'center', 'font-weight': 'bold', 'margin': '0px 0 5px 0', 'padding': '5px', 'background-color': 'rgba(112, 128, 144, 0.1)', 'border-radius': '3px', 'grid-column': '1 / -1', 'cursor': 'default' // Domyślny kursor } }).append( $('<span/>').text(GSJTOPlang.silverJobsTitle), $('<div/>', { css: { 'display': 'flex', 'gap': '5px', 'cursor': 'default' // Domyślny kursor } }).append( createSortButton('XP', job => GSJTOP.getJobData(job, true).experience, 'silverJobs'), createSortButton('$', job => GSJTOP.getJobData(job, true).money, 'silverJobs'), createSortButton('↔', job => job.distance, 'silverJobs') ) ); var goldHeader = $('<div/>', { css: { 'display': 'flex', 'justify-content': 'space-between', 'align-items': 'center', 'font-weight': 'bold', 'margin': '10px 0 5px 0', 'padding': '5px', 'background-color': 'rgba(171, 153, 48, 0.1)', 'border-radius': '3px', 'grid-column': '1 / -1', 'cursor': 'default' // Domyślny kursor } }).append( $('<span/>').text(GSJTOPlang.goldJobsTitle), $('<div/>', { css: { 'display': 'flex', 'gap': '5px', 'cursor': 'default' // Domyślny kursor } }).append( createSortButton('XP', job => GSJTOP.getJobData(job, false).experience, 'goldJobs'), createSortButton('$', job => GSJTOP.getJobData(job, false).money, 'goldJobs'), createSortButton('↔', job => job.distance, 'goldJobs') ) ); var silverContainer = $('<div/>', { class: 'silverJobs', css: {'cursor': 'default'} }); // Domyślny kursor var goldContainer = $('<div/>', { class: 'goldJobs', css: {'cursor': 'default'} }); // Domyślny kursor checkboxContainer.append(silverHeader, silverContainer, goldHeader, goldContainer); refreshJobList(silverJobs, 'silverJobs'); refreshJobList(goldJobs, 'goldJobs'); // Dla panelu widoczności pokazujemy wszystkie prace data.forEach(function(job) { var jobData = GSJTOP.getJobData(job, job.silver); if (jobData) { var checkbox = $('<input/>', { type: 'checkbox', id: 'job-visibility-' + job.jobId, checked: !job.hidden, css: { 'margin': '2px', 'cursor': 'pointer' // Kursor pointer } }).on('change', function(e) { // Dodajemy e (event) e.stopPropagation(); // Zapobiegamy propagacji var hiddenJobs = JSON.parse(localStorage.getItem('GSJTOP_hiddenJobs') || '{}'); hiddenJobs[job.jobId] = !this.checked; localStorage.setItem('GSJTOP_hiddenJobs', JSON.stringify(hiddenJobs)); GSJTOP.refreshWindow(); }).on('mousedown', function(e) { // Dodajemy mousedown e.stopPropagation(); // Zapobiegamy rozpoczęciu przeciągania }); var label = $('<label/>', { for: 'job-visibility-' + job.jobId, text: job.shortname, css: { 'font-size': '11px', 'margin-right': '5px', 'cursor': 'pointer' // Kursor pointer } }).on('mousedown', function(e) { // Dodajemy mousedown e.stopPropagation(); // Zapobiegamy rozpoczęciu przeciągania }); // Usunęliśmy dodawanie do jobVisibilityContainer, bo jest już obsłużone w refreshJobList } }); visibilityPanel.append(title, searchContainer, toggleButton, checkboxContainer); $('body').append(visibilityPanel); // Uczynienie okna widoczności przesuwalnym za pomocą całego okna (visibilityPanel) GSJTOP.makeDraggable(visibilityPanel, visibilityPanel); } GSJTOP.refreshWindow = function () { }; // Nowa funkcja do obsługi przeciągania elementów GSJTOP.makeDraggable = function(panelElement, handleElement) { handleElement.css('cursor', 'move'); // Ustawienie kursora 'move' na uchwycie handleElement.on('mousedown', function(e) { // Ignoruj kliknięcia innymi przyciskami niż lewy if (e.button !== 0) return; e.preventDefault(); // Zapobiegaj zaznaczaniu tekstu podczas przeciągania var initialMouseX = e.pageX; var initialMouseY = e.pageY; // Używamy offset() do pobrania pozycji względem dokumentu var initialPanelPos = panelElement.offset(); var initialPanelLeft = initialPanelPos.left; var initialPanelTop = initialPanelPos.top; // Obliczamy przesunięcie kursora względem lewego górnego rogu panelu var offsetX = initialMouseX - initialPanelLeft; var offsetY = initialMouseY - initialPanelTop; var originalZIndex = panelElement.css('z-index'); // Zwiększamy z-index, usuwamy transformację i ustawiamy pozycję w pikselach panelElement.css({ 'z-index': 11000, // Wyższy z-index na czas przeciągania 'transform': 'none', // Usuwamy transformację, aby pozycjonować przez top/left 'top': initialPanelTop + 'px', 'left': initialPanelLeft + 'px' }); // Upewniamy się, że pozycje top/left nie są procentowe (choć offset() powinien dać px) // Ten krok może być nadmiarowy, ale nie zaszkodzi if (panelElement.css('top').includes('%')) { panelElement.css('top', initialPanelTop + 'px'); } if (panelElement.css('left').includes('%')) { panelElement.css('left', initialPanelLeft + 'px'); } // Nasłuchujemy na ruch myszy i puszczenie przycisku na całym dokumencie $(document).on('mousemove.gsjtopdrag', function(moveEvent) { var newLeft = moveEvent.pageX - offsetX; var newTop = moveEvent.pageY - offsetY; // Aktualizujemy pozycję panelu panelElement.css({ left: newLeft + 'px', top: newTop + 'px' }); }); $(document).on('mouseup.gsjtopdrag', function(upEvent) { // Ignoruj puszczenie innego przycisku niż lewy if (upEvent.button !== 0) return; // Usuwamy nasłuchiwanie zdarzeń po puszczeniu przycisku $(document).off('mousemove.gsjtopdrag mouseup.gsjtopdrag'); // Przywracamy oryginalny z-index panelElement.css('z-index', originalZIndex); // --- POCZĄTEK EDYCJI: Zapisywanie pozycji --- try { var finalPos = { top: panelElement.css('top'), // Pobieramy aktualną pozycję top left: panelElement.css('left') // Pobieramy aktualną pozycję left }; // Tworzymy klucz do localStorage na podstawie ID panelu var storageKey = 'GSJTOP_' + panelElement.attr('id') + '_Pos'; // Zapisujemy pozycję jako string JSON localStorage.setItem(storageKey, JSON.stringify(finalPos)); } catch (err) { // Logujemy błąd, jeśli zapis się nie powiedzie console.error("GSJTOP: Nie udało się zapisać pozycji okna w localStorage.", err); } // --- KONIEC EDYCJI: Zapisywanie pozycji --- }); }); }; GSJTOP.getJobIcon = function (jobId, x, y, shortname, gold, jobData) { var t = gold ? 'gold' : 'silver'; var tooltipHtml = this.preferences.showTooltips ? '<div class="job-info" style="' + 'position: absolute; ' + 'top: 65px; ' + 'width: 63px; ' + 'background: #FDF7E5; ' + 'border: 1px solid #B89B6D; ' + 'padding: 0; ' + 'box-shadow: 2px 2px 3px rgba(0,0,0,0.2); ' + 'z-index: 10003;">' + // XP z tooltipem - powiększone cyfry '<div style="display: flex; justify-content: space-between; align-items: center; height: 18px; padding: 0 3px; background: #F5ECD4;" title="' + GSJTOPlang.tooltipExperience + '">' + '<div style="width: 14px; height: 14px; background: url(' + (GSJTOP.cdnUrl ? GSJTOP.cdnUrl + '/images/window/job/bigicon_xp.png' : 'https://westpl.innogamescdn.com/images/window/job/bigicon_xp.png') + ') no-repeat center; background-size: contain;"></div>' + '<div style="color: #5C4219; font-size: 13px; font-weight: bold; text-align: right; min-width: 35px;">' + jobData.experience + '</div>' + '</div>' + // Pieniądze z tooltipem - powiększone cyfry '<div style="display: flex; justify-content: space-between; align-items: center; height: 18px; padding: 0 3px;" title="' + GSJTOPlang.tooltipMoney + '">' + '<div style="width: 14px; height: 14px; background: url(' + (GSJTOP.cdnUrl ? GSJTOP.cdnUrl + '/images/window/job/bigicon_money.png' : 'https://westpl.innogamescdn.com/images/window/job/bigicon_money.png') + ') no-repeat center; background-size: contain;"></div>' + '<div style="color: #5C4219; font-size: 13px; font-weight: bold; text-align: right; min-width: 35px;">' + jobData.money + '</div>' + '</div>' + // Motywacja z tooltipem - powiększone cyfry '<div style="display: flex; justify-content: space-between; align-items: center; height: 18px; padding: 0 3px; background: #F5ECD4;" title="' + GSJTOPlang.tooltipMotivation + '">' + '<div style="color: #5C4219; font-size: 13px; font-weight: bold; width: 100%; text-align: center;">' + jobData.motivation + '%</div>' + '</div>' + // Czas z tooltipem - powiększone cyfry '<div style="display: flex; justify-content: space-between; align-items: center; height: 18px; padding: 0 3px;" title="' + GSJTOPlang.tooltipTime + '">' + '<div style="color: #5C4219; font-size: 13px; font-weight: bold; width: 100%; text-align: center;">' + GSJTOP.formatTime(jobData.distance) + '</div>' + '</div>' + '</div>' : ''; return '<div class="job-wrapper" style="position: relative; display: inline-flex; flex-direction: column; align-items: center; margin: -1px; width: 65px; height: 65px; z-index: 10000;">' + '<div class="job" style="position: relative; width: 65px; height: 65px;">' + '<img src="images/jobs/' + shortname + '.png" class="job_icon" style="width: 65px; height: 65px; position: relative; z-index: 10000;">' + '<div onclick="javascript:GameMap.JobHandler.openJob(' + jobId + ',{x:' + x + ',y:' + y + '})" ' + 'class="featured ' + t + '" style="position: absolute; top: -5px; left: -5px; z-index: 10001; width: 75px; height: 75px; background-size: contain;"></div>' + this.getGotoIcon(x, y) + '</div>' + tooltipHtml + '</div>'; }; GSJTOP.formatTime = function(seconds) { seconds = Math.round(seconds); var hours = Math.floor(seconds / 3600); var minutes = Math.floor((seconds % 3600) / 60); var secs = seconds % 60; return String(hours).padStart(2, '0') + ':' + String(minutes).padStart(2, '0') + ':' + String(secs).padStart(2, '0'); }; GSJTOP.getGotoIcon = function (x, y) { return '<div class="centermap" onclick="javascript:GameMap.center(' + x + ',' + y + ');" ' + 'style="position: absolute; background-image: url(\'images/map/icons/instantwork.png\'); ' + 'width: 25px; height: 25px; top: -2px; right: -2px; cursor: pointer; z-index: 10002; background-size: contain;"></div>'; }; GSJTOP.createNavigationButtons = function() { var wrapperStyle = { 'width': '30px', 'height': '60px', 'display': 'flex', 'justify-content': 'center', 'align-items': 'center', 'cursor': 'pointer', 'margin-top': '35px', 'z-index': 10001 }; var buttonStyle = { 'width': '25px', 'height': '44px', 'background-image': 'url(\'https://westit.innogamescdn.com/images/window/trader/arrows.png\')', 'color': 'white', 'padding': '0px', 'display': 'block', 'background-size': 'cover', 'text-align': 'center' }; var prevWrapper = $('<div/>', { css: Object.assign({}, wrapperStyle, { 'margin-right': '0px', 'position': 'relative', 'top': '-15px' }) }); var nextWrapper = $('<div/>', { css: Object.assign({}, wrapperStyle, { 'margin-left': '0px', 'position': 'relative', 'top': '-15px' }) }); var prevButton = $('<div/>', { css: Object.assign({}, buttonStyle, { 'background-position': 'top left' }) }); var nextButton = $('<div/>', { css: Object.assign({}, buttonStyle, { 'background-position': 'top right' }) }); // Pobierz tylko widoczne prace var visibleJobs = this.getFilteredData(this.preferences.showSilver, this.preferences.showGold) .filter(job => !job.hidden); var totalJobs = visibleJobs.length; var maxPages = Math.ceil(totalJobs / this.preferences.jobsPerPage); // Aktualizacja stylów przycisków w zależności od dostępności stron var updateButtonStyles = function() { if (GSJTOP.preferences.currentPage <= 0) { prevWrapper.css('opacity', '0.5').css('cursor', 'default'); } else { prevWrapper.css('opacity', '1').css('cursor', 'pointer'); } if (GSJTOP.preferences.currentPage >= maxPages - 1 || maxPages <= 1) { nextWrapper.css('opacity', '0.5').css('cursor', 'default'); } else { nextWrapper.css('opacity', '1').css('cursor', 'pointer'); } }; updateButtonStyles(); prevWrapper.append(prevButton).click(function() { if (GSJTOP.preferences.currentPage > 0) { GSJTOP.preferences.currentPage--; GSJTOP.refreshWindow(); } }); nextWrapper.append(nextButton).click(function() { // Pobierz aktualną liczbę widocznych prac var visibleJobs = GSJTOP.getFilteredData(GSJTOP.preferences.showSilver, GSJTOP.preferences.showGold) .filter(job => !job.hidden); var totalJobs = visibleJobs.length; var maxPages = Math.ceil(totalJobs / GSJTOP.preferences.jobsPerPage); if (GSJTOP.preferences.currentPage < maxPages - 1) { GSJTOP.preferences.currentPage++; GSJTOP.refreshWindow(); } }); return { prev: prevWrapper, next: nextWrapper }; }; GSJTOP.createJobsContainer = function (showSilver, showGold) { var hideHighIdJobsFilter = $('<div/>', { title: this.preferences.hideHighIdJobs ? GSJTOPlang.showHighIdJobs : GSJTOPlang.hideHighIdJobs, css: { 'width': '24px', 'height': '24px', 'cursor': 'pointer', 'margin-right': '1px', 'background-color': this.preferences.hideHighIdJobs ? '#cccccc' : '#FFD700', 'border-radius': '3px', 'border': '1px solid rgba(0, 0, 0, 0.2)', 'transition': 'all 0.3s ease', 'box-shadow': '0 1px 3px rgba(0, 0, 0, 0.1)' } }).on('mouseenter', function() { $(this).css({ 'transform': 'scale(1.1)', 'box-shadow': '0 2px 5px rgba(0, 0, 0, 0.2)' }); }).on('mouseleave', function() { $(this).css({ 'transform': 'scale(1)', 'box-shadow': '0 1px 3px rgba(0, 0, 0, 0.1)' }); }).click(function() { var isCurrentlyActive = $(this).css('background-color') === 'rgb(204, 204, 204)'; GSJTOP.preferences.hideHighIdJobs = !isCurrentlyActive; localStorage.setItem('GSJTOP_hideHighIdJobs', GSJTOP.preferences.hideHighIdJobs); $(this).css({ 'background-color': isCurrentlyActive ? '#FFD700' : '#cccccc' }).attr('title', isCurrentlyActive ? GSJTOPlang.hideHighIdJobs : GSJTOPlang.showHighIdJobs); GSJTOP.preferences.currentPage = 0; // Odśwież mapę GSJTOP.refreshMap(); GSJTOP.refreshWindow(); }) var lowIdJobsFilter = $('<div/>', { title: this.preferences.hideLowIdJobs ? GSJTOPlang.showLowIdJobs : GSJTOPlang.hideLowIdJobs, css: { 'width': '24px', 'height': '24px', 'cursor': 'pointer', 'margin-right': '1px', 'background-color': this.preferences.hideLowIdJobs ? '#cccccc' : '#ff4444', 'border-radius': '3px', 'border': '1px solid rgba(0, 0, 0, 0.2)', 'transition': 'all 0.3s ease', 'box-shadow': '0 1px 3px rgba(0, 0, 0, 0.1)' } }).on('mouseenter', function() { $(this).css({ 'transform': 'scale(1.1)', 'box-shadow': '0 2px 5px rgba(0, 0, 0, 0.2)' }); }).on('mouseleave', function() { $(this).css({ 'transform': 'scale(1)', 'box-shadow': '0 1px 3px rgba(0, 0, 0, 0.1)' }); }).click(function() { var isCurrentlyActive = $(this).css('background-color') === 'rgb(204, 204, 204)'; GSJTOP.preferences.hideLowIdJobs = !isCurrentlyActive; localStorage.setItem('GSJTOP_hideLowIdJobs', GSJTOP.preferences.hideLowIdJobs); $(this).css({ 'background-color': isCurrentlyActive ? '#ff4444' : '#cccccc' }).attr('title', isCurrentlyActive ? GSJTOPlang.hideLowIdJobs : GSJTOPlang.showLowIdJobs); GSJTOP.preferences.currentPage = 0; // Aktualizuj dane o pracach var data = GSJTOP.getFilteredData(GSJTOP.preferences.showSilver, GSJTOP.preferences.showGold); // Odśwież mapę GSJTOP.refreshMap(); GSJTOP.refreshWindow(); }) var container = $('<div/>', { class: 'jobs-container', css: { 'display': 'flex', 'flex-direction': 'column', 'justify-content': 'flex-start', 'align-items': 'center', 'gap': '10px', 'height': '120px', 'overflow': 'visible', 'background': 'rgba(255, 255, 255, 0.0)', 'padding': '0 15px', 'position': 'relative', 'width': 'auto', 'z-index': 10000, 'min-height': '120px' } }); var jobsWrapper = $('<div/>', { css: { 'display': 'flex', 'justify-content': 'center', 'align-items': 'center', 'gap': '10px', 'width': 'auto', 'margin-bottom': '5px', 'margin-top': '25px', 'min-height': '65px', 'flex-wrap': 'nowrap', 'position': 'relative' } }); var jobVisibilityContainer = $('<div/>', { css: { 'display': 'flex', 'flex-wrap': 'wrap', 'gap': '5px', 'justify-content': 'center', 'margin-top': '-20px', // Zmienione z 5px na -20px 'background-color': 'rgba(255, 255, 255, 0.7)', 'padding': '5px', 'border-radius': '3px', 'position': 'relative', // Dodane 'z-index': '10001' // Dodane } }); var filterContainer = $('<div/>', { id: 'gsjtop-filters-container', css: { // Przywrócone pozycjonowanie absolutne 'position': 'absolute', 'top': '85px', 'left': '50%', // Przykład: wycentrowanie poziome 'transform': 'translateX(-50%)', // Przykład: wycentrowanie poziome 'width': 'fit-content', // Aby szerokość dopasowała się do zawartości przy centrowaniu // Zachowany warunkowy margines górny dla ruchu góra-dół 'margin-top': this.preferences.showTooltips ? '87px' : '10px', // Nadal kontroluje przesunięcie pionowe // Style layoutu (bez zmian) 'display': this.preferences.hideFilters ? 'none' : 'flex', 'justify-content': 'center', 'align-items': 'center', 'gap': '8px', 'margin-bottom': '10px', // Może nie być potrzebny przy absolute // Style ciemnej ramki (bez zmian) 'background': 'linear-gradient(to bottom, #4d351b 0%, #2a1608 100%)', 'padding': '5px 10px', 'border-radius': '5px', 'border': '2px solid #1b0a00', 'border-top-color': '#150800', 'border-left-color': '#150800', 'box-shadow': '0 2px 5px rgba(0, 0, 0, 0.5), inset 0 0 3px rgba(0,0,0,0.2)', 'z-index': 10001 // Dodaj z-index, aby był nad innymi elementami } }); var jobsPerPageSelect = $('<select/>', { css: { 'padding': '2px', 'margin-left': '5px', 'border-radius': '3px', 'border': '1px solid rgba(0, 0, 0, 0.2)', 'background-color': 'white', 'cursor': 'pointer' } }).on('change', function() { GSJTOP.preferences.jobsPerPage = parseInt($(this).val()); localStorage.setItem('GSJTOP_jobsPerPage', GSJTOP.preferences.jobsPerPage); GSJTOP.preferences.currentPage = 0; GSJTOP.refreshWindow(); }); [4, 6, 8, 10].forEach(function(num) { jobsPerPageSelect.append($('<option/>', { value: num, text: num + ' ' + (num === 1 ? GSJTOPlang.jobWordSingular : (num < 5 ? GSJTOPlang.jobWordPluralFew : GSJTOPlang.jobWordPluralMany)), selected: GSJTOP.preferences.jobsPerPage === num })); }); var tooltipsButton = $('<div/>', { id: 'gj-tooltips', title: GSJTOP.preferences.showTooltips ? GSJTOPlang.hideInfo : GSJTOPlang.showInfo, // Dodany tooltip css: { 'width': '24px', 'height': '24px', 'background-color': GSJTOP.preferences.showTooltips ? '#4CAF50' : '#cccccc', 'border-radius': '3px', 'cursor': 'pointer', 'transition': 'all 0.3s ease', 'border': '1px solid rgba(0, 0, 0, 0.2)', 'box-shadow': '0 1px 3px rgba(0, 0, 0, 0.1)', 'margin-right': '1px' } }).click(function() { GSJTOP.preferences.showTooltips = !GSJTOP.preferences.showTooltips; localStorage.setItem('GSJTOP_showTooltips', GSJTOP.preferences.showTooltips); $(this).css('background-color', GSJTOP.preferences.showTooltips ? '#4CAF50' : '#cccccc') .attr('title', GSJTOP.preferences.showTooltips ? GSJTOPlang.hideInfo : GSJTOPlang.showInfo); // Aktualizacja tooltipa po kliknięciu GSJTOP.refreshWindow(); }); var visibilityButton = $('<div/>', { id: 'gsjtop-visibility-toggle', // Dodajmy ID dla łatwiejszego odniesienia (opcjonalne) title: GSJTOPlang.toggleVisibilityTooltip, // Zaktualizujmy tooltip css: { 'width': '24px', 'height': '24px', 'background-color': '#4a90e2', 'border-radius': '3px', 'cursor': 'pointer', 'transition': 'all 0.3s ease', 'border': '1px solid rgba(0, 0, 0, 0.2)', 'box-shadow': '0 1px 3px rgba(0, 0, 0, 0.1)', 'margin-right': '1px', 'background-size': '16px', 'background-position': 'center', 'background-repeat': 'no-repeat' } }).on('click', function() { // --- EDYCJA 1: Logika przełączania widoczności --- var visibilityPanel = $('#job-visibility-window'); var mapWindow = $('#custom-map-window'); // Sprawdź, czy którekolwiek z okien jest aktualnie otwarte if (visibilityPanel.length > 0 || mapWindow.length > 0) { // Jeśli tak, zamknij oba visibilityPanel.remove(); mapWindow.remove(); // Opcjonalnie: zmień wygląd przycisku na "nieaktywny" $(this).css({ 'background-color': '#4a90e2', // Wróć do domyślnego koloru }); } else { // Jeśli nie, otwórz oba GSJTOP.createVisibilityWindow(); // Najpierw okno widoczności try { GSJTOP.createCustomMapWindow(); // Potem mapa } catch(e) { console.error('Błąd podczas tworzenia okna mapy:', e); } // Opcjonalnie: zmień wygląd przycisku na "aktywny" $(this).css('background-color', '#cccccc'); // Ciemniejszy niebieski jako wskaźnik aktywności } }); var centerJobsFilter = $('<div/>', { title: this.preferences.hideCenterJobs ? GSJTOPlang.showLowIdJobs : GSJTOPlang.hideLowIdJobs, // Zmieniony tooltip css: { 'width': '24px', 'height': '24px', 'cursor': 'pointer', 'margin-right': '1px', 'background-color': this.preferences.hideCenterJobs ? '#cccccc' : '#ff4444', 'border-radius': '3px', 'border': '1px solid rgba(0, 0, 0, 0.2)', 'transition': 'all 0.3s ease', 'box-shadow': '0 1px 3px rgba(0, 0, 0, 0.1)' } }).on('mouseenter', function() { $(this).css({ 'transform': 'scale(1.1)', 'box-shadow': '0 2px 5px rgba(0, 0, 0, 0.2)' }); }).on('mouseleave', function() { $(this).css({ 'transform': 'scale(1)', 'box-shadow': '0 1px 3px rgba(0, 0, 0, 0.1)' }); }).click(function() { var isCurrentlyActive = $(this).css('background-color') === 'rgb(204, 204, 204)'; GSJTOP.preferences.hideCenterJobs = !isCurrentlyActive; localStorage.setItem('GSJTOP_hideCenterJobs', GSJTOP.preferences.hideCenterJobs); $(this).css({ 'background-color': isCurrentlyActive ? '#ff4444' : '#cccccc' }).attr('title', isCurrentlyActive ? GSJTOPlang.hideLowIdJobs : GSJTOPlang.showLowIdJobs); // Zmieniony tooltip GSJTOP.preferences.currentPage = 0; GSJTOP.refreshWindow(); }); var goldFilter = $('<img/>', { src: 'images/jobs/featured/goldjob.png', css: { 'width': '24px', 'height': '24px', 'opacity': showGold ? this.shownImageOpacity : this.hiddenImageOpacity, 'cursor': 'pointer', 'border': showGold ? '1px solid rgba(171, 153, 48, 0.5)' : 'none', 'border-radius': '3px' }, click: function() { GSJTOP.onJobIconFilterClick('gold', $(this)); } }); var silverFilter = $('<img/>', { src: 'images/jobs/featured/silverjob.png', css: { 'width': '24px', 'height': '24px', 'opacity': showSilver ? this.shownImageOpacity : this.hiddenImageOpacity, 'cursor': 'pointer', 'border': showSilver ? '1px solid rgba(112, 128, 144, 0.5)' : 'none', 'border-radius': '3px' }, click: function() { GSJTOP.onJobIconFilterClick('silver', $(this)); } }); filterContainer.append( tooltipsButton, lowIdJobsFilter, hideHighIdJobsFilter, visibilityButton, silverFilter, goldFilter, jobsPerPageSelect ); // Pobieramy zapisane ustawienia widoczności prac var hiddenJobs = JSON.parse(localStorage.getItem('GSJTOP_hiddenJobs') || '{}'); var data = this.getFilteredData(showSilver, showGold); var start = this.preferences.currentPage * this.preferences.jobsPerPage; // Filtrujemy ukryte prace tylko dla głównego wyświetlania, nie dla panelu widoczności var visibleJobs = data.filter(job => !job.hidden); var pageJobs = visibleJobs.slice(start, start + this.preferences.jobsPerPage); pageJobs.forEach(function(job) { var jobData = GSJTOP.getJobData(job, job.silver); if (jobData) { jobsWrapper.append(GSJTOP.getJobIcon(job.jobId, job.x, job.y, job.shortname, job.gold, jobData)); } }); // Dla panelu widoczności pokazujemy wszystkie prace data.forEach(function(job) { var jobData = GSJTOP.getJobData(job, job.silver); if (jobData) { var checkbox = $('<input/>', { type: 'checkbox', id: 'job-visibility-' + job.jobId, checked: !job.hidden, css: { 'margin': '2px' } }).on('change', function() { var hiddenJobs = JSON.parse(localStorage.getItem('GSJTOP_hiddenJobs') || '{}'); hiddenJobs[job.jobId] = !this.checked; localStorage.setItem('GSJTOP_hiddenJobs', JSON.stringify(hiddenJobs)); GSJTOP.refreshWindow(); }); var label = $('<label/>', { for: 'job-visibility-' + job.jobId, text: job.shortname, css: { 'font-size': '11px', 'margin-right': '5px' } }); jobVisibilityContainer.append($('<div/>').append(checkbox, label)); } }); container.append(jobsWrapper, filterContainer); return container; }; GSJTOP.onJobIconFilterClick = function (type, element) { var hiddenImageOpacity = this.hiddenImageOpacity; var shownImageOpacity = this.shownImageOpacity; var isCurrentlyActive = ($(element).css('opacity') == shownImageOpacity); var otherFilterActive = (type === 'gold') ? this.preferences.showSilver : this.preferences.showGold; if (isCurrentlyActive && !otherFilterActive) { return; } if (type === 'gold') { this.preferences.showGold = !isCurrentlyActive; localStorage.setItem('GSJTOP_showGold', this.preferences.showGold); $(element).css({ 'opacity': isCurrentlyActive ? hiddenImageOpacity : shownImageOpacity, 'border': isCurrentlyActive ? 'none' : '1px solid rgba(171, 153, 48, 0.5)' }); } else { this.preferences.showSilver = !isCurrentlyActive; localStorage.setItem('GSJTOP_showSilver', this.preferences.showSilver); $(element).css({ 'opacity': isCurrentlyActive ? hiddenImageOpacity : shownImageOpacity, 'border': isCurrentlyActive ? 'none' : '1px solid rgba(112, 128, 144, 0.5)' }); } // Pobierz aktualne dane po zmianie filtrów var data = this.getFilteredData(this.preferences.showSilver, this.preferences.showGold); // Odśwież mapę z aktualnymi danymi this.refreshMap(data); this.preferences.currentPage = 0; this.refreshWindow(); }; GSJTOP.openWindow = function () { if (!this.preferences.enabled) return; $('#goldJobs-bar').remove(); var bar = $('<div/>', { id: 'goldJobs-bar', css: { 'position': 'fixed', 'background': 'rgba(255, 255, 255, 0.0)', 'border': 'none', 'padding': '0px', 'z-index': 10000, 'display': 'flex', 'flex-direction': 'row', 'align-items': 'center', 'justify-content': 'center', 'width': '100%', 'height': '130px', 'left': '0', 'right': '0', 'top': '20px', 'margin': '0 auto', 'pointer-events': 'none' } }); var contentWrapper = $('<div/>', { css: { 'display': 'flex', 'justify-content': 'center', 'align-items': 'center', 'width': 'auto', 'margin': '0 auto', 'pointer-events': 'auto', 'position': 'relative' } }); var navigation = this.createNavigationButtons(); var jobsContainer = this.createJobsContainer(this.preferences.showSilver, this.preferences.showGold); contentWrapper.append(navigation.prev, jobsContainer, navigation.next); bar.append(contentWrapper); $('#ui_topbar').append(bar); $(document).on('position_change', GSJTOP.refreshWindow); }; GSJTOP.getAllTiles = function (callback) { Ajax.get('map', 'get_minimap', {}, function (r) { if (r.error) { return; } var result = []; var jobGroups = r.job_groups, i, j; for (i in jobGroups) { for (j in jobGroups[i]) { var coords = jobGroups[i][j]; var xTile = Math.floor(coords[0] / GameMap.tileSize); var yTile = Math.floor(coords[1] / GameMap.tileSize); if (!result.hasOwnProperty(xTile)) { result[xTile] = {}; } result[xTile][yTile] = 1; } } GSJTOP.tilesWithJobs = result; callback(); }); }; GSJTOP.getFilteredData = function (showSilver, showGold) { var jobs = GameMap.JobHandler.Featured; var k, jobId, job, j; var result = []; var hiddenJobs = JSON.parse(localStorage.getItem('GSJTOP_hiddenJobs') || '{}'); try { for (k in jobs) { var jobPlace = jobs[k]; for (jobId in jobPlace) { job = JobList.getJobById(jobId); if (!job) continue; j = jobPlace[jobId]; if ((j.silver && !showSilver) || (j.gold && !showGold)) { continue; } if (this.preferences.hideLowIdJobs && parseInt(jobId) < 131) { continue; } if (this.preferences.hideHighIdJobs && parseInt(jobId) > 160) { continue; } // Dodajemy wszystkie prace do rezultatu, ale oznaczamy je jako ukryte var distance = 0; try { distance = this.calculateDistance(j.x, j.y); } catch (e) {} var motivation = 0; try { var jobData = JobsModel.Jobs.find(jb => jb.id === parseInt(jobId)); if (jobData) { motivation = Math.round(jobData.jobmotivation * 100); } } catch (e) {} // Nazwa pracy var name = job.name || "Praca #" + jobId; result.push({ jobId: jobId, x: j.x, y: j.y, shortname: job.shortname, name: name, gold: j.gold, silver: j.silver, distance: distance, motivation: motivation, hidden: !!hiddenJobs[jobId] // Upewniamy się, że hidden jest wartością logiczną }); } } // Sortowanie według odległości result.sort(function(a, b) { if (a.distance !== b.distance) { return a.distance - b.distance; } return parseInt(a.jobId) - parseInt(b.jobId); }); } catch (e) { console.error("Błąd podczas filtrowania danych:", e); } return result; }; GSJTOP.refreshWindow = function () { if (!GSJTOP.preferences.enabled) { $('#goldJobs-bar').remove(); return; } Ajax.get('work', 'index', {}, function(response) { if (response.error) { return; } JobsModel.initJobs(response.jobs); // Pobierz aktualną liczbę widocznych prac var visibleJobs = GSJTOP.getFilteredData(GSJTOP.preferences.showSilver, GSJTOP.preferences.showGold) .filter(job => !job.hidden); var totalJobs = visibleJobs.length; var maxPages = Math.ceil(totalJobs / GSJTOP.preferences.jobsPerPage); // Aktualizuj przyciski nawigacji var updateButtonStyles = function() { var prevButton = $('#goldJobs-bar .jobs-container').prev(); var nextButton = $('#goldJobs-bar .jobs-container').next(); if (GSJTOP.preferences.currentPage <= 0) { prevButton.css('opacity', '0.5').css('cursor', 'default'); } else { prevButton.css('opacity', '1').css('cursor', 'pointer'); } if (GSJTOP.preferences.currentPage >= maxPages - 1 || maxPages <= 1) { nextButton.css('opacity', '0.5').css('cursor', 'default'); } else { nextButton.css('opacity', '1').css('cursor', 'pointer'); } }; // Wymień kontener z pracami var newContainer = GSJTOP.createJobsContainer(GSJTOP.preferences.showSilver, GSJTOP.preferences.showGold); $('#goldJobs-bar .jobs-container').replaceWith(newContainer); // Aktualizuj style przycisków updateButtonStyles(); }); }; GSJTOP.createCustomMapWindow = function() { // Sprawdź czy okno już istnieje if ($('#custom-map-window').length > 0) return; var self = this; // Zapisz referencję do GSJTOP // --- POCZĄTEK EDYCJI: Odczyt zapisanej pozycji --- var storageKey = 'GSJTOP_custom-map-window_Pos'; var savedPosition = null; try { var savedPosString = localStorage.getItem(storageKey); if (savedPosString) { savedPosition = JSON.parse(savedPosString); } } catch (err) { console.error("GSJTOP: Nie udało się odczytać pozycji okna mapy z localStorage.", err); localStorage.removeItem(storageKey); // Usuń uszkodzone dane } // --- KONIEC EDYCJI: Odczyt zapisanej pozycji --- var windowCss = { 'position': 'fixed', // Nowe pozycjonowanie - wyśrodkowane poziomo, górna krawędź 20px pod środkiem ekranu 'top': 'calc(50% + 120px)', 'left': '50%', 'transform': 'translateX(-50%)', // ... (reszta stylów: background, padding, border, etc.) ... 'background': 'linear-gradient(to bottom, #4d351b 0%, #2a1608 100%)', 'padding': '15px', 'border-radius': '5px', 'border': '2px solid #1b0a00', 'border-top-color': '#150800', 'border-left-color': '#150800', 'box-shadow': '0 4px 15px rgba(0, 0, 0, 0.7), inset 0 0 5px rgba(0,0,0,0.2)', 'z-index': 10003, 'cursor': 'move' }; // --- POCZĄTEK EDYCJI: Zastosowanie zapisanej pozycji --- if (savedPosition && savedPosition.top && savedPosition.left) { windowCss.top = savedPosition.top; windowCss.left = savedPosition.left; windowCss.transform = 'none'; // Usuwamy transformację } // --- KONIEC EDYCJI: Zastosowanie zapisanej pozycji --- // Okno główne (kontener z ramką w stylu TW) var mapWindow = $('<div/>', { id: 'custom-map-window', css: windowCss // Używamy przygotowanego obiektu CSS }); // Kontener na samą mapę i znaczniki var mapContent = $('<div/>', { css: { 'width': '770px', 'height': '338px', 'position': 'relative', 'margin': '0 auto', // Centrowanie wewnątrz paddingu okna 'overflow': 'hidden', 'background-color': 'white', 'border': '1px solid #333', // Ciemna ramka mapy 'cursor': 'default' // Przywracamy domyślny kursor dla obszaru mapy } }); // Definicje hrabstw (kafelków mapy) var counties = [ {id: 1, left: 0, top: 0, width: 110, height: 169}, {id: 2, left: 110, top: 0, width: 110, height: 169}, {id: 3, left: 220, top: 0, width: 110, height: 169}, {id: 4, left: 330, top: 0, width: 110, height: 114}, {id: 5, left: 440, top: 0, width: 110, height: 169}, {id: 6, left: 550, top: 0, width: 110, height: 169}, {id: 7, left: 660, top: 0, width: 110, height: 169}, {id: 8, left: 0, top: 169, width: 110, height: 169}, {id: 9, left: 110, top: 169, width: 110, height: 169}, {id: 10, left: 220, top: 169, width: 110, height: 169}, {id: 11, left: 330, top: 224, width: 110, height: 114}, {id: 12, left: 440, top: 169, width: 110, height: 169}, {id: 13, left: 550, top: 169, width: 110, height: 169}, {id: 14, left: 660, top: 169, width: 110, height: 169}, {id: 15, left: 330, top: 114, width: 110, height: 110} ]; // Tworzenie kafelków mapy jako elementy <img> counties.forEach(function(county) { $('<img>', { src: 'https://westpl.innogamescdn.com/images/map/minimap/county_' + county.id + '.jpg', css: { 'position': 'absolute', 'left': county.left + 'px', 'top': county.top + 'px', 'width': county.width + 'px', 'height': county.height + 'px', 'border': '1px solid #000' } }).appendTo(mapContent); }); // Pobierz dane o pracach var data = self.getFilteredData(self.preferences.showSilver, self.preferences.showGold); // Ustawienia mapy i markerów var maxX = 46592; var maxY = 20480; var mapWindowWidth = 770; var mapWindowHeight = 338; var markerSize = 24; var markerOffset = markerSize / 2; // Dodaj markery prac na mapie data.forEach(function(job) { if (!job.hidden) { var markerLeft = (job.x / maxX * mapWindowWidth) - markerOffset; var markerTop = (job.y / maxY * mapWindowHeight) - markerOffset; // Utwórz element markera (div) var marker = $('<div/>', { title: job.name, // Użyj przetłumaczonej nazwy pracy jako tooltip css: { 'position': 'absolute', 'left': markerLeft + 'px', 'top': markerTop + 'px', 'width': markerSize + 'px', 'height': markerSize + 'px', 'cursor': 'pointer', // Kursor 'pointer' dla markerów 'z-index': 10004, 'border-radius': '50%', } }).append($('<img/>', { // Dodaj ikonę pracy do markera src: 'https://westpl.innogamescdn.com/images/jobs/' + job.shortname + '.png', css: { 'width': '100%', 'height': '100%' } })); // Dodaj obramowanie/cień w zależności od typu pracy if (job.gold) { marker.css('box-shadow', '0 0 5px 2px gold'); } else if (job.silver) { marker.css('box-shadow', '0 0 5px 2px silver'); } else { marker.css('box-shadow', '0 0 3px rgba(0,0,0,0.5)'); } // Dodaj akcję kliknięcia marker.click(function(e) { // Dodajemy e (event) e.stopPropagation(); // Zapobiegamy propagacji kliknięcia do mapWindow if (typeof GameMap !== 'undefined' && GameMap.center) { GameMap.center(job.x, job.y); } // Zamknij okno mapy mapWindow.remove(); // Zamknij okno widoczności, jeśli istnieje $('#job-visibility-window').remove(); // Opcjonalnie: Zaktualizuj wygląd przycisku przełączającego widoczność var visibilityButton = $('#gsjtop-visibility-toggle'); if (visibilityButton.length > 0) { visibilityButton.css('background-color', '#4a90e2'); // Wróć do domyślnego koloru } }); // Dodaj marker do kontenera mapy mapContent.append(marker); } }); // Dodaj tylko mapContent do mapWindow (bez mapTitle) mapWindow.append(mapContent); // Dodaj całe okno mapy do strony $('body').append(mapWindow); // Uczynienie okna mapy przesuwalnym za pomocą całego okna (mapWindow) jako uchwytu GSJTOP.makeDraggable(mapWindow, mapWindow); }; GSJTOP.init = function () { if (!GSJTOP.preferences.enabled) return; var onLoad = function () { Ajax.get('work', 'index', {}, function(response) { if (response.error) { return; } JobsModel.initJobs(response.jobs); GSJTOP.preferences.currentPage = 0; GSJTOP.openWindow(); GSJTOP.dataLoaded = true; GSJTOP.startAutoRefresh(); }); }; if (!GSJTOP.hasOwnProperty('tilesWithJobs')) { new UserMessage(GSJTOPlang.loading, UserMessage.TYPE_HINT).show(); GSJTOP.getAllTiles(function () { GSJTOP.parseWholeMap(GSJTOP.tilesWithJobs, onLoad); }); } else { onLoad(); } }; GSJTOP.gui = {}; GSJTOP.gui.init = function () {}; // Inicjalizacja przy załadowaniu dokumentu $(document).ready(function () { try { GSJTOP.gui.init(); GSJTOP.createToggleCheckbox(); if (GSJTOP.preferences.enabled) { GSJTOP.init(); } } catch (e) { // Usunięto log błędu } }); }); // Zamknięcie głównej funkcji // Skrypt usuwający element first-purchase function usuńFirstPurchase() { const element = document.querySelector('.first-purchase'); if (element) { element.remove(); } else { clearInterval(intervalId); } } const intervalId = setInterval(usuńFirstPurchase, 1000); // Funkcja do stworzenia słownika tłumaczeń z API gry function createJobTranslations() { const translations = {}; JobList.getSortedJobs().forEach(job => { // Tworzymy klucz w formacie takim jak w oryginalnym ID (np. 'picking_mushrooms') const englishKey = job.id.toString(); translations[englishKey] = job.name; }); return translations; } // Używamy w skrypcie const jobTranslations = createJobTranslations(); // Funkcja do zamiany nazw w interfejsie document.querySelectorAll('#job-visibility-window label').forEach(label => { const jobId = label.getAttribute('for').split('-')[2]; // Pobieramy ID z atrybutu 'for' if (jobTranslations[jobId]) { label.textContent = jobTranslations[jobId]; } }); // Funkcja do tłumaczenia okna widoczności prac function translateJobVisibilityWindow() { const translations = {}; JobList.getSortedJobs().forEach(job => { translations[job.id] = job.name; }); const labels = document.querySelectorAll('#job-visibility-window label'); labels.forEach(label => { const jobId = label.getAttribute('for').split('-')[2]; if (translations[jobId]) { label.textContent = translations[jobId]; } }); } // Obserwator do wykrywania okna widoczności const jobVisibilityObserver = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.addedNodes && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach((node) => { if (node.id === 'job-visibility-window') { setTimeout(translateJobVisibilityWindow, 100); } }); } }); }); // Uruchomienie obserwatora jobVisibilityObserver.observe(document.body, { childList: true, subtree: true }); // Dodanie stylów dla paska przewijania w Chrome/Safari $('<style>') .text(` #job-visibility-window div::-webkit-scrollbar { width: 8px; } #job-visibility-window div::-webkit-scrollbar-track { background: #f0f0f0; } #job-visibility-window div::-webkit-scrollbar-thumb { background: #888; border-radius: 4px; } #job-visibility-window div::-webkit-scrollbar-thumb:hover { background: #666; } `) .appendTo('head'); })();