您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Help to calculate the point value when you searching for Marriott hotels.
// ==UserScript== // @name 万豪积分价值计算(国内酒店中信92折人民币结算) // @namespace http://tampermonkey.net/ // @version 1.1.2 // @description Help to calculate the point value when you searching for Marriott hotels. // @author You // @include https://www.marriott.*/search/* // @match https://www.marriott.com*/reservation/availabilityCalendar.mi // @icon https://www.google.com/s2/favicons?sz=64&domain=marriott.com // @grant none // @run-at document-end // @license MIT // ==/UserScript== //setTimeout(calculatePointValue, 5000); // Adjust the delay as needed var list = ""; var currency = [ {type: "THB", exchange: 0.215, tax: 0.84246218 }, {type: "CNY", exchange: 1, tax:0.8577 } ] var config = [{ host: "marriott.com.cn", abbrev: "cn", seedList: "div.property-card-container > div.property-card", calenderBoxList: "#isSubtotalView > tbody > tr > td ", calenderBoxPointsList: "#isNightlyRateView > tbody > tr > td ", leftButton : "li.js-previous-month a", rightButton : "li.js-next-month a", priceCell:"div.price-value", pointsCell: "div.m-price.points-value", seedHotelName: "", seedSize: "", seedUploaderNum: "", seedDownloaderNum: "", seedFInishNum: "", numOfClumns: "", fixedT0: "", fixedN0: "", A_ValueLevels: [ { A_Value: 1, fontWeight: 'bold', color: '#900C3F ', fontSize: '140%' }, { A_Value: 0.98, fontWeight: 'bold', color: 'red', fontSize: '100%' }, { A_Value: 0, fontWeight: '', color: '', fontSize: '100%' } ] }, { host: "marriott.com", abbrev: "com", seedList: "#merch-property-results > .js-property-list-container > .property-record-item", calenderBoxList: "div.DayPicker-Body > div.DayPicker-Week > div.DayPicker-Day", calenderBoxPointsList: "#isNightlyRateView > tbody > tr > td ", rightButton : "div.DayPicker-NavBar > span.DayPicker-NavButton.DayPicker-NavButton--next", leftButton : "div.DayPicker-NavBar > span.DayPicker-NavButton.DayPicker-NavButton--prev", seedHotelName: "", seedSize: "", seedUploaderNum: "", seedDownloaderNum: "", seedFInishNum: "", numOfClumns: "", fixedT0: "", fixedN0: "", A_ValueLevels: [ { A_Value: 1, fontWeight: 'bold', color: '#900C3F ', fontSize: '140%' }, { A_Value: 0.98, fontWeight: 'bold', color: 'red', fontSize: '100%' }, { A_Value: 0, fontWeight: '', color: '', fontSize: '100%' } ] } ] function getHotelName(dataObject){ return dataObject.hotelName; } function getHotelPrice(dataObject){ return parseInt(dataObject.replace(",","")); } function getCurrencyType(dataObject){ return dataObject.currency; } function getCurencyRatio(dataObject){ const foundCurrency = currency.find(cc => dataObject.includes(cc.type)); return foundCurrency ? foundCurrency.exchange : null; // Return the excha } function getCurencyRatioTax(dataObject){ const foundCurrency = currency.find(cc => dataObject.includes(cc.type)); return foundCurrency ? foundCurrency.tax : null; // Return the excha } function calculatePointValue(){ console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----start1") var rateContainers = document.getElementsByClassName('rate-container'); console.log(rateContainers[0]) console.log("legnth: "+ rateContainers.length) // Iterate through each 'rate-container' for (var j = 0; j < rateContainers.length; j++) { if(!rateContainers[j].getElementsByClassName('m-price points-value')[0]){ if(rateContainers[j].getElementsByClassName('t-points t-point-saver-point')[0]){ console.log(j+"HHHHHHHHHHHHHHHHHHHHHHHHHH---------CNCNCNCN-----------------") var points2 = parseInt( rateContainers[j].getElementsByClassName('t-points t-point-saver-point')[0].innerText.replace(" ","").replace(",","")); var currency2 = parseInt(rateContainers[j].getElementsByClassName('t-price')[0].innerText.replace(" ","").replace(",","")); var result2 = currency2 * 0.92 / points2; var button2 = document.createElement('button'); button2.innerText = 'CP Value: ' + result2.toFixed(3); button2.style.marginRight = '10px'; if(result2 >= 0.05){ button2.setAttribute("style", "background-color: #4CAF50; color: white; margin-right: 10px;"); } if(result2 < 0.05){ button2.setAttribute("style", "background-color: #f44336; ; color: white; margin-right: 10px;"); } rateContainers[j].insertBefore(button2, rateContainers[j].firstChild); } continue } console.log(j+"HHHHHHHHHHHHHHHHHHHHHHHHHH-------------------------------------") console.log('', rateContainers[j].getElementsByClassName('m-price points-value')[0].innerText.replace(" ","").replace(",","")); console.log('', rateContainers[j].getElementsByClassName('t-font-alt-xs price-value m-price-currency-s')[0].innerText.replace(" ","").replace(",","")); var points = parseInt( rateContainers[j].getElementsByClassName('m-price points-value')[0].innerText.replace(" ","").replace(",","")); var currency = parseInt(rateContainers[j].getElementsByClassName('t-font-alt-xs price-value m-price-currency-s')[0].innerText.replace(" ","").replace(",","")); var result = currency * 0.92 / points; var button = document.createElement('button'); button.innerText = 'CP Value: ' + result.toFixed(3); button.style.marginRight = '10px'; if(result >= 0.05){ button.setAttribute("style", "background-color: #4CAF50; color: white; margin-right: 10px;"); } if(result < 0.05){ button.setAttribute("style", "background-color: #f44336; ; color: white; margin-right: 10px;"); } rateContainers[j].insertBefore(button, rateContainers[j].firstChild); } console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----end") } function add_A_Value_Columns_helper(html, theConfig) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH---------------检查页面按钮加载------------"); const intv = setInterval(function() { const RightButton = document.querySelector("a.shop-pagination-next"); if (RightButton==null) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----返回错误33333--页面按钮还没加载,继续加载-"); return; } const rows = document.querySelectorAll(theConfig.seedList); for (let i = 0; i < rows.length; i++) { let trd = rows[i].querySelector(theConfig.priceCell); if(trd){ continue; }else{ let trd2 = rows[i].querySelector("div.price-container > div > div > a > div > span > span.price-value > span"); if(trd2){ continue; }else{ let trd3 = rows[i].querySelector(theConfig.pointsCell); if(trd3){ continue; }else{ let trd4 = rows[i].querySelector("div.unavailable-text"); if(trd4){ continue; }else{ let trd5 = rows[i].querySelector("span.opening-soon-font"); if(trd5){ continue; }else{ console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH---------------rows 数据出错------------"); console.log(rows[i]); return; }} } } }} clearInterval(intv); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH---------------按钮加载完成------------"); console.log(RightButton); add_A_Value_Columns(html, theConfig); }, 1000); } async function add_A_Value_Columns(html, theConfig) { const rows = document.querySelectorAll(theConfig.seedList); const firstRowRecorrd = rows[0].innerHTML; console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH------/////酒店列表模式/////---------------------------------------------------"); const pointsValueStoreList = new Array(rows.length); const hotelNameStoreList = new Array(rows.length); for (let i = 0; i < rows.length; i++) { pointsValueStoreList[i] = 0; hotelNameStoreList[i] = ""; console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----LOOP----------------------------------------------------------------"+i); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----当前row[i]---"); console.log(rows[i]); const dataProperty = rows[i].getAttribute('data-property'); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----当前dataPropertyl---"); console.log(dataProperty) console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----当前搜刮路径---"); console.log(theConfig.priceCell); let trd = rows[i].querySelector(theConfig.priceCell); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----当前搜刮结果---"); console.log(trd); const dataObject = JSON.parse(dataProperty.replace(/"/g, '"')); const hotelName = getHotelName(dataObject); hotelNameStoreList[i] = hotelName; if(!trd){ trd = rows[i].querySelector("div.price-container > div > div > a > div > span > span.price-value > span"); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----再次搜刮div.price-container > div > div > a > div > span > span.price-value > span--结果"); console.log(trd); if(!trd){ console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----价格Cell为空,跳过---"); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----------------------------------------------------------------------------"); continue; } } let price = getHotelPrice(trd.innerHTML); const currType = getCurrencyType(dataObject); const currRatio = getCurencyRatio(currType); const RMBValue = price * currRatio; let pointsAmount = rows[i].querySelector(theConfig.pointsCell); if(!pointsAmount){ console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----积分Cell为空,跳过---"); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----------------------------------------------------------------------------"); continue; } pointsAmount = getHotelPrice(pointsAmount.innerHTML); const targecell = rows[i].querySelector('div.price-container'); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----下面是targecell---"); console.log(targecell) // Create a new div element const newDiv = document.createElement('div'); newDiv.className = 'new-div-class'; // Add a class name to the new div if(currType == 'CNY'){ price = parseInt(price * 0.92 / 0.8577); const eachWworth = parseInt(price/pointsAmount*10000); pointsValueStoreList[i] = eachWworth; newDiv.innerHTML = ` <div>${hotelName}</div> <div>中信92折包税:${price}</div> <div>每万分值:<span style="background-color: ${eachWworth > 500 ? 'green' : 'red'}; color: white;">${eachWworth}</span></div> `; }else{ newDiv.innerHTML = ` <div>${hotelName}</div> <div>${price}</div> <div>人民币:${RMBValue}</div> `; } // Style the new div newDiv.style.display = 'block'; // Ensure it's a block-level element newDiv.style.textAlign = 'top'; // Align text to the left (default, but added for clarity) newDiv.style.marginRight = '10px'; // Add padding to the right newDiv.style.verticalAlign = 'top'; // Align text to the top newDiv.style.lineHeight = 'normal'; // Ensure normal line height for proper alignment newDiv.style.border = '1px solid black'; // Add a 1px black border to indicate the boundary const rateValueContainer = targecell.querySelector('div.price-sub-section'); targecell.insertBefore(newDiv, rateValueContainer); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----插入自建DIV完成!!!---"); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----------------------------------------------------------------------------"); } const RightButton = document.querySelector("a.shop-pagination-next"); if (!RightButton.hasAttribute('data-listener-added')) { RightButton.addEventListener('click', function (event) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-BUtton CLICK!!!!-----222222222222--"); RightButton.removeAttribute('data-listener-added'); //event.preventDefault(); // Prevent the default link behavior const intv = setInterval(function() { const newrows = document.querySelectorAll(theConfig.seedList); if (newrows==null) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----返回错误0000--rows为空,继续检测-"); return false; } const newfirstRowRecorrd = newrows[0].innerHTML; if (newrows && newrows.length < 1) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----返回错误1111--页面为空,继续检测-"); return false; } if (newfirstRowRecorrd == firstRowRecorrd) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----返回错误2222---页面还没有刷新,继续检测---"); return false; } clearInterval(intv); add_A_Value_Columns_helper(html, theConfig); }, 1000); // Remove the custom attribute after the click }, { once: true }); // Mark the listener as added RightButton.setAttribute('data-listener-added', 'true'); } const LButton = document.querySelector("a.shop-pagination-prev"); if (!LButton.hasAttribute('data-listener-added')) { LButton.addEventListener('click', function (event) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-BUtton CLICK!!!!-----222222222222--"); LButton.removeAttribute('data-listener-added'); //event.preventDefault(); // Prevent the default link behavior const intv = setInterval(function() { const newrows = document.querySelectorAll(theConfig.seedList); if (newrows==null) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----返回错误0000--rows为空,继续检测-"); return false; } const newfirstRowRecorrd = newrows[0].innerHTML; if (newrows && newrows.length < 1) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----返回错误1111--页面为空,继续检测-"); return false; } if (newfirstRowRecorrd == firstRowRecorrd) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----返回错误2222---页面还没有刷新,继续检测---"); return false; } clearInterval(intv); add_A_Value_Columns_helper(html, theConfig); }, 1000); // Remove the custom attribute after the click }, { once: true }); // Mark the listener as added LButton.setAttribute('data-listener-added', 'true'); } //重新排序 // Combine rows with their points into an array of objects console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----开始排序!!!前3个排序!!!!!---"); for (let i = 0; i < rows.length; i++) { console.log("XXXOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO----i="+i+"--HOTEL:--"+hotelNameStoreList[i]+"-----POINTS:"+pointsValueStoreList[i]+"----------------------------------------------------"); } // Extract the first 3 elements and their corresponding values const TOTALNUMTOREMOVE = rows.length; const rowsArray = Array.from(rows); const firstThree = rowsArray.slice(0, TOTALNUMTOREMOVE); const firstThreeValues = pointsValueStoreList.slice(0, TOTALNUMTOREMOVE); const pairedRows = firstThree.map((row, index) => ({ row: row, value: firstThreeValues[index] })); pairedRows.sort((a, b) => b.value - a.value); const parent = rows[0].parentElement; for (let i = pairedRows.length - 1; i >= 0; i--) { parent.insertBefore(pairedRows[i].row, parent.firstChild); // Moves the row to the top in reverse order } console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----所有代码完成!!!---"); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH--------STOP--------------------------------------------------------------"); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH--------STOP--------------------------------------------------------------"); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH--------STOP--------------------------------------------------------------"); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH--------STOP--------------------------------------------------------------"); } async function runMainfnction(html, theConfig, settingQuery){ console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----进入主程序:下面是rows[0]---"); const rows = document.querySelectorAll(settingQuery); console.log(rows[0]); for (let i = 0; i < rows.length; i++) { const price = rows[i].querySelector('a > div > h2'); if(!price){ continue; } price = price.innerText; price = getHotelPrice(price); const currencyText = rows[i].querySelector('a > div > p').innerText; const CurrType = currencyText.substring(0, 3); // " THB/总计 5 晚 " if(currencyText.substring(3)[0] == "总"){ const nights =currencyText.substring(3).replace("总计", "").replace("晚", "").trim(); } else{ const nights = currencyText.substring(3).replace("晚", "").trim(); } const currRatio = getCurencyRatio(CurrType); const totalP = parseInt(price * currRatio); //计算包税价格 totalP = totalP/getCurencyRatioTax(CurrType); const h2Element = document.createElement("p"); h2Element.setAttribute( "class", "l-margin-none t-font-xs" ); h2Element.style.fontSize = "18px"; h2Element.textContent = "人民币税后:" + parseInt(totalP); h2Element.style.color = "red"; const targecell = rows[i].querySelector('a > div'); //const rateValueContainer = rows[i].querySelector('a > div > p'); targecell.appendChild(h2Element); } list = document.querySelectorAll(settingQuery)[0].innerHTML; console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----list更新为---"); console.log(list); console.log("YES DONE--------"); } async function add_A_Value_Columns_Calender(html, theConfig, mode) { const settingQuery = null; console.log("mode 是" + mode); if(mode == "1"){ settingQuery = theConfig.calenderBoxList; } else if(mode == "2"){ settingQuery = theConfig.calenderBoxPointsList; } const intv = setInterval(function() { var rows = document.querySelectorAll(settingQuery); console.log(settingQuery); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----检查list是否和rows一样---"); var rowcontent = rows[0].innerHTML; console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----list是---"); console.log(list); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----rows[0]是---"); console.log(rowcontent); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----匹配结果---"); console.log(list == rowcontent); if (rows && rows.length < 1) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----返回错误1111---"); return false; } if (list == rowcontent) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH----返回错误2222---"); return false; } const RightButton = document.querySelector(theConfig.rightButton); if (!RightButton.hasAttribute('data-listener-added')) { RightButton.addEventListener('click', function (event) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-BUtton CLICK!!!!-----222222222222--"); event.preventDefault(); // Prevent the default link behavior add_A_Value_Columns_Calender(document, theConfig, mode); // Remove the custom attribute after the click RightButton.removeAttribute('data-listener-added'); }, { once: true }); // Mark the listener as added RightButton.setAttribute('data-listener-added', 'true'); } const leftButton = document.querySelector(theConfig.leftButton); if (!leftButton.hasAttribute('data-listener-added')) { leftButton.addEventListener('click', function (event) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-BUtton CLICK!!!!-----222222222222--"); event.preventDefault(); // Prevent the default link behavior add_A_Value_Columns_Calender(document, theConfig, mode); // Remove the custom attribute after the click leftButton.removeAttribute('data-listener-added'); }, { once: true }); // Mark the listener as added leftButton.setAttribute('data-listener-added', 'true'); } clearInterval(intv); runMainfnction(html, theConfig, settingQuery); }, 1000); } (function() { 'use strict'; var currentwebsite = window.location.host; var foundConfig = config.find(cc => currentwebsite.includes(cc.host)); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-网址已匹配-------"+foundConfig.host); const observeDOMChanges1 = (selector, callback) => { const observer = new MutationObserver(() => { const elements = document.querySelectorAll(selector); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-拉取1所有元素!!!-------"); if (elements && elements.length > 0) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-拉取1成功 结束观察!!! 拉取结果是 -------"); console.log(elements); observer.disconnect(); // Stop observing once elements are found callback("1"); } }); observer.observe(document.body, { childList: true, subtree: true, // To observe changes within child nodes attributes: true, // To detect changes in attributes characterData: true, // To detect changes in text nodes }); }; const observeDOMChanges2 = (selector,selector2, callback) => { const observer = new MutationObserver(() => { const elements = document.querySelectorAll(selector); const elementsPoints = document.querySelectorAll(selector2); console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-拉取2所有元素!!!-------"); if (elements && elements.length > 0) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-拉取2.1成功 结束观察!!!-------"); observer.disconnect(); // Stop observing once elements are found callback("1"); } else if (elementsPoints && elementsPoints.length > 0) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-拉取2.2成功 结束观察!!!-------"); observer.disconnect(); // Stop observing once elements are found callback("2"); } }); observer.observe(document.body, { childList: true, subtree: true, // To observe changes within child nodes attributes: true, // To detect changes in attributes characterData: true, // To detect changes in text nodes }); }; async function myFunction() { if (window.location.href.includes("reservation/availabilityCalendar.mi")) { observeDOMChanges2(foundConfig.calenderBoxList,foundConfig.calenderBoxPointsList, (result) => { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-运行myfunction11111-------"); add_A_Value_Columns_Calender(document, foundConfig, result); }); } else if (window.location.href.includes("search/findHotels.mi")) { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-运行myfunction2222-------"); observeDOMChanges1(foundConfig.seedList, () => { add_A_Value_Columns_helper(document, foundConfig); }); }else if (window.location.href.includes("search/availabilityCalendar.mi")) { observeDOMChanges2(foundConfig.calenderBoxList,foundConfig.calenderBoxPointsList, (result) => { console.log("HHHHHHHHHHHHHHHHHHHHHHHHHH-运行myfunction3333-------"); add_A_Value_Columns_Calender(document, foundConfig, result); }); } } myFunction(); })();