您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add additional search and filter functions to the Tweakers Pricewatch
// ==UserScript== // @name Tweakers Pricewatch search extension // @namespace http://remyblok.tweakers.net/ // @version 0.6 // @description Add additional search and filter functions to the Tweakers Pricewatch // @author Remy Blok // @match https://*.tweakers.net/* // @run-at document-idle // @grant unsafeWindow // @license MIT // ==/UserScript== (function() { 'use strict'; var debug = false; function FilterFormExtended(filterForm) { this.filterForm = filterForm; this.originalHandleAjaxResponse = null; } Object.extend(FilterFormExtended.prototype, { init: function () { this.hideMoreLinks(); }, hideMoreLinks: function () { if (!filterForm.listing) return; // find all the links more/less options link var links = document.querySelectorAll('.showLink, .hideLink'); var i = 0, link; while ((link = links[i++])) { try { //this is the id of the div element of the current filter var filterName = link.parentNode.parentNode.id; // if filtername is empty, generate one to keep unique elements if(!filterName) { filterName = "fn" + (Math.round(Math.random()*10000000)); } // creating HTML, could be rendered at the server this.generateHtml(link, filterName); //select the ul element of the extended filter var ul = link.parentNode.querySelectorAll(':scope #' + filterName + '_extendedFilter')[0]; // get the actual filter element var filter = ul.querySelectorAll(':scope #' + filterName + '_filter')[0]; filter.onkeyup = this.filterList; // get the actual select all element var selectAllCheckbox = ul.querySelectorAll(':scope #' + filterName + '_selectall')[0]; selectAllCheckbox.onclick = this.selectAll; // create a options bag to store key references into var options = {}; options.filterName = filterName; options.filterElement = filter; options.filterContainer = ul; options.selectAllCheckbox = selectAllCheckbox; // figure out what option values there are in the filer // we need to exclude the li elements from the extended filter options.optionsElements = link.parentNode.querySelectorAll(':scope ul:not(#' + ul.id + ') li'); filter.options = options; selectAllCheckbox.options = options; link.options = options; // we want additional things to happen when the user clicks on the link this.filterForm.addSimpleEvent(link, 'onclick', this.toggleHideMore); // trigger the initial visibility this.toggleHideMore.call(link); //just a check to see the scipt is working if (debug) { link.innerHTML = link.innerHTML + " extended"; } } catch { //just a check to see the scipt is working if (debug) { link.innerHTML = link.innerHTML + " failure"; } } } // replace the original handleAjaxResponse method so we can inject our onw code first. this.originalHandleAjaxResponse = this.filterForm.handleAjaxResponse; this.filterForm.handleAjaxResponse = this.handleAjaxResponse; }, generateHtml: function (link, filterName) { var html = ('<li>' + '<input type="text" id="{filterName}_filter" name="extendedFilter" class="text" placehoder="Filter">' + '</li>' + '<li>' + '<label for="{filterName}_selectall" class="checkbox">' + '<span class="inputWrapper">' + '<input type="checkbox" name="extendedSelectall" id="{filterName}_selectall" value="{filterName}_selectall">' + '</span> ' + '<span title="Alles Selecteren" class="facetLabel"><b>Alles Selecteren</b></span>' + '</label>' + '</li>').replace(/{filterName}/g, filterName); // add the a new ul to the page // separate ul used so that is can be easily hidden var ul = document.createElement('ul'); ul.id = filterName + "_extendedFilter"; ul.innerHTML = html; link.parentNode.insertBefore(ul, link.previousSibling.previousSibling); // before the link there ar two ul elements, we place it above these two elements }, handleAjaxResponse: function (response) { // validate the data is OK var data = checkJsonResponse(response); // because the querystring returned from the server does not include the extended field these need to be added // otherwise the original handleAjaxResponse will clear the form, and not reset the values if (data && 'querystring' in data) { // first we do all the select all comboboxes var selectAlls = document.querySelectorAll("[name='extendedSelectall']"); var i = 0, selectAll; data.querystring.extendedSelectall = []; while ((selectAll = selectAlls[i++])) { if (selectAll.checked) { data.querystring.extendedSelectall[data.querystring.extendedSelectall.length] = selectAll.value; } } // now the do the user typed filer var filters = document.querySelectorAll("[name='extendedFilter']"); var j = 0, filter; // we check if there is a value filled in and then add it to the query string. while ((filter = filters[j++])) { if (filter.value && filter.value !== '' ) { data.querystring[filter.id] = filter.value; } } } // call the original method filterFormExtended.originalHandleAjaxResponse.call(this, response); }, // 'this' is the 'a'-element of the show/hide more link toggleHideMore: function () { // because the original code has already run the check is the other way around //if (this.className != /*==*/ 'showLink') { if (this.className == 'hideLink') { this.options.filterContainer.classList.remove('hideMore'); } else { this.options.filterContainer.classList.add('hideMore'); // reset the filter and make sure all options are visible again this.options.selectAllCheckbox.parentNode.parentNode.classList.remove('selected'); this.options.selectAllCheckbox.checked = false; this.options.filterElement.value = ''; filterFormExtended.filterList.call(this.options.filterElement); } }, // onKeyUp event of the filter box // 'this' is the 'input'-element text filter filterList: function () { // we don't want to be case sensitive var searchString = this.value.toLowerCase(); var i = 0, li, needsScreenUpdate = false, shownItems = 0, checkedItems = 0; // loop over all options within this filter to see if they match // if they don't match we hide them, oterwise we show them // it also keeps track of the state of the checkbox before it was hidden // so then when it is shown again we can reset the correct state while ((li = this.options.optionsElements[i++])) { var label = singleOrNull(li.querySelectorAll(':scope .facetLabel')); var checkbox = singleOrNull(li.querySelectorAll(':scope input')); if (label !== null && checkbox !== null) { // do the string comparision, also here lower case if (label.innerText.toLowerCase().includes(searchString)) { // if it is currently hidden, we need to make it visible if (li.className == 'hideMore') { li.classList.remove('hideMore'); // restore the state of check box from before the filter if (checkbox.checkedBeforeFilter) { checkbox.checked = checkbox.checkedBeforeFilter; } checkbox.checkedBeforeFilter = checkbox.checked; // if a checked option is now visible we need to refresh the screen needsScreenUpdate |= checkbox.checked; } shownItems++; if (checkbox.checked) { checkedItems++; } } // only hide it if it is not already hidden // otherwise we override the checkedBeforeFilter state else if (li.className != 'hideMore') { li.classList.add('hideMore'); // if a checked option is now hidden we need to refresh the screen needsScreenUpdate |= checkbox.checked; checkbox.checkedBeforeFilter = checkbox.checked; // make sure the option in no longer checked checkbox.checked = false; } } } // disable the select all button if there are no options this.options.selectAllCheckbox.disabled = shownItems === 0; // show the selected all button as selected if all filter options are selected if (shownItems > 0 && checkedItems === shownItems) { this.options.selectAllCheckbox.parentNode.parentNode.classList.add('selected'); this.options.selectAllCheckbox.checked = true; } else { this.options.selectAllCheckbox.parentNode.parentNode.classList.remove('selected'); this.options.selectAllCheckbox.checked = false; } // finally update the page with the selected options if needed if (needsScreenUpdate) { filterForm.ajaxTimer(); } }, // onClick event of the selected all combobox // 'this' is sthe 'input'-element combobox selectAll: function (e) { var i = 0, li, needsScreenUpdate = false; // loop over all options, check if there are not hidden due to the text filter // then check the options while ((li = this.options.optionsElements[i++])) { if (!this.checked || li.className != 'hideMore') { var checkbox = singleOrNull(li.querySelectorAll(':scope input')); if (checkbox !== null) { // only update the screen if the checkbox is switched from state needsScreenUpdate |= (checkbox.checked != this.checked); checkbox.checked = this.checked; } } } // finally update the page with the selected options if needed if (needsScreenUpdate) { filterForm.ajaxTimer(); } }, }); // little helper function to get the first element from an array function singleOrNull(array) { if( array instanceof NodeList && array.length === 1) { return array[0]; } if (Array.isArray(array) && array.length == 1) { return array[0]; } return null; } // init the code if (unsafeWindow.filterForm) { var filterFormExtended = new FilterFormExtended(unsafeWindow.filterForm); filterFormExtended.init(); } })();