Cathay Award Search Fixer 2022

Beware: Only install using the "Install this Script" or "安裝腳本" button below, beware of fake buttons in the ad under it.

Before you install, Greasy Fork would like you to know that this script contains antifeatures, which are things there for the script author's benefit, rather than yours.

This script is only fully functional after you make a payment. Greasy Fork is not involved in the payment, so can't validate you will get anything of value or help you get a refund. The author of this script explains: Unlocked multi city pair search, route saving/favouriting, saved route searching, and multicity award booking for sponsors.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name                Cathay Award Search Fixer 2022
// @name:zh-TW          國泰獎勵機票搜尋引擎修復神器 2022
// @namespace           jayliutw
// @version             4.0.3
// @description         Beware: Only install using the "Install this Script" or "安裝腳本" button below, beware of fake buttons in the ad under it.
// @description:zh-tw   注意:安裝請務必點選下方 "Install this Script" 或 "安裝腳本" 的按鈕,慎防不肖業者用下方 Google Ads 投放假按鈕的釣魚廣告!
// @author              jayliutw
// @connect             greasyfork.org
// @connect             cathaypacific.com
// @connect             userscripts.jayliu.net
// @match               https://*.cathaypacific.com/cx/*/book-a-trip/redeem-flights/redeem-flight-awards.html*
// @match               https://*.cathaypacific.com/cx/*/book-a-trip/redeem-flights/facade.html*
// @match               https://api.cathaypacific.com/redibe/IBEFacade*
// @match               https://cxplanner.jayliu.net/*
// @match               https://userscripts.jayliu.net/*
// @match               https://book.cathaypacific.com/*
// @grant               GM_setValue
// @grant               GM_getValue
// @grant               GM_log
// @grant               GM_xmlhttpRequest
// @grant               unsafeWindow
// @antifeature         payment Unlocked multi city pair search, route saving/favouriting, saved route searching, and multicity award booking for sponsors.
// @license             GPL
// ==/UserScript==


if(window.location.href.indexOf("planner") > -1) {
    unsafeWindow.checkVersion = function(){
        return GM_info.script.version;
    }
}

//============================================================
// Main Userscript
//============================================================

(function() {
    'use strict';



const debug = false;

//============================================================
// Greasymonkey Function Wrappers
//============================================================

// GM_Log
function log(data){ if (debug) GM_log(data); }

// Get and Set Stored Values
function value_get(valueName, defaultValue) { return GM_getValue(valueName, defaultValue) }
function value_set(valueName, setValue) { GM_setValue(valueName, setValue); return setValue; }

// XMLHttpRequest and GM_xmlhttpRequest
function httpRequest(request, native = false){
    if(!native && !debug) {
        GM_xmlhttpRequest(request);
    } else {
      if (!request.method || !request.url) return;
      var http = new XMLHttpRequest();
      http.withCredentials = true;
      http.open(request.method, request.url, true);
      if (request.headers) { for ( var key in request.headers ) { http.setRequestHeader(key,request.headers[key]) } }
      if (request.onreadystatechange) http.onreadystatechange = function(){ request.onreadystatechange(this) }
      if (request.onload) http.onload = function(){ request.onload(this) }
      if (request.data) { http.send(request.data) } else { http.send() }
    }
}

//============================================================
// Initialize Variables
//============================================================

    let route_changed = false;

    // Retrieve CX Parameters

    let static_path = value_get("static_path", "/CathayPacificAwardV3/AML_IT3.1.14/");
    let requestVars = {};
    let tab_id = "";
    let availability_url = "https://book.cathaypacific.com/CathayPacificAwardV3/dyn/air/booking/availability?TAB_ID=";
    let form_submit_url = availability_url + tab_id;

    let autoScroll = true;

    function initCXvars(){
        if(typeof staticFilesPath !== "undefined" && static_path != staticFilesPath){
            log(typeof staticFilesPath);
            static_path = staticFilesPath;
            value_set("static_path", static_path);
        }

        if (typeof tabId === 'string') { tab_id = tabId; }
        if (typeof requestParams === 'string') {
            requestVars = JSON.parse(requestParams);
            tab_id = requestVars.TAB_ID;
        } else if (typeof requestParams === 'object') {
            requestVars = requestParams;
            tab_id = requestParams.TAB_ID || "";
        }

        form_submit_url = (typeof formSubmitUrl !== 'undefined') ? formSubmitUrl : availability_url + tab_id;
    }

    const browser_locale = navigator.language;
    const browser_lang = (browser_locale.trim().split(/-|_/)[0] == "zh") ? "zh" : "en";
    const browser_country = (browser_locale.trim().split(/-|_/)[1]?.toUpperCase() == "TW") ? "TW" : "HK";

    let login_url = "https://www.cathaypacific.com/content/cx/" + browser_lang + "_" +browser_country + "/sign-in.html?loginreferrer=" + encodeURI("https://www.cathaypacific.com/cx/" + browser_lang + "_" +browser_country + "/book-a-trip/redeem-flights/redeem-flight-awards.html");

    let r = Math.random();
    let t = tab_id || "";

//============================================================
// Helper Functions
//============================================================

    // Wait for Element to Load
    function waitForElm(selector) {return new Promise(resolve => {if (document.querySelector(selector)) {return resolve(document.querySelector(selector)); } const observer = new MutationObserver(mutations => {if (document.querySelector(selector)) {resolve(document.querySelector(selector)); observer.disconnect(); } }); observer.observe(document.body, {childList: true, subtree: true }); }); }

    // Check CX Date String Validity (dateString YYYYMMDD)
    function isValidDate(dateString) {if (!/^\d{8}$/.test(dateString)) return false; let year = dateString.substring(0, 4); let month = dateString.substring(4, 6); let day = dateString.substring(6, 8); if(year < 1000 || year > 3000 || month == 0 || month > 12) return false; let monthLength = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; if(year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)) monthLength[1] = 29; if(day <= 0 || day > monthLength[month - 1]) return false; let today = new Date(); let date = new Date(year, month - 1, day); if ((date - today)/24/60/60/1000 >= 366 || (date - today)/24/60/60/1000 < -1) return false; return true; };

    // Add to Date and Return CX Date String
    function dateAdd(days = 0, date = false) { let new_date = new Date(); if(date) { let year = +date.substring(0, 4); let month = +date.substring(4, 6); let day = +date.substring(6, 8); new_date = new Date(year, month - 1, day); }; new_date.setDate(new_date.getDate() + days); return new_date.getFullYear() +""+ (new_date.getMonth() +1).toString().padStart(2, '0') +""+ new_date.getDate().toString().padStart(2, '0'); };

    // Convert CX Date String to Dashed Date String
    function toDashedDate(date){ return date.substring(0, 4).toString() + "-" + date.substring(4, 6).toString().padStart(2, '0') + "-" + date.substring(6, 8).toString().padStart(2, '0');}
    function toDashedDateShort(date){ return date.substring(2, 4).toString() + "-" + date.substring(4, 6).toString().padStart(2, '0') + "-" + date.substring(6, 8).toString().padStart(2, '0');}

    // Get Weekday from CX Date String
    function dateWeekday(date){ let newdate = new Date(+date.substring(0, 4),(+date.substring(4, 6)-1),+date.substring(6, 8)); const weekday = {1:"Mon", 2:"Tue", 3:"Wed", 4:"Thu", 5:"Fri", 6:"Sat", 0:"Sun"}; return weekday[newdate.getDay()];
    };

    // Get Time
    function getFlightTime(timestamp, timeonly = false){ 
        let date = new Date(timestamp);
        if (timeonly) {
            let hours = (date.getUTCDate() - 1)*24 + date.getUTCHours();
            return (hours > 0 ? hours.toString() + "hr " : "") + date.getUTCMinutes().toString() + "mins";
        } else {
            return date.getUTCFullYear() + "-" + (date.getUTCMonth() + 1).toString().padStart(2, '0') + "-" + date.getUTCDate().toString().padStart(2, '0') + " " + date.getUTCHours().toString().padStart(2, '0') + ":" + date.getUTCMinutes().toString().padStart(2, '0');
        };
    };

    // Append CSS to DOM Element (Default to Shadow Root)
    function addCss(cssString, target = shadowRoot) {
        var styleSheet = document.createElement("style");
        styleSheet.innerHTML = cssString;
        target.appendChild(styleSheet);
    }


//============================================================
// Get Stored Values
//============================================================

    // Set Search Parameters

    let uef_from = value_get("uef_from", "HKG");
    let uef_to = value_get("uef_to", "TYO");
    let uef_date = value_get("uef_date", dateAdd(14));
    let uef_adult = value_get("uef_adult", "1");
    let uef_child = value_get("uef_child", "0");

    // Saved Queries

    let saved = value_get("saved",{});
    let saved_flights = value_get("saved_flights",{});

    let cont_query = value_get("cont_query", "0") == "0" ? 0 : 1; ///cont_query/.test(window.location.hash); //urlParams.get('cont_query');
    let cont_batch = value_get("cont_batch", "0") == "0" ? 0 : 1;; ///cont_batch/.test(window.location.hash); //urlParams.get('cont_batch');
    let cont_saved = value_get("cont_saved", "0") == "0" ? 0 : 1;; ///cont_saved/.test(window.location.hash); //urlParams.get('cont_saved');
    let cont_ts = value_get("cont_ts", "0"); //window.location.hash.match(/cont_ts=([0-9]+)&/) ? window.location.hash.match(/cont_ts=([0-9]+)&/)[1] : 0;
    let redirect_search = value_get("redirect_search", "0"); ///cont_query/.test(window.location.hash); //urlParams.get('cont_query');
    let cxplanner_flights = value_get("cxplanner_flights", []) == [] ? 0 : value_get("cxplanner_flights", []); ///cont_query/.test(window.location.hash); //urlParams.get('cont_query');

    function reset_cont_vars() {
        if(redirect_search != "0"){
            value_set("redirect_search", "0")
        } else {
            value_set("cont_query", "0");
            value_set("cont_batch", "0");
            value_set("cont_saved", "0");
            value_set("cont_ts", "0");
        }
    }

//============================================================
// Initialize Shadow Root
//============================================================

    const shadowWrapper = document.createElement("div");
    shadowWrapper.style.margin = 0;
    shadowWrapper.style.padding = 0;
    const shadowRoot = shadowWrapper.attachShadow({ mode: "closed" });
    const shadowContainer = document.createElement("div");
    shadowContainer.classList.add("unelevated_container");
    shadowRoot.appendChild(shadowContainer);

    if (debug && unsafeWindow.shadowRoot == undefined) {
        unsafeWindow.shadowRoot = shadowRoot;
    }

    function initRoot() {

        log("initRoot();")

        addCss(styleCss);

        const current_page = window.location.href;

        if(current_page.indexOf("/redibe/IBEFacade") > -1){

            log("initRoot /redibe/IBEFacade")
            waitForElm('h1').then((elm) => {
                if(elm.innerText == "Access Denied") {
                    document.body.querySelector("body > p").innerHTML = `<a href="https://www.cathaypacific.com/cx/${lang.el}_${lang.ec}/book-a-trip/redeem-flights/redeem-flight-awards.html">Go back and try again.</a>`
                }
            });

        } else if(current_page.indexOf("redeem-flight-awards.html") > -1){
            reset_cont_vars();

            log("initRoot redeem-flight-awards.html")
            waitForElm('.redibe-v3-flightsearch form').then((elm) => {
                elm.before(shadowWrapper);
                initSearchBox();
                if(redirect_search != "0") {
                    btn_search.innerHTML = lang.searching;
                    btn_search.classList.add("searching");
                    setTimeout(function(){
                        location.href = redirect_search;
                    },1500);
                } else {
                    checkLogin();
                }
            });

        } else if(current_page.indexOf("facade.html") > -1){
            reset_cont_vars();

            log("initRoot facade.html")
            waitForElm('.ibered__search-panel').then((elm) => {
                elm.before(shadowWrapper);
                initSearchBox();
                checkLogin();
            });

        } else if(current_page.indexOf("air/booking/availability") > -1 && cont_query){

            log("initRoot air/booking/availability with cont_query")
            waitForElm('body > header').then((elm) => {
                const boxes = document.querySelectorAll("body > div");
                boxes.forEach( box => { box.remove() });
                addCss(`
                html, body {overflow-x:inherit !important;}
                header {overflow-x:hidden;}
                `, document.body);
                document.body.append(shadowWrapper);
                shadowContainer.classList.add("results_container");
                if(cxplanner_flights) initCXplannerBox();
                initSearchBox();
                checkLogin();
            });


        } else if(window.location.href.indexOf("air/booking/availability") > -1){
            reset_cont_vars();

            log("initRoot air/booking/availability without cont_query")
            waitForElm('#section-flights .bound-route, #section-flights-departure .bound-route').then((elm) => {
                shadowWrapper.style.margin = "30px 20px 0px 20px";
                shadowWrapper.style.padding = 0;
                document.querySelector("#section-flights, #section-flights-departure").before(shadowWrapper);
                initSearchBox();
                checkLogin();
            });

        } else if(window.location.href.indexOf("air/booking/complexAvailability") > -1){
            reset_cont_vars();

            log("initRoot air/booking/complexAvailability")
            waitForElm('.mc-trips .bound-route').then((elm) => {
                shadowWrapper.style.margin = "30px 20px 0px 20px";
                shadowWrapper.style.padding = 0;
                document.querySelector(".mc-trips").before(shadowWrapper);
                initSearchBox();
                checkLogin();
            });

        }

    }

//============================================================
// Localisation
//============================================================

    var lang = (browser_lang != "zh") ? {
        "ec" : "HK",
        "el": "en",
        "search" : "Search",
        "coffee" : "Did this tool help you? Buy me a coffee! ",
        "searching" : "<img src='https://book.cathaypacific.com"+static_path+"common/skin/img/icons/cx/icon-loading.gif'> Searching...",
        "searching_w_cancel" : "<img src='https://book.cathaypacific.com"+static_path+"common/skin/img/icons/cx/icon-loading.gif'> Searching... (Click to Stop)",
        "next_batch" : "Load More...",
        "search_20" : "Batch Availability for 20 Days",
        "search_all_cabins" : "Search Availability in All Cabins",
        "flights" : "Available Flights",
        "nonstop":"Non-Stop",
        "first" : "First",
        "business" : "Bus",
        "premium" : "Prem",
        "economy" : "Econ",
        "first_full" : "First Class",
        "business_full" : "Business Class",
        "premium_full" : "Premium Economy",
        "economy_full" : "Economy Class",
        "date" : "Date",
        "no_flights" : "No Redemption Availability",
        "expired" : "Search Next 20 (Requires Refresh)",
        "searching_cont" : "<img src='https://book.cathaypacific.com"+static_path+"common/skin/img/icons/cx/icon-loading.gif'> Please wait... (Page will refresh)",
        "super" : "SuperCharged Award Search",
        "error" : "Unknown Error... Try Again",
        "bulk_batch" : "Batch Search",
        "bulk_flights" : "Flights",
        "new_version" : "New Version Available:",
        "login" : "Reminder: Login before searching.",
        "tab_retrieve_fail" : "Failed to retrieve key. Try logging out and in again.",
        "key_exhausted" : "Key request quota exhausted, attempting to get new key...",
        "getting_key" : "Attempting to retrieve API key...",
        "invalid_code": "Invalid Destination Code",
        "invalid_date": "Invalid Date",
        "saved_queries": "Saved Flight Queries",
        "maxsegments":"Max 6 Sectors Accepted",
        "multi_book":"Book Multi-City Award",
        "query":"Search",
        "delete":"Remove",
        "search_selected":"Search All Saved",
        "book_multi":"Book Multicity Award",
        "nosaves":"You do not have any saved queries. Click on ♥ in batch results to save.",
        "advanced":"Advanced<br>Features",
        "loading":"Searching...",
        "hu_prompt":"Looks like you're searching a lot. You might want to read this.",
        "prem_title":"Enable Advanced Features",
        "prem_intro":"Hey fellow award flyer! Hope you're enjoying this plugin. How would you like to enhance your search experience even further? Here are some advanced features I think you're gonna love:",
        "prem_feat1":"Search multiple routes at once.",
        "prem_text1":"Flexible with your routings? Select multiple origins and destinations (up to 6 each), and find availability between those cities across multiple days with a single search! Also excellent for those of you trying to find availability for complex round-the-world itineraries! For example, searching for TPE,HKG to NRT,KIX will search for flights from Taipei and Hong Kong to Tokyo and Osaka.",
        "prem_feat2":"Bookmark your search queries.",
        "prem_text2":"Found availability for a date and want to save it to come back to later? Have a particular route that you're regularly watching for availability? Now you can save your date and itinerary simply by clicking a heart on the results page, and later search for that route again by simply selecting it from your list.",
        "prem_feat3":"Batch search your saved routes.",
        "prem_text3":"At the click of a button, search for all itineraries and dates you have previously saved, regardless of their origins, destinations, and dates. Say goodbye to endlessly changing your origin and destination search paramters!",
        "prem_feat4":"Submit oneworld Multi-City Award Search",
        "prem_text4":"From your saved routes, create a multi-city itinerary search that would have taken ages on Cathay's multicity search.",
        "prem_feat5":"And more to come!",
        "prem_donate":"To enable these extended features, please click below to view the Extras package on buymeacoffee.com.<a href='https://www.buymeacoffee.com/jayliutw/e/106024' target='_blank' class='unlock_btn'>Unlock Advanced Features</a>",
        "human":"Cathay's website needs you to prove you're a human:",
        "bot_check":"Please Complete Cathay Bot Check",
        "mixed":"Mixed Class Available via"
    } : {
        "ec" : "TW",
        "el": "zh",
        "search" : "搜尋",
        "coffee" : "這工具有幫到你嗎?歡迎請我喝杯咖啡呀!",
        "searching" : "<img src='https://book.cathaypacific.com"+static_path+"common/skin/img/icons/cx/icon-loading.gif'> 請稍後...",
        "searching_w_cancel" : "<img src='https://book.cathaypacific.com"+static_path+"common/skin/img/icons/cx/icon-loading.gif'> 請稍後... (點我暫停)",
        "next_batch" : "載人更多...",
        "search_20" : "批次搜尋 20 天可兌換航班",
        "search_all_cabins" : "批次搜尋全艙等航班",
        "flights" : "可兌換航班",
        "nonstop":"直飛",
        "first" : "頭等",
        "business" : "商務",
        "premium" : "豪經",
        "economy" : "經濟",
        "first_full" : "頭等艙",
        "business_full" : "商務艙",
        "premium_full" : "特選經濟艙",
        "economy_full" : "經濟艙",
        "date" : "日期",
        "no_flights" : "查無獎勵機位",
        "expired" : "再搜尋 20 天 (畫面需重整)",
        "searching_cont" : "<img src='https://book.cathaypacific.com"+static_path+"common/skin/img/icons/cx/icon-loading.gif'> 請稍後... (視窗將會刷新)",
        "super" : "SUPERCharged Award Search",
        "error" : "不明錯誤... 再試一次",
        "bulk_batch" : "批次查詢",
        "bulk_flights" : "航班",
        "new_version" : "有新版本可更新:",
        "login" : "提醒: 請先登入後再搜尋。",
        "tab_retrieve_fail" : "無法取得金鑰,請試著登出再重新登入。",
        "key_exhausted" : "金鑰查詢額度用盡,正嘗試取得新金鑰...",
        "getting_key" : "正在嘗試取得 API 金鑰...",
        "invalid_code": "目的地代碼錯誤",
        "invalid_date": "日期錯誤",
        "saved_queries": "收藏的航程日期",
        "maxsegments":"至多可選擇 6 航段",
        "multi_book":"兌換多城市行程",
        "query":"查詢",
        "delete":"刪除",
        "search_selected":"批次查詢收藏行程",
        "book_multi":"多目的地行程預定",
        "nosaves":"您沒有收藏任何行程。您可以在批次結果頁點擊愛心 ♥ 收藏行程。",
        "advanced":"進階功能<br>啟用說明",
        "loading":"查詢中...",
        "hu_prompt":"你好像查詢量很大...或許你該看看這裡!",
        "prem_title":"啟用神器進階功能",
        "prem_intro":"嗨,哩程同好們!希望這個插件有幫助到各位!有沒有想再更進一步提升查票體驗呢?那別錯過以下幾個相信一定會讓各位愛不釋手的進階功能:",
        "prem_feat1":"一次查詢多個路線",
        "prem_text1":"你也是行程彈性、有票就飛一族嗎?進階版支援同時輸入多個出發地和目的地(至多各 6 個),就可以輕鬆批次查找多個城市之間跨日期的獎勵機位!這對想要組合複雜的環球票行程的哩民們也是超級方便!比如,只要出發地選 TPE,KHH、目的地選 NRT,KIX,就可以同時輕鬆查找台北到東京、大阪和高雄到東京、大阪的機票!",
        "prem_feat2":"把行程存到最愛",
        "prem_text2":"找到了某個航線有位子的日期,想要收藏起來之後回來查?還是已經鎖定某個日期和航線,定期回來關注有沒有放票?現在只要在批次搜尋結果頁簡單按個愛心,就可以把選定的航程日期收藏到自己的最愛起來,之後回來就可以輕鬆從清單裡隨選即查!",
        "prem_feat3":"一批次查詢收藏的路線",
        "prem_text3":"只要一鍵就能批次查詢所有收藏清單裡的行程和日期,所有結果一次呈現,一目了然!再也不必來來回回改搜尋條件和日期!",
        "prem_feat4":"多城市行程(環球票)查詢",
        "prem_text4":"在你收藏的行程清單裡,簡單勾選你的行程(最多6個)就能組出國泰網站「多個城市」查詢的參數,並直接搜尋和選航班!",
        "prem_feat5":"還有更多待產中,請拭目以待!",
        "prem_donate":"若有興起解鎖上述進階功能,請點擊下方按鈕至 buymeacoffee.com 查看進階功能 Extras 包:<a href='https://www.buymeacoffee.com/jayliutw/e/106024' target='_blank' class='unlock_btn'>解鎖進階功能</a>",
        "human":"國泰網站需要你證明你是真人:",
        "bot_check":"請解除國泰網站機器人檢查",
        "mixed":"潛在混艙航班經由"
    };

//============================================================
// Search Box
//============================================================

    const searchBox = document.createElement("div");
    searchBox.innerHTML = `
        <div class='unelevated_form'>
            <div class='unelevated_title'><a href="https://www.cathaypacific.com/cx/${lang.el}_${lang.ec}/book-a-trip/redeem-flights/redeem-flight-awards.html">Unelevated Award Search</a></div>

            <div class='login_prompt hidden'><span class='unelevated_error'><a href="` + login_url + `">` + lang.login + `</a></span></div>

            <div class='unelevated_update hidden'><a href='https://pse.is/cxupdate' target='_blank'>`+ lang.new_version + ` <span id='upd_version'>3.2.1</span> &raquo;</a></div>
            
            <div class='unelevated_prem_desc unelevated_prem_hidden'>
                <span class="prem_title">` + lang.prem_title + `</span>
                <div class="prem_content">
                    <span class="prem_intro">` + lang.prem_intro + `</span>
                    <ul>
                        <li><span class="feat_title">` + lang.prem_feat1 + `</span><span class="feat_text">` + lang.prem_text1 + `</span></li>
                        <li><span class="feat_title">` + lang.prem_feat2 + `</span><span class="feat_text">` + lang.prem_text2 + `</span></li>
                        <li><span class="feat_title">` + lang.prem_feat3 + `</span><span class="feat_text">` + lang.prem_text3 + `</span></li>
                        <li><span class="feat_title">` + lang.prem_feat4 + `</span><span class="feat_text">` + lang.prem_text4 + `</span></li>
                        <li><span class="feat_title">` + lang.prem_feat5 + `</span></li>
                    </ul>
                    <span class="prem_intro">` + lang.prem_donate + `</span>
                </div>
            </div>

            <div class='unelevated_faves unelevated_faves_hidden'>
                <div class="faves_tabs">
                    <a href="javascript:void(0);" class="tabs tab_queries">Routes</a>
                    <a href="javascript:void(0);" class="tabs tab_flights">Flights</a>
                </div>
                <a href="javascript:void(0);" class="search_selected">`+lang.search_selected+` &raquo;</a>
                <!--<a href="javascript:void(0);" class="search_multicity">${lang.book_multi} &raquo;</a>-->
                <div class="saved_flights"></div>
                <div class="saved_queries"></div>
            </div>

            <div class="unelevated_saved"><a href="javascript:void(0);"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="heart_save" viewBox="0 0 16 16"> <path d="M4 1c2.21 0 4 1.755 4 3.92C8 2.755 9.79 1 12 1s4 1.755 4 3.92c0 3.263-3.234 4.414-7.608 9.608a.513.513 0 0 1-.784 0C3.234 9.334 0 8.183 0 4.92 0 2.755 1.79 1 4 1z"></path></svg><span>0</span></a></div>
            <div class="unelevated_premium"><a href="javascript:void(0);"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" class="premium_crown"><!--! Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M576 136c0 22.09-17.91 40-40 40c-.248 0-.4551-.1266-.7031-.1305l-50.52 277.9C482 468.9 468.8 480 453.3 480H122.7c-15.46 0-28.72-11.06-31.48-26.27L40.71 175.9C40.46 175.9 40.25 176 39.1 176c-22.09 0-40-17.91-40-40S17.91 96 39.1 96s40 17.91 40 40c0 8.998-3.521 16.89-8.537 23.57l89.63 71.7c15.91 12.73 39.5 7.544 48.61-10.68l57.6-115.2C255.1 98.34 247.1 86.34 247.1 72C247.1 49.91 265.9 32 288 32s39.1 17.91 39.1 40c0 14.34-7.963 26.34-19.3 33.4l57.6 115.2c9.111 18.22 32.71 23.4 48.61 10.68l89.63-71.7C499.5 152.9 496 144.1 496 136C496 113.9 513.9 96 536 96S576 113.9 576 136z"/></svg><span>` + lang.advanced + `</span></a></div>

            <div class='labels'>
                <a href="javascript:void(0);" class="switch"><svg height="16px" width="16px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 365.352 365.352" xml:space="preserve" stroke-width="0" transform="rotate(180)"><g id="SVGRepo_bgCarrier" stroke-width="0"></g> <path d="M363.155,169.453l-14.143-14.143c-1.407-1.407-3.314-2.197-5.304-2.197 c-1.989,0-3.897,0.79-5.304,2.197l-45.125,45.125v-57.503c0-50.023-40.697-90.721-90.721-90.721H162.3c-4.143,0-7.5,3.358-7.5,7.5 v20c0,4.142,3.357,7.5,7.5,7.5h40.26c30.725,0,55.721,24.996,55.721,55.721v57.503l-45.125-45.125 c-1.407-1.407-3.314-2.197-5.304-2.197c-1.989,0-3.896,0.79-5.304,2.197l-14.143,14.143c-1.406,1.406-2.196,3.314-2.196,5.303 c0,1.989,0.79,3.897,2.196,5.303l82.071,82.071c1.465,1.464,3.385,2.197,5.304,2.197c1.919,0,3.839-0.732,5.304-2.197 l82.071-82.071c1.405-1.406,2.196-3.314,2.196-5.303C365.352,172.767,364.561,170.859,363.155,169.453z"></path> <path d="M203.052,278.14h-40.26c-30.725,0-55.721-24.996-55.721-55.721v-57.503l45.125,45.126 c1.407,1.407,3.314,2.197,5.304,2.197c1.989,0,3.896-0.79,5.304-2.197l14.143-14.143c1.406-1.406,2.196-3.314,2.196-5.303 c0-1.989-0.79-3.897-2.196-5.303l-82.071-82.071c-2.93-2.929-7.678-2.929-10.607,0L2.196,185.292C0.79,186.699,0,188.607,0,190.596 c0,1.989,0.79,3.897,2.196,5.303l14.143,14.143c1.407,1.407,3.314,2.197,5.304,2.197s3.897-0.79,5.304-2.197l45.125-45.126v57.503 c0,50.023,40.697,90.721,90.721,90.721h40.26c4.143,0,7.5-3.358,7.5-7.5v-20C210.552,281.498,207.194,278.14,203.052,278.14z"></path> </svg></a>
                <label class="labels_left"><span>From</span>
                    <input tabindex="1" type='text' id='uef_from' name='uef_from' placeholder='TPE' value='` + uef_from + `'><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="clear_from" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"></path> </svg></a></label>
                <label class="labels_right"><span>Adults</span>
                    <input tabindex="4" type='number' inputmode='decimal' onClick='this.select();' id='uef_adult' name='uef_adult' placeholder='Adults' value='` + uef_adult + `'></label>
                <label class="labels_left"><span>To</span>
                    <input tabindex="2" type='text' id='uef_to' name='uef_to' placeholder='TYO' value='` + uef_to + `'><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="clear_to" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"></path> </svg></label>
                <label class="labels_right"><span>Children</span>
                    <input tabindex="5" type='number' inputmode='decimal' onClick='this.select();' id='uef_child' name='uef_child' placeholder='Children' value='` + uef_child + `'></label>
                <label class="labels_left"><span>Date</span>
                    <input tabindex="3" class='uef_date' onClick='this.setSelectionRange(6, 8);' id='uef_date' inputmode='decimal' name='uef_date' placeholder='` + dateAdd(30) + `' value='` + uef_date + `'></label>
                <button class='uef_search'>` + lang.search + `</button>
            </div>

            <div class='unelevated_sub'><a href='https://jayliu.net/buymeacoffee' target='_blank'>` + lang.coffee + `</a><span class='coffee_emoji'>☕</span></div>
        </div>

        <div class='multi_box hidden'>
        <select id="multi_cabin">
    <option value="Y">${lang.economy_full}</option>
    <option value="W">${lang.premium_full}</option>
    <option value="C">${lang.business_full}</option>
    <option value="F">${lang.first_full}</option>
</select>
                <label class="labels_right"><span>Adults</span>
                    <input type='number' inputmode='decimal' onClick='this.select();' id='multi_adult' name='multi_adult' placeholder='Adults' value='1'></label>
                <label class="labels_right"><span>Children</span>
                    <input type='number' inputmode='decimal' onClick='this.select();' id='multi_child' name='multi_child' placeholder='Children' value='0'></label>
                                    <a href="javascript:void(0)" class='multi_search'>` + lang.multi_book + `</a>
        </div>

        <div class='bulk_box'>
            <div class="bulk_results bulk_results_hidden">
            <div class="filters">
<label><input type="checkbox" id="filter_nonstop">${lang.nonstop}</label>
<label><input type="checkbox" id="filter_first" checked>${lang.first}</label>
<label><input type="checkbox" id="filter_business" checked>${lang.business}</label>
<label><input type="checkbox" id="filter_premium" checked>${lang.premium}</label>
<label><input type="checkbox" id="filter_economy" checked>${lang.economy}</label>
</div>
                <table class='bulk_table show_first show_business show_premium show_economy'><thead><th class='bulk_date'>` + lang.date + `</th><th class='bulk_flights'>` + lang.flights + ` <span class='info-x info-f'>` + lang.first + `</span><span class='info-x info-j'>` + lang.business + `</span><span class='info-x info-p'>` + lang.premium + `</span><span class='info-x info-y'>` + lang.economy + `</span></th></thead><tbody></tbody></table>
            </div>
            <div class="bulk_footer">
                <div class="bulk_footer_container">
                    <button class='bulk_submit'>` + lang.search_20 + `</button>
                    <div class="bulk_error bulk_error_hidden"><span></span></div>
                </div>
            </div>
        </div>
        <div id="encbox"></div>
       `;

//============================================================
// Styles
//============================================================

    const styleCss = `
        .unelevated_form * { box-sizing:border-box; -webkit-text-size-adjust: none;}
        .unelevated_form a, .bulk_box a { color:#367778; }
        .unelevated_form input:focus { outline: none; }
        .results_container { max-width: 900px; margin: 0 auto; padding: 20px 20px; }
        @media screen and (max-width: 500px) { .results_container { padding:20px 10px; } }
        .cont_query .modal {display:none !important;}
        .unelevated_form { position:relative;transition: margin-left 0.7s ease-out;z-index: 11; font-family: "GT Walsheim","Cathay Sans EN", CathaySans_Rg, sans-serif; border: 1px solid #bcbec0; margin:10px 0; background: #f7f6f0; padding: 8px 0px 8px 8px; border-top: 5px solid #367778; box-shadow: 0px 0px 7px rgb(0 0 0 / 20%);}
        .unelevated_form.uef_collapsed { margin-left:-90%;}
        .unelevated_title {font-weight: 400; font-size: 17px; font-family: "GT Walsheim","Cathay Sans EN", CathaySans_Rg, sans-serif; color: #2d2d2d; margin: 5px; height:26px;}
        .unelevated_title a {text-decoration:none; color: #2d2d2d;}
        .unelevated_form .unelevated_premium {    position: absolute;
            right: 10px;
            top: 6px;
            background: linear-gradient(339deg, #fdf98b, #e4c63f,#fef985,#eec946);
            box-shadow: -1px 1px 3px rgb(155 95 70 / 40%);
            display: inline-block;
            border-radius: 5px;
            padding: 1px;
        }
        .unelevated_form .unelevated_premium a, .unelevated_form .unelevated_premium a:hover, .unelevated_form .unelevated_premium a:active, .unelevated_form .unelevated_premium a:focus {
            font-size: 15px; line-height: 28px; text-decoration: none !important; color: #4d3b0e; display: block; height: 28px;
            background: linear-gradient(180deg, #fcd54a, #e8b524,#ffd561,#f7eb6d);
            border-radius: 5px;
            padding: 1px 8px;
        }
        .unelevated_form .unelevated_premium svg.premium_crown { width: 16px;margin-right: 6px;height: 26px;display: inline-block;}
        .unelevated_form .unelevated_premium svg.premium_crown path { fill: #bf8028;}
        .unelevated_form .unelevated_premium a span {
            display:inline-block;
            padding: 3px 0;
            vertical-align: top;
            line-height: 22px;
            font-size: 18px;
            zoom:0.5;
            text-transform:uppercase;
        }
        .unelevated_form .unelevated_saved { position:absolute; right:10px;top:6px;background: #ae4b4b; display: inline-block; border-radius: 5px; padding: 3px 10px;}
        .unelevated_form .unelevated_saved a, .unelevated_form .unelevated_saved a:hover, .unelevated_form .unelevated_saved a:active, .unelevated_form .unelevated_saved a:focus {font-size: 15px; line-height: 24px; text-decoration: none !important; color: white; display: block; height: 24px;}
        .unelevated_form .unelevated_saved svg.heart_save { width: 16px;margin-right: 6px;height: 24px;display: inline-block;}
        .unelevated_form .unelevated_saved svg.heart_save path { fill: #ff8b8b;}
        .unelevated_form .unelevated_saved a span {vertical-align: top; line-height: 24px;}
        .unelevated_container .unelevated_saved{ display:none; }

        .elevated_on .unelevated_prem_desc,
        .elevated_on .unelevated_premium {
            display:none;
        }

        .unelevated_prem_desc {
            line-height: 24px;
            overflow:scroll;
            transition: all 0.5s ease-out;
            background:#fdfefe;
            border: 1px solid #bebebe;
            margin-right: 8px;
            box-shadow: inset 0px 0px 4px 0px rgb(0 0 0 / 10%);
            position: absolute;
            top: 0px;
            right: 0;
            left: 8px;
            z-index: 100;
            height: calc(100% + 14px);
            margin-top: 42px;
            opacity:1;
            padding:10px;
        }
        .unelevated_prem_hidden {height:0;opacity:0; z-index: -1;}
        .unelevated_form .autocomplete-items div:hover{
            background-color: #e9e9e9;
        }
        .unelevated_form .autocomplete-active {
            /*when navigating through the items using the arrow keys:*/
            background-color: DodgerBlue !important;
            color: #ffffff;
        }

        .prem_title {
            display:block;
            text-align:center;
            font-size:17px;
            font-weight:bold;
            margin-bottom:8px;
            color:#367778;
        }

        .prem_intro {
            display:block;
            margin:10px;
            font-size:14px;
        }

        #activation_input {
            font-size:17px;
            margin:10px;
            width: calc(100% - 20px);
            padding: 10px;
            text-transform:uppercase;
        }

        .unelevated_prem_desc ul {
            list-style-type: disclosure-closed;
            padding-left:23px;
        }

        .unelevated_prem_desc li{
            list-style-type: disclosure-closed;
            margin-bottom:5px;
            padding-left:0;
        }

        .feat_title {
            display:block;
            font-size:17px;
            font-weight:bold;
            margin-bottom:5px;
            color:#ae4b4b;
        }


        .feat_text {
            display:block;
            font-size:14px;
            color:#666;
        }

        .unelevated_form .unlock_btn{
            display: block;
            margin: 10px auto;
            padding: 5px;
            border-radius: 5px;
            text-align: center;
            text-decoration: none;
            width: 200px;
            background: linear-gradient(180deg, #fcd54a, #e8b524,#ffd561,#f7eb6d);
            color: rgb(130, 85, 50);
            border: 1px solid #f8c19c;
            box-shadow: -1px 1px 3px rgba(0,0,0,0.3);
            font-weight:bold;
        }

        .unelevated_faves .saved_queries{
            display:block;
        }

        .unelevated_faves .saved_flights{
            display:none;
        }
        
        .unelevated_faves.flights .saved_queries {
            display:none;
        }

        .unelevated_faves.flights .saved_flights {
            display:block;
        }
        .faves_tabs{
            margin-left: 10px;
        }
        .faves_tabs a.tabs {
            display: inline-block;
            border-radius: 5px 5px 0 0;
            text-decoration: none;
            font-size: 12px;
            line-height: 15px;
            margin-right: 5px;
            height: 25px;
            padding: 5px 10px;
            margin-top: 7px;
        }
        .unelevated_faves .tab_queries,
        .unelevated_faves.flights .tab_flights {
            background:#357677;
            color:white;
        }
        .unelevated_faves .tab_flights,
        .unelevated_faves.flights .tab_queries {
            background: #cec9b9;
            color:#444444;
        }

        .unelevated_faves .saved_queries,
        .unelevated_faves .saved_query {
            list-style: none;
        }
        .unelevated_faves .saved_queries {
            margin: 0 10px;
            padding:0px;
            border-top: 2px solid #367778;
            position: absolute;
            left: 0;
            right: 0;
            bottom: 0;
            top: 32px;
            overflow: scroll;
        }
        .saved_queries:empty:after {
            display:flex;
            content:"` + lang.nosaves + `";
            text-align: center;
            font-size: 14px;
            align-items: center;
            justify-content: center;
            height: 95%;
            opacity: 40%;
            line-height: 25px;
            margin: 0 25px;

        }
        .unelevated_faves .saved_query {
            position:relative;
            margin: 0;
            padding:3px 10px;
            font-size:12px;
            font-family: "Cathay Sans EN", CathaySans_Md, sans-serif;
        }
        .unelevated_faves .saved_query label {
            margin: 0;
            min-width: 150px;
            display: inline-block;
        }
        .unelevated_faves .saved_query input {
            vertical-align:-2px;
            margin-right:5px;
            //display:none;
        }
        .unelevated_faves .saved_query:nth-child(odd){
            background: #f1efe6;
        }




        .unelevated_faves .saved_flights {
            list-style: none;
        }
        .unelevated_faves .saved_flights {
            margin: 0 10px;
            padding:0px;
            border-top: 2px solid #367778;
            position: absolute;
            left: 0;
            right: 0;
            bottom: 0;
            top: 32px;
            overflow: scroll;
        }
        .saved_flights:empty:after {
            display:flex;
            content:"` + lang.nosaves + `";
            text-align: center;
            font-size: 14px;
            align-items: center;
            justify-content: center;
            height: 95%;
            opacity: 40%;
            line-height: 25px;
            margin: 0 25px;

        }
        .unelevated_faves .saved_flights .saved_flight {
            position:relative;
            margin: 0;
            padding:3px 10px;
            font-size:10px;
            font-family: "Cathay Sans EN", CathaySans_Md, sans-serif;
        }
        .unelevated_faves .saved_flights .saved_flight label {
            margin: 0 0 5px 0;
            min-width: 150px;
            display: inline-block;
        }
        .unelevated_faves .saved_flights .saved_flight input {
            vertical-align:-2px;
            margin-right:5px;
        }
        .unelevated_faves .saved_flights .saved_flight:nth-child(odd){
            background: #f1efe6;
        }
        .unelevated_faves .saved_flights .saved_flight label > span {
            display:inline-block;
            vertical-align:top;
        }
        span.sf_date {
            display:block;
        }
        span.sf_route {
            background: #CCCCCC;
            padding: 2px 6px;
            border-radius: 5px 0 0 5px;
            display: inline-block;
        }
        span.sf_flights {
            background: #e3cfc8;
            padding: 2px 6px;
            border-radius: 0 5px 5px 0;
            display: inline-block;
        }
        span.sf_avail > span {
            display: inline-block;
            line-height: 11px;
            font-size: 10px;
            padding: 2px 4px;
            color: white;
            font-weight: normal;
            border-radius: 3px;
            margin-left: 3px;
            height: 15px;
        }
        span.sf_avail .av_j { background: #002e6c;}
        span.sf_avail .av_f { background: #832c40;}
        span.sf_avail .av_p { background: #487c93;}
        span.sf_avail .av_y { background: #016564;}

        .multi_box{
            height: 67px;
            background: #f7f6f0;
            border: 1px solid #bcbec0;
            position: relative;
            margin-top: -11px;
            margin-bottom: -67px;
            z-index: 10;
            padding: 10px;
            box-sizing: border-box;
            display: flex; flex-wrap: wrap;
        }
        .multi_box * {
            box-sizing: border-box;

        }
        .multi_box.hidden{
            display:none;
        }
        .multi_box select{
            border: 1px solid #bcbec0;
            height: 45px;
            width: calc(35% - 10px);
            margin-right:10px;
            display:inline-block;
            vertical-align:top;
            padding: 10px;
        }

        .multi_box label { margin:0; display: inline-block; position: relative; width: calc(20% - 10px); margin-right:10px; }
        .multi_box label > span { position: absolute; top: 0px; left: 5px; color: #66686a; font-family: Cathay Sans EN, CathaySans_Rg, sans-serif; line-height: 25px; font-size: 10px;}
        .multi_box input {  font-family: Cathay Sans EN, CathaySans_Rg, sans-serif; padding: 19px 5px 5px 5px; border-radius: 0px; border: 1px solid #bcbec0; display: inline-block; margin: 0px 8px 8px 0px; height: 45px; width: 100%; font-size:16px}
        .multi_box a.multi_search { background-color: #367778;
            overflow: hidden;
            text-overflow: ellipsis;
            border: none;
            color: white;
            vertical-align: top;
            margin: 0px;
            height: 45px;
            width: calc(25%);
            font-size: 11px;
            text-align: center;
            display: flex;
            flex-wrap: wrap;
            align-content: center;
            justify-content: center;
            text-decoration: none;
            padding: 0px 10px;
            line-height:15px;
        }
        a.switch:active {
            margin-top: 40px;
            margin-left: -18px;
        }
        a.switch {
            display: inline-block;
            position: absolute;
            background: white;
            z-index: 15;
            margin-top: 38px;
            border: 1px solid #bcbec0;
            text-decoration: none;
            padding: 2px 10px;
            border-radius: 15px;
            left: 32.5%;
            margin-left: -19px;
            height: 22px;
            line-height: 16px;
        }
        a.switch svg path {
            fill: #AAA;
        }
        .unelevated_form .labels { display: flex; flex-wrap: wrap;}
        .unelevated_form .labels label { margin:0; display: inline-block; position: relative; width:50%; padding: 0px 8px 0px 0px; }
        .unelevated_form .labels label.labels_left {width:65%;}
        .unelevated_form .labels label.labels_right {width:35%;}
        .unelevated_form .labels label > span { position: absolute; top: 0px; left: 5px; color: #66686a; font-family: Cathay Sans EN, CathaySans_Rg, sans-serif; line-height: 25px; font-size: 10px;}
        .unelevated_form .labels input {  font-family: Cathay Sans EN, CathaySans_Rg, sans-serif; padding: 19px 5px 5px 5px; border-radius: 0px; border: 1px solid #bcbec0; display: inline-block; margin: 0px 8px 8px 0px; height: 45px; width: 100%; font-size:16px}
        svg.clear_from, svg.clear_to {
            position: absolute;
            right: 20px;
            top: 15px;
            opacity: 30%;
        }

        .unelevated_form button.uef_search { background-color: #367778; white-space:nowrap; overflow:hidden;text-overflow:ellipsis;border: none; color: white; display: inline-block;vertical-align: top; margin: 0px; height: 45px; width: calc(35% - 8px); font-size:15px}
        .unelevated_sub { line-height:25px; vertical-align:top;} .coffee_emoji {display:inline-block; line-height:25px; font-size: 25px; margin-left: 6px; vertical-align: top;}
        .unelevated_sub a { line-height:25px; vertical-align:top; font-family: Cathay Sans EN, CathaySans_Bd, sans-serif; font-size: 14px !important; text-decoration:underline dotted !important; margin: 0px; color: #ae4b4b !important; font-weight: bold;}
        .unelevated_sub a:after { content:none !important; }


        .heavy_user_prompt {
            background: linear-gradient(339deg, #fdf98b, #e4c63f,#fef985,#eec946);
            box-shadow: -1px 1px 3px rgb(155 95 70 / 40%);
            border-radius: 5px;
            padding: 1px;
            margin-right:10px;
            margin-top:10px;
        }
        .heavy_user_prompt a {
            font-size: 15px; min-height:20px; padding:10px; line-height: 20px; text-decoration: underline !important; color: #802d2d; display: block;
            background: linear-gradient(180deg, #fcd54a, #e8b524,#ffd561,#f7eb6d);
            border-radius: 5px;
            padding: 10px 8px;
            text-align:center;
        }


        a.uef_toggle, a.uef_toggle:hover { background: #367778; display: block; position: absolute; right: -1px; top: -5px; padding-top:5px; width: 30px; text-align: center; text-decoration: none; color: white !important; padding-bottom: 5px; }
        a.uef_toggle:after {content:'«'} .uef_collapsed a.uef_toggle:after {content : '»'}
        .bulk_box {min-height: 60px; transition: margin-top 0.7s ease-out;background: #f7f6f0; border: 1px solid #bcbec0; box-shadow: 0px 0px 7px rgb(0 0 0 / 20%); margin-top: -11px !important; margin-bottom: 20px; z-index: 9; position: relative;}
        .bulk_box_hidden {position:relative; margin-top:-80px;}
        .bulk_results {transition: all 0.5s ease-out; min-height: 30px; margin: 0 10px 10px 10px;}
        .bulk_results_hidden { height:0; min-height:0; margin:0; overflow:hidden; transition: all 0.5s ease-out;}
        .filters {
            text-align: center;
            font-size: 12px;
            padding: 10px 0px 10px 0px;
            position: sticky;
            top: 0;
            z-index: 10;
            background: #f7f6f0;
            border-bottom: 1px solid #c6c2c1;
        }
        .filters input{
            vertical-align: -2px;
            margin-right:5px;
            margin-left:10px;
        }
        .filters label {
            display:inline-block;
        }
        .bulk_table { width:100%; border: 1px solid #c6c2c1; margin-top: -1px; font-size: 12px; border-spacing: 0; border-collapse: collapse; }
        .bulk_table th { text-align:center !important; font-weight:bold; background: #ebedec; line-height:17px; font-size: 12px; }
        .bulk_table td { background:white; }
        .bulk_table tr:nth-child(even) td { background:#f9f9f9; }
        .bulk_table th, .bulk_table td { border: 1px solid #c6c2c1; padding: 5px; }
        .bulk_table .bulk_date { width:80px; text-align:center; }
        .bulk_table .bulk_date a { text-decoration:underline !important; font-family: "Cathay Sans EN", CathaySans_Md, sans-serif; font-weight: 400; display:block;margin-bottom:5px;}
        .unelevated_container .bulk_table td.bulk_flights > div { display:none; }
        .unelevated_container .bulk_table td.bulk_flights > div:first-of-type { display:block; }
        .unelevated_container .bulk_table td.bulk_flights .flight_title { display:none; }
        .bulk_table td.bulk_flights { padding:5px 5px 0 5px; font-family: "Cathay Sans EN", CathaySans_Rg, sans-serif; font-weight: 400; line-height:0px; }
        .bulk_table td.bulk_flights .flight_list:empty:after {
            display: block;
            height: 24px;
            content: "` + lang.no_flights+ `";
            margin-bottom: 5px;
            margin-top: -3px;
            margin-left: 10px;
            font-family: "Cathay Sans EN", CathaySans_Rg, sans-serif;
            font-weight: 400;
            line-height: 24px;
            color: #AAA;
           }
        .bulk_table td.bulk_flights .flight_list span.bulk_response_error { line-height: 24px;}
        .bulk_table .bulk_flights .bulk_no_flights { display:block;padding-bottom:5px; }
        .bulk_response_error { display:block;padding-bottom:5px;padding-left:5px;padding-right:5px; color:red; }
        .bulk_table .flight_title { display: block; background: #dde8e8; font-size: 12px; line-height: 15px; padding: 3px 7px; margin-bottom: 7px; margin-top: 2px; border-bottom: 3px solid #357677; position:relative; }
        .bulk_go_book { float:right; margin-right:5px; margin-left:10px; font-weight:bold;}
        a.bulk_save, a.bulk_save:hover, a.bulk_save:active { outline: none !important; float: left; margin-right:5px; text-decoration: none !important;}
        a.bulk_save svg.heart_save { width: 12px; height: 12 px;display: inline-block;}
        a.bulk_save svg.heart_save path { fill:gray;}
        a.bulk_saved svg.heart_save path { fill:#d65656; }
        a.bulk_save *, a.bulk_go_book * {  pointer-events: none; }
        .flight_wrapper {
            position:relative;
            display:inline-block;
        }
        .flight_info {
            position: absolute;
            left: 0;
            top: 37px;
            background: #e0e0e0;
            border: 1px solid #bbb;
            padding: 6px 10px;
            display: none;
            line-height: 18px;
            z-index: 15;
            border-radius: 5px;
            white-space: nowrap;
            box-shadow: 0px 0px 5px rgb(0 0 0 / 30%);
        }
        .flight_info > span {
            display:block;
        }
        .flight_info span.info_flight {
            font-weight:bold;
            font-family: CathaySans_Bd, sans-serif;
        }
        .info_dept > span, .info_arr > span {
            display: inline-block;
            width: 50px;
            color: #999;
            font-weight: bold;
            font-family: CathaySans_Md, sans-serif;
        }
        span.info_transit,
        span.info_duration {
            margin: 8px 0px;
            background: #ededed;
            border-radius: 5px;
            padding: 2px 8px;
            text-align: center;
            font-size: 11px;
            color: #888;
        }
        span.info_duration {
            margin-bottom:5px;
        }
        .flight_item.active + .flight_info {
            display:block;
        }
        .flight_item {
            transition: all 0.5s ease-in;
            background: #e0e0e0;
            line-height:15px !important;
            border-radius: 5px;
            margin-bottom: 5px;
            white-space: nowrap;
            font-size:12px;
            font-family: "GT Walsheim","Cathay Sans EN", CathaySans_Rg, sans-serif;
            font-weight:400;
            position:relative;
            display: inline-block;
            overflow:hidden;

            max-width:0px;
            padding: 6px 0px;
            margin-right: 0px;
        }
        .flight_item span.stopover { border-radius:5px;padding: 2px 4px; color: #909090 !important; display: inline-block; background: white; font-size: 10px; margin: 0px 4px !important; line-height: 11px; }
        .flight_item.direct { background: #cbe0cf; }
        .flight_item.saved { background:#f5ebd8; }
        .flight_item img { line-height: 15px; max-height: 15px; vertical-align: middle; margin-right: 2px; max-width: 20px;}
        .show_first .flight_item[data-f="1"],
        .show_business .flight_item[data-j="1"],
        .show_premium .flight_item[data-p="1"],
        .show_economy .flight_item[data-y="1"] {
        max-width:280px;
        padding: 6px 6px;
        margin-right: 6px;
        }
        .nonstop_only .flight_item[data-direct="0"] {
            max-width:0px;
            padding:6px 0px;
            margin-right: 0px;
        }
        span.bulk_j { background: #002e6c;}
        span.bulk_f { background: #832c40;}
        span.bulk_p { background: #487c93;}
        span.bulk_y { background: #016564;}
        .flight_item span.flight_num{
            line-height: 16px;
            vertical-align: middle;
            height: 16px;
            display: inline-block;
            padding: 2px 0;
        }
        .flight_item span.bulk_j,
        .flight_item span.bulk_f,
        .flight_item span.bulk_p,
        .flight_item span.bulk_y {
            color: white;
            border-radius: 5px;
            font-size:10px;
            overflow:hidden;
            transition: all 0.5s ease-in;
            display:inline-block;
            vertical-align:top;
            height: 16px;
            line-height: 16px;

            max-width:0px;
            padding: 2px 0px;
            margin-left: 0px;
        }
        .show_first span.bulk_f,
        .show_business span.bulk_j,
        .show_premium span.bulk_p,
        .show_economy span.bulk_y {
            max-width:25px;
            padding: 2px 5px;
            margin-left: 3px;
        }


        .flight_item:hover img,
        .flight_item:focus img,
        .flight_item:active img,
        .flight_item.saved img {
            opacity:0;
        }
        span.flight_save {
            display:none;
            position:absolute;
            left:5px;
            top:5px;
            opacity:0.6;
        }
        span.flight_save * {
            pointer-events: none;
        }
        span.flight_save svg {
            height:12px;
            width:12px;
            padding:5px;
        }
        .flight_item.saved span.flight_save {
            opacity:1;
            display:block;
        }
        .flight_item.saved svg.heart_save path {
            fill:#d65656;
        }
        .flight_item:hover span.flight_save,
        .flight_item:focus span.flight_save,
        .flight_item:active span.flight_save {
            display:inline-block;
        }
        .flight_item .chevron {
            vertical-align: top;
            display: inline-block;
            padding: 2px 0 2px 0px;
            height: 16px;
            opacity: 0.5;
            margin-right: -2px;
            margin-left: -2px;
        }
        .flight_item .chevron svg {
            vertical-align: top;
            transform:rotate(-90deg);
        }

        .flight_item.active .chevron svg {
            transform:rotate(0deg);
        }
        .flight_item * {  pointer-events: none; }
        .flight_item .flight_save {  pointer-events: auto; }

        .bulk_footer{ min-height: 45px; position:sticky; bottom:0;}
        /*.bulk_footer.bulk_sticky .bulk_footer_container { position: fixed; bottom: 0; padding: 10px; background: #f7f6f0; margin: 0 auto; border-top: 1px solid #c6c2c1; box-shadow: 0px 0px 7px rgb(0 0 0 / 20%); max-width: 858px; left: 0; right: 0; }*/
        .bulk_footer .bulk_footer_container { padding: 10px; background: #f7f6f0; margin: 0; }
        .bulk_footer.bulk_sticky .bulk_footer_container { border-top: 1px solid #c6c2c1; box-shadow: 0px 0px 7px rgb(0 0 0 / 20%); }
        @media screen and (max-width: 500px) { .bulk_footer.bulk_sticky .bulk_footer_container  { max-width: 838px;} }
        button.bulk_submit {position:relative;background-color: #367778; border: none; color: white; vertical-align: middle; margin: 0px auto; height: 45px; line-height: 35px; padding: 5px 0; width: 100%; display: block; font-family: "GT Walsheim","Cathay Sans EN", CathaySans_Rg, sans-serif !important;font-size:15px}
        .bulk_submit img, button.uef_search img {line-height: 35px; height: 25px; width:auto; display: inline-block; margin-right: 10px; vertical-align: -7px;}
        .bulk_searching, .uef_search.searching  {background-color: #b9cdc9 !important;}
        .col-select-departure-flight > .row:last-of-type { padding-bottom: 140px; }
        span.info-x { border-radius: 5px; padding: 2px 5px; margin-left: 5px; color:white; font-size:10px; font-family: CathaySans_Md, Cathay Sans EN; font-weight: 400; }
        span.info-f { background: #832c40;}
        span.info-j { background: #002e6c;}
        span.info-p { background: #487c93;}
        span.info-y { background: #016564;}
        .unelevated_update.hidden { display:none; }
        .unelevated_update { border-radius: 5px; background: #f27878; padding: 5px 10px; margin: 0px 8px 10px 0; text-align: center; }
        .unelevated_update a { color:white !important; } .unelevated_update a:after { content:none !important; }
        .unelevated_update a span { font-weight:bold; font-family: "GT Walsheim","Cathay Sans EN", CathaySans_Md, sans-serif; }
        .unelevated_update.update_show { display:block; }

        .login_prompt {  height: 40px; line-height: 20px; overflow: hidden; transition: all 0.5s ease-out; margin-bottom: 10px; }
        .login_prompt.hidden { height: 0; overflow:hidden; margin: 0; }

        .unelevated_faves {
            line-height: 20px;
            overflow: hidden;
            transition: all 0.5s ease-out;
            background: #eae6d9;
            border: 1px solid #bebebe;
            margin-right: 8px;
            box-shadow: inset 0px 0px 4px 0px rgb(0 0 0 / 10%);
            position: absolute;
            top: 0px;
            right: 0;
            left: 8px;
            z-index: 100;
            height: calc(100% - 52px);
            margin-top: 42px;
            opacity:1;
        }
        .unelevated_faves_hidden {height:0;opacity:0;}
        .unelevated_faves span.saved_title {
            height:20px;
            display: block;
            margin: 6px 15px;
            font-size: 13px;
            color: #787878;
            font-weight: bold;
            font-family: "Cathay Sans EN", CathaySans_Md, sans-serif;
        }
        a.search_selected {
            position: absolute;
            right: 15px;
            top: 6px;
            height: 20px;
            line-height: 20px !important;
            font-size: 12px !important;
            font-weight: bold !important;
            display:block;
        }
        .flights a.search_selected {
            position: absolute;
            right: 15px;
            top: 6px;
            height: 20px;
            line-height: 20px !important;
            font-size: 12px !important;
            font-weight: bold !important;
            display:none;
        }
        a.search_multicity {
            position: absolute;
            right: 15px;
            top: 6px;
            height: 20px;
            line-height: 20px !important;
            font-size: 12px !important;
            font-weight: bold !important;
            display:none;
        }
        .saved_book {
            margin-left:10px;
            line-height:20px !important;
            font-weight:bold;
            display:inline-block;
        }
        .saved_remove {
            font-weight:bold;
            position: absolute;
            line-height: 20px !important;
            font-weight: bold;
            right: 5px;
            top: 3px;
        }
        .flights .saved_remove {
            line-height: 36px !important;
        }
        .multi_on .search_multicity {
            position: absolute;
            right: 15px;
            top: 6px;
            height: 20px;
            line-height: 20px !important;
            font-size: 12px !important;
            font-weight: bold !important;
            display:block;
        }
        .multi_on .saved_book,
        .multi_on .saved_remove,
        .multi_on .search_selected {
            display:none;
        }
        .leg{ color: #ae4b4b !important; font-weight:bold;}
        .saved_remove svg{
            height:20px;
            fill:#b4afaf;
        }
        .saved_book *, .saved_remove * {
            pointer-events: none;
        }
        span.unelevated_error { padding: 10px 0 10px 10px; line-height:20px; max-height:100%; display: block; background: #ffd2d2; border-radius: 5px; margin: 0 10px 5px 0; text-align: center; color: #b54545; font-weight:bold; font-size:14px;}
        span.unelevated_error a {padding: 0; margin: 0;  text-decoration: underline; line-height: 20px; max-height: 100%; height: 24px; display: block; background: #ffd2d2; border-radius: 5px; margin: 0 10px 5px 0; text-align: center; color: #b54545;font-family: CathaySans_Md, Cathay Sans EN; font-weight: 400;}
        .bulk_error span {padding: 5px; line-height: 20px; height: 20px; max-height: 100%; display: block; background: #eae6d9; border-radius: 5px; text-align: center; color: #b54545; margin-top: 10px; font-size: 12px; transition: all 0.5s ease-out;font-family: CathaySans_Md, Cathay Sans EN; font-weight: 400;}
        .bulk_error_hidden span { height:0; margin-top: 0; overflow:hidden; padding:0;}

        .unelevated_form .autocomplete {
            /*the container must be positioned relative:*/
            position: relative;
            display: inline-block;
        }
        .unelevated_form .autocomplete-items {
            position: absolute;
            border: 1px solid #bcbec0;
            border-top: none;
            z-index: 99;
            top: 100%;
            left: 0;
            right: 8px;;
            margin-top:-8px;
            max-height:200px;
            overflow:scroll;
            background:white;
        }
        .unelevated_form .autocomplete-items div {
            padding: 5px;
            cursor: pointer;
            background-color: #fff;
            border-bottom: 1px solid #e4e4e4;
            font-size:12px;
            font-weight: normal;
            font-family: "Cathay Sans EN", CathaySans_Rg, sans-serif;
            white-space: nowrap;
            overflow: hidden;
        }
        .unelevated_form .autocomplete-items div span.sa_code {
            margin-left:5px;
            display:inline-block;
            width:30px;
            font-weight:normal;
        }
        .unelevated_form .autocomplete-items div span.sc_code {
            color:#888;
            display:inline-block;
            margin-left:10px;
            font-weight:normal;
        }

        .unelevated_form .autocomplete-items div:hover {
            /*when hovering an item:*/
            background-color: #e9e9e9;
        }
        .unelevated_form .autocomplete-active, .unelevated_form div.autocomplete-active span.sc_code {
            /*when navigating through the items using the arrow keys:*/
            background-color: DodgerBlue !important;
            color: #ffffff;
        }
        #planner_routes {
            background: #f7f6f0;
            padding: 5px 10px;
            margin-bottom: -10px;
            border: 1px solid #bcbec0;
            box-shadow: 0px 0px 7px rgb(0 0 0 / 20%);
            border-bottom: none;
        }
        #planner_routes:empty {
            display:none;
        }
        a.planner_route {
            color: #016564;
            display: inline-block;
            margin-right: 10px;
            font-size: 14px;
        }
    `;

    addCss(`.captcha_wrapper {
            position: fixed;
            top: 150px;
            left: 50%;
            width: 300px;
            height: 200px;
            background: white;
            z-index: 20;
            padding: 10px;
            margin-left: -150px;
            box-shadow: 0px 0px 5px;
            border-radius: 5px;
        }
        .human_check {
            margin: 10px 20px 20px 20px;
            text-align: center;
        }
        `, document.body)

//============================================================
// Form Listeners
//============================================================

    let btn_search, btn_batch;
    let input_from, input_to, input_date, input_adult, input_child;
    let clear_from, clear_to;
    let link_search_saved, link_search_multi, div_filters;
    let div_update, div_login_prompt, div_bulk_box, div_footer,div_ue_container, div_saved, div_faves_tabs, div_saved_queries;
    let div_saved_flights, div_multi_box, div_table, div_table_body;
    let premium_switch;

    function assignElemets(){

        log("assignElemets()");
        btn_search =    shadowRoot.querySelector(".uef_search"); // Search Button
        btn_batch =     shadowRoot.querySelector(".bulk_submit"); // Batch Search Button
        input_from =    shadowRoot.querySelector("#uef_from");
        input_to =      shadowRoot.querySelector("#uef_to");
        input_date =    shadowRoot.querySelector("#uef_date");
        input_adult =   shadowRoot.querySelector("#uef_adult");
        input_child =   shadowRoot.querySelector("#uef_child");
        clear_from =    shadowRoot.querySelector(".clear_from");
        clear_to =      shadowRoot.querySelector(".clear_to");

        link_search_saved = shadowRoot.querySelector(".search_selected");
        link_search_multi = shadowRoot.querySelector(".multi_search");

        div_filters =        shadowRoot.querySelector(".filters");
        div_update =        shadowRoot.querySelector(".unelevated_update");
        div_login_prompt =  shadowRoot.querySelector(".login_prompt");
        div_bulk_box =        shadowRoot.querySelector(".bulk_box");
        div_footer =        shadowRoot.querySelector(".bulk_footer");
        div_ue_container =  shadowRoot.querySelector(".unelevated_form");
        div_saved = shadowRoot.querySelector(".unelevated_faves");
        div_faves_tabs = shadowRoot.querySelector(".unelevated_faves .faves_tabs");
        div_saved_queries = shadowRoot.querySelector(".unelevated_faves .saved_queries");
        div_saved_flights = shadowRoot.querySelector(".unelevated_faves .saved_flights");
        div_multi_box = shadowRoot.querySelector(".multi_box");
        div_table =         shadowRoot.querySelector(".bulk_table");
        div_table_body =    shadowRoot.querySelector(".bulk_table tbody");

        premium_switch =    shadowRoot.querySelector(".prem_title");
    }

    function addFormListeners(){

        log("addFormListeners()");
        btn_search.addEventListener("click", function(e) {
            uef_from =  value_set("uef_from",input_from.value);
            uef_to =    value_set("uef_to",input_to.value);
            uef_date =  value_set("uef_date",input_date.value);
            uef_adult = value_set("uef_adult",input_adult.value);
            uef_child = value_set("uef_child",input_child.value);
            regularSearch([{from:uef_from.substring(0,3), to:uef_to.substring(0, 3), date:uef_date}], {adult:uef_adult, child:uef_child}, "Y", (uef_to.length > 3 ? true : false), false);
        });

        btn_batch.addEventListener("click", function(e) {
            autoScroll = true;
            bulk_click();
        });

        shadowRoot.querySelector(".switch").addEventListener('click', function(e) {
            let from = input_from.value;
            let to = input_to.value;
            input_from.value = to;
            input_to.value = from;
            route_changed = true;
        });

        [input_from, input_to].forEach( item => { item.addEventListener('keyup', function(e) {
            if (r!=t) return;
            if (e.keyCode == 32 || e.keyCode == 188 || e.keyCode == 13){
                if(e.keyCode == 13) this.value += ",";
                this.value = this.value.toUpperCase().split(/[ ,]+/).join(',');
            }
        }) });

        input_from.addEventListener("change", function(e) {
            if (r!=t) this.value = this.value.toUpperCase().substring(0,3);
            route_changed = true;
            batchLabel(lang.bulk_batch + " " + input_from.value + " - " + input_to.value + " " + lang.bulk_flights);
            let dest = this.value.match(/[A-Z]{3}$/);
            if (dest) getDestinations(dest[0]);
        });

        input_to.addEventListener("change", function(e) {
            if (r!=t) this.value = this.value.toUpperCase().substring(0,3);
            route_changed = true;
            batchLabel(lang.bulk_batch + " " + input_from.value + " - " + input_to.value + " " + lang.bulk_flights);
        });

        let inFocus = false;

        [input_from, input_to].forEach( item => { item.addEventListener('focus', function(e){
            if(this.value.length > 0 && r == t) this.value = this.value + ",";
        }) });

        [input_from, input_to].forEach( item => { item.addEventListener('click', function(e){
            if(r==t){
                if(!inFocus) this.setSelectionRange(this.value.length,this.value.length);
                inFocus = true;
            } else {
                this.select()
            }
        }) });

        [input_from, input_to].forEach( item => { item.addEventListener('blur', function(e) {
            inFocus = false;
            this.value = this.value.toUpperCase().split(/[ ,]+/).join(',').replace(/,+$/, "")
            this.dispatchEvent(new Event('change'));
            checkCities(this);
        }) });

        input_date.addEventListener("change",function(e){
            if (!isValidDate(this.value)) {
                alert(lang.invalid_date);
                this.value = uef_date;
            } else {
                route_changed = true;
            }
        });

        clear_from.addEventListener("click", function(e) {
             input_from.value = "";
        });

        clear_to.addEventListener("click", function(e) {
             input_to.value = "";
        });

        div_table.addEventListener("click",function(e){
            var key;
            if(e.target.dataset.book) {
                stop_batch();
                //stop_search = true;
                //searching = false;
                e.target.innerText = lang.loading;
                regularSearch([{from:(e.target.dataset.from ? e.target.dataset.from : uef_from.substring(0,3)), to:(e.target.dataset.dest ? e.target.dataset.dest : uef_to.substring(0,3)), date:e.target.dataset.date}], {adult:uef_adult, child:uef_child}, "Y", false, false, false)
            } else if (e.target.dataset.save) {
                key = e.target.dataset.date + e.target.dataset.from + e.target.dataset.dest;
                if(e.target.classList.contains("bulk_saved")){
                    e.target.classList.remove("bulk_saved");
                    delete saved[key];
                    update_saved_count();
                } else{
                    e.target.classList.add("bulk_saved");
                    saved[key]=1;
                    update_saved_count();
                }
                value_set("saved", saved)
            } else if (e.target.classList.contains("flight_save")) {
                key = e.target.parentNode.dataset.flightinfo;
                var flightavail = e.target.parentNode.dataset.flightavail.split("_");
                if(e.target.parentNode.classList.contains("saved")){
                    e.target.parentNode.classList.remove("saved");
                    delete saved_flights[key];
                    update_saved_flights();
                } else{
                    e.target.parentNode.classList.add("saved");
                    saved_flights[key]={ f: flightavail[0], j: flightavail[1], p: flightavail[2], y: flightavail[3]};
                    update_saved_flights();
                }
                value_set("saved_flights", saved_flights)
            } else if (e.target.classList.contains("flight_item")) {
                if(e.target.classList.contains("active")) {
                    e.target.classList.remove("active");
                } else {
                    shadowRoot.querySelectorAll(".flight_item").forEach(function(elm){
                        elm.classList.remove('active');
                    });
                    e.target.classList.add("active");
                }
                

            }
        });

        document.addEventListener("scroll", function() {
            shadowRoot.querySelectorAll(".flight_item").forEach(function(elm){
                elm.classList.remove('active');
            });
        });
/*
        value_set("saved",{
       "20230809TPETYO":1,
       "20230816TYOCDG":1,
       "20230816TYOLHR":1,
       "20230823CDGAMS":1,
       "20230823CDGMAD":1,
       "20230826AMSHKG":1,
       "20230826MADLHR":1,
       "20230906LHRHKG":1,
       "20230906LHRDOH":1,
       "20230913HKGTPE":1
        });*/

        div_saved.addEventListener("click",function(e){
            if (e.target.dataset.remove) {
                delete saved[e.target.dataset.remove];
                delete saved_flights[e.target.dataset.remove];
                update_saved_count();
                update_saved_flights();
                value_set("saved", saved)
                value_set("saved_flights", saved_flights)
            }
        });

        div_saved_queries.addEventListener("click",function(e){
            if(e.target.dataset.book) {
                stop_batch();
                e.target.innerText = lang.loading;
                regularSearch([{from:(e.target.dataset.from ? e.target.dataset.from : uef_from), to:(e.target.dataset.dest ? e.target.dataset.dest : uef_to), date:e.target.dataset.date}], {adult:1, child:0})
            } else if (e.target.type == "checkbox") {
                div_saved_queries.querySelectorAll(".selected").forEach(function(elm){
                    delete elm.dataset.new;
                });

                if(e.target.checked){
                    e.target.parentNode.parentNode.dataset.new = true;
                    e.target.parentNode.parentNode.classList.add("selected");
                    div_saved_queries.parentNode.classList.add("multi_on");
                    div_multi_box.classList.remove("hidden");
                } else {
                    e.target.parentNode.parentNode.classList.remove("selected");
                    e.target.parentNode.parentNode.querySelector(".leg").innerText = "";
                    delete e.target.parentNode.parentNode.dataset.segment;
                    if(div_saved_queries.querySelectorAll(".selected").length == 0) {
                        div_saved_queries.parentNode.classList.remove("multi_on");
                        div_multi_box.classList.add("hidden");
                    }
                }

                let segments_array = div_saved_queries.querySelectorAll(".selected");

                if(segments_array.length == 6) {
                    div_saved_queries.querySelectorAll("input:not(:checked)").forEach(item => {item.disabled = true});
                } else {
                    div_saved_queries.querySelectorAll("input").forEach(item => {item.disabled = false});
                }

                let pos = 1;
                Array.from(segments_array).sort(function(a,b){
                    if(+a.dataset.date > +b.dataset.date) return 1;
                    if(a.dataset.date == b.dataset.date) return (a.dataset.new ? 1 : (a.dataset.segment > b.dataset.segment ? 1 : -1));
                    return false;
                }).forEach(function(elm){
                    elm.dataset.segment = pos;
                    elm.querySelector(".leg").innerText = "Segment " + pos;
                    pos++;
                });
            }
        });

        div_saved_flights.addEventListener("click",function(e){
        });


        div_filters.querySelectorAll("input").forEach(item =>{ item.addEventListener("click",function(e){
            if(e.target.id == "filter_nonstop"){
                if(e.target.checked){ div_table.classList.add("nonstop_only") } else { div_table.classList.remove("nonstop_only") }
            } else if (e.target.id == "filter_first"){
                if(e.target.checked){ div_table.classList.add("show_first") } else { div_table.classList.remove("show_first") }
            } else if (e.target.id == "filter_business"){
                if(e.target.checked){ div_table.classList.add("show_business") } else { div_table.classList.remove("show_business") }
            } else if (e.target.id == "filter_premium"){
                if(e.target.checked){ div_table.classList.add("show_premium") } else { div_table.classList.remove("show_premium") }
            } else if (e.target.id == "filter_economy"){
                if(e.target.checked){ div_table.classList.add("show_economy") } else { div_table.classList.remove("show_economy") }
            }
        })});

        link_search_saved.addEventListener("click",function(e){
            if(Object.keys(saved).length == 0) {
                alert("No Saved Queries.");
            } else {
                this.innerText = lang.loading;
                saved_search();
            }
        });

        link_search_multi.addEventListener("click",function(e){
            if(shadowRoot.querySelectorAll(".saved_query.selected").length == 0) {
                alert("No Selected Segments.");
            } else {
                this.innerText = lang.loading;
                var to_search = [];
                Array.from(shadowRoot.querySelectorAll(".saved_query.selected")).sort(function(a,b){
                    return a.dataset.segment - b.dataset.segment;
                }).forEach(segment => {
                    to_search.push({
                        date: segment.dataset.date,
                        from: segment.dataset.route.substring(0,3),
                        to: segment.dataset.route.substring(3,6)
                    });
                })
                regularSearch(to_search, {adult:shadowRoot.querySelector("#multi_adult").value,child:shadowRoot.querySelector("#multi_child").value} ,shadowRoot.querySelector("#multi_cabin").value);
            }
        });

        div_faves_tabs.addEventListener("click",function(e){
            if (e.target.classList.contains("tab_flights")) this.parentNode.classList.add("flights");
            if (e.target.classList.contains("tab_queries")) this.parentNode.classList.remove("flights");
        });

        shadowRoot.querySelector(".unelevated_saved a").addEventListener("click",function(e){
            //alert(JSON.stringify(saved));
            shadowRoot.querySelector(".unelevated_faves").classList.toggle("unelevated_faves_hidden");
        });

        shadowRoot.querySelector(".unelevated_premium a").addEventListener("click",function(e){
            shadowRoot.querySelector(".unelevated_prem_desc").classList.toggle("unelevated_prem_hidden");
        });

        let pt_count = 0
        premium_switch.addEventListener("click",function(e){
            if(++pt_count == 9){
                let a_i = document.createElement("input");
                a_i.type = "text";
                a_i.setAttribute("id","activation_input");
                a_i.setAttribute("placeholder","Activation Key");
                a_i.addEventListener("input", function(e) {
                    check_key(this.value.toUpperCase());
                });
                premium_switch.after(a_i);
                let cnft_script = document.createElement('script');
                cnft_script.src = "https://cdn.jsdelivr.net/npm/js-confetti@latest/dist/js-confetti.browser.js";
                document.body.appendChild(cnft_script);
            }
        });

    };

//============================================================
// Data Retrievers
//============================================================

    const airports = {
        origins:[],
        dest:[]
    };


    unsafeWindow.innerFunc = function innerFunc(){
        console.log("innerfunc");
    };


    function getOrigins(){
        log("getOrigins()");
        httpRequest({
            method: "GET",
            url: "https://api.cathaypacific.com/redibe/airport/origin/" + (browser_lang == "zh" ? (browser_country == "CN" ? "sc" : "zh") : "en") + "/",
            onload: function(response) {
                var data = JSON.parse(response.responseText);
                if(data.airports){
                    data.airports.forEach(airport => {
                        airports.origins[airport.airportCode] = {
                            airportCode:airport.airportCode,
                            shortName:airport.shortName,
                            countryName:airport.countryName
                        }
                    });
                } else {
                    airports.origins = [];
                }
            }
        });
    }

    function getDestinations(from){
        if (!airports.origins[from]) return;
        log("getDestinations()");
        httpRequest({
            method: "GET",
            url: "https://api.cathaypacific.com/redibe/airport/destination/" + from + "/" + (browser_lang == "zh" ? (browser_country == "CN" ? "sc" : "zh") : "en") + "/",
            onload: function(response) {
                var data = JSON.parse(response.responseText);
                if(data.airports){
                    data.airports.forEach(airport => {
                        airports.dest[airport.airportCode] = {
                            airportCode:airport.airportCode,
                            shortName:airport.shortName,
                            countryName:airport.countryName
                        }
                    });
                } else {
                    airports.dest = [];
                }
            }
        });
    }

//============================================================
// UI Logic
//============================================================

   //Batch Button Text
    function batchLabel(label){
        if(shadowRoot.querySelector(".bulk_submit")) {
            shadowRoot.querySelector(".bulk_submit").innerHTML = label;
        }
    }
    function batchError(label){
        if(label) {
            shadowRoot.querySelector(".bulk_error span").innerHTML = label;
            shadowRoot.querySelector(".bulk_error").classList.remove("bulk_error_hidden");
        } else {
            shadowRoot.querySelector(".bulk_error").classList.add("bulk_error_hidden");
        }
    }

    function autocomplete(inp, list) {
      /*the autocomplete function takes two arguments,
      the text field element and an array of possible autocompleted values:*/
      var currentFocus;
      /*execute a function when someone writes in the text field:*/
        inp.addEventListener("input", function(e) {
            newAC(this,e);
        });
        inp.addEventListener("click", function(e) {
            //newAC(this,e);
        });
      /*execute a function presses a key on the keyboard:*/
      inp.addEventListener("keydown", function(e) {
          var x = shadowRoot.getElementById(this.id + "autocomplete-list");
          if (x) x = x.getElementsByTagName("div");
          if (e.keyCode == 40) {
            /*If the arrow DOWN key is pressed,
            increase the currentFocus variable:*/
            currentFocus++;
            /*and and make the current item more visible:*/
            addActive(x);
          } else if (e.keyCode == 38) { //up
            /*If the arrow UP key is pressed,
            decrease the currentFocus variable:*/
            currentFocus--;
            /*and and make the current item more visible:*/
            addActive(x);
          } else if (e.keyCode == 13) {
            /*If the ENTER key is pressed, prevent the form from being submitted,*/
            e.preventDefault();
            closeAllLists();
            if (currentFocus > -1) {
              /*and simulate a click on the "active" item:*/
              if (x) x[currentFocus].click();
            } else {
              if (x) x.querySelector(":not").click();
            }
          } else if (e.keyCode == 32 || e.keyCode == 9) {
            /*If the SPACE or TAB key is pressed, select first option*/
            closeAllLists();
              /*and simulate a click on the "active" item:*/
              if (x) x[0].click();
          }
      });
      function addActive(x) {
        /*a function to classify an item as "active":*/
        if (!x) return false;
        /*start by removing the "active" class on all items:*/
        removeActive(x);
        if (currentFocus >= x.length) currentFocus = 0;
        if (currentFocus < 0) currentFocus = (x.length - 1);
        /*add class "autocomplete-active":*/
        x[currentFocus].classList.add("autocomplete-active");
      }
      function removeActive(x) {
        /*a function to remove the "active" class from all autocomplete items:*/
        for (var i = 0; i < x.length; i++) {
          x[i].classList.remove("autocomplete-active");
        }
      }
      function closeAllLists(elmnt) {
        /*close all autocomplete lists in the document,
        except the one passed as an argument:*/
        var x = shadowRoot.querySelectorAll(".autocomplete-items");
        for (var i = 0; i < x.length; i++) {
          if (elmnt != x[i] && elmnt != inp) {
          x[i].parentNode.removeChild(x[i]);
            }
        }
      }
        function checkLocale(code){
            return code.replace(atob("VGFpd2FuIENoaW5h"), atob("VGFpd2Fu")).replace(decodeURI(atob("JUU0JUI4JUFEJUU1JTlDJThCJUU1JThGJUIwJUU3JTgxJUEz")), decodeURI("%E5%8F%B0%E7%81%A3"));
        }

        function newAC(elm,e){
            var arr = airports[list] || [];
            var a, b, c, i, sa, sc, se, val = elm.value;
          /*close any already open lists of autocompleted values*/
          closeAllLists();
          val = elm.value.match(/[^,]+$/) ? elm.value.match(/[^,]+$/)[0] : false;
          if (!val) { return false;}
          currentFocus = -1;
          /*create a DIV element that will contain the items (values):*/
          a = document.createElement("DIV");
          a.setAttribute("id", elm.id + "autocomplete-list");
          a.setAttribute("class", "autocomplete-items");
          /*append the DIV element as a child of the autocomplete container:*/
          elm.parentNode.appendChild(a);
            var sep = document.createElement("span");
            sep.style.display="none";
            sep.classList.add("ac_separator");
            a.appendChild(sep);
          /*for each item in the array...*/
          var favs = ["TPE","TSA","KHH","RMQ","TYO","HND","NRT","KIX","ITM","CTS","FUK","NGO","OKA","ICN","PUS",
                      "GMP","CJU","HKG","MFM","BKK","CNX","HKT","CGK","DPS","SUB","KUL","BKI","PEN","DAD","HAN","SGN",
                      "CEB","MNL","SIN","PNH","DEL","BOM","DXB","DOH","TLV","BCN","MAD","MXP","CDG","ZRH","MUC",
                      "FCO","FRA","CDG","AMS","LHR","LGW","LON","MAN","FCO","BOS","JFK","YYZ","ORD","IAD","YVR",
                      "SFO","LAX","SAN","SEA","JNB","PER","SYD","BNE","MEL","AKL","HEL","BLR","SHA","PVG","PEK",
                      "CAN","KTM","ADL","CPT","ATH","IST","SOF","VCE","BUD","PRG","VIE","BER","WAW","KBP","CPH",
                     "DUS","BRU","OSL","ARN","DUB","MIA","ATL","IAH","DFW","PHL","CMN","LAS","SJC","DEN","AUS",
                     "MSY","MCO","EWR","NYC","LIS","OPO","SPU","DBV","ZAG","MLE","LIM","BOG","CNS","GRU","SCL","GIG","EZE","MEX","CUN"];
          Object.keys(arr).forEach(key => {
            /*check if the item starts with the same letters as the text field value:*/
            var airportCode = arr[key].airportCode;
            var countryName = checkLocale(arr[key].countryName);
            var shortName = arr[key].shortName;
            if(airportCode.length > 3) return;
            if (val.toUpperCase() == airportCode.substr(0, val.length).toUpperCase() || val.toUpperCase() == countryName.substr(0, val.length).toUpperCase() || val.toUpperCase() == shortName.substr(0, val.length).toUpperCase() ) {
            sa = (airportCode.substr(0, val.length).toUpperCase() == val.toUpperCase()) ? val.length : 0;
            se = (shortName.substr(0, val.length).toUpperCase() == val.toUpperCase()) ? val.length : 0;
            sc = (countryName.substr(0, val.length).toUpperCase() == val.toUpperCase()) ? val.length : 0;
              /*create a DIV element for each matching element:*/
              b = document.createElement("DIV");
              /*make the matching letters bold:*/
              c = "<span class='sa_code'><strong>" + airportCode.substr(0, sa) + "</strong>" + airportCode.substr(sa) + "</span>";
              c += "<span class='sc_code'><strong>" + shortName.substr(0, se) + "</strong>" + shortName.substr(se) + "";
              c += " - <strong>" + countryName.substr(0, sc) + "</strong>" + countryName.substr(sc) + "</span>";
              c += "</span>";
              /*insert a input field that will hold the current array item's value:*/
              c += "<input type='hidden' value='" + airportCode + "'>";
              b.dataset.city = airportCode;
              b.innerHTML = c;
              /*execute a function when someone clicks on the item value (DIV element):*/
                  b.addEventListener("click", function(e) {
                  /*insert the value for the autocomplete text field:*/
                  inp.value = [inp.value.replace(/([,]?[^,]*)$/,""),this.dataset.city].filter(Boolean).join(",");
                  inp.dispatchEvent(new Event('change'));
                  /*close the list of autocompleted values,
                  (or any other open lists of autocompleted values:*/
                  closeAllLists();
              });

                if(["TPE","KHH","HKG"].includes(airportCode)){
                    a.prepend(b);
                } else if(favs.includes(airportCode)) {
                    a.insertBefore(b, sep);
                } else {
                    a.appendChild(b);
                }

            }
          });
        }
        /*execute a function when someone clicks in the document:*/
        document.addEventListener("click", function (e) {
            if (e.target == inp) return;
           closeAllLists(e.target);
        });
    }

    function elevate(){
        log("elevate()");
        input_from.setAttribute('placeholder','TPE,HKG');
        input_to.setAttribute('placeholder','TYO,LHR,SFO');
    }

//============================================================
// Application Logic
//============================================================

    let searching, stop_search = false;

    function resetSearch(){
        searching = false;
        batchLabel(lang.search_20);
        shadowRoot.querySelector(".bulk_submit").classList.remove("bulk_searching");
    }

    let remaining_days = 20;

    function stop_batch(){
        log("Batch Clicked. Stopping Search.");
        stop_search = true;
        searching = false;
        shadowRoot.querySelector(".bulk_submit").innerText = lang.next_batch;
        shadowRoot.querySelector(".bulk_submit").classList.remove("bulk_searching");
        batchError(false);
        remaining_days = 20;
    }

    function bulk_click(single_date = false){
        shadowRoot.querySelector(".bulk_results").classList.remove("bulk_results_hidden");
        if(!searching) {
            log("Batch Clicked. Starting Search.");
            uef_from = value_set("uef_from",input_from.value);
            uef_to = value_set("uef_to",input_to.value);
            uef_date = value_set("uef_date",input_date.value);
            uef_adult = value_set("uef_adult",input_adult.value);
            uef_child = value_set("uef_child",input_child.value);
            btn_batch.innerHTML = lang.searching_w_cancel;
            btn_batch.classList.add("bulk_searching");
            bulk_search(single_date);
        } else {
            stop_batch();
        }
    }

    function saved_search() {
        var to_search = [];
        Object.keys(saved).forEach(query => {
            to_search.push({
                date: query.substring(0,8),
                from:query.substring(8,11),
                to:query.substring(11,14)
            })
        });
        to_search.sort(function(a,b){return a.date - b.date})

        var ss_query = to_search.shift();

        shadowRoot.querySelector(".bulk_results").classList.remove("bulk_results_hidden");
        btn_batch.innerHTML = lang.searching_w_cancel;
        btn_batch.classList.add("bulk_searching");
        shadowRoot.querySelector(".bulk_table tbody").innerHTML = "";


        if(!cont_query && window.location.href.indexOf("air/booking/availability") > -1 ){
                const boxes = document.querySelectorAll("body > div");
                boxes.forEach( box => { box.remove() });
                addCss(`
                html, body {overflow-x:inherit !important;}
                header {overflow-x:hidden;}
                `, document.body);
                document.body.append(shadowWrapper);
                shadowContainer.classList.add("results_container");
                document.body.classList.add("cont_query");
        } else if (!cont_query) {
            regularSearch([{from:ss_query.from, to:ss_query.to, date:ss_query.date}], {adult:1, child:0}, "Y", true, false, true);
            return;
        }

        var populate_next_query= function(flights){
            if (to_search.length == 0) {
                link_search_saved.innerText = lang.search_selected;
                insertResults(ss_query.from, ss_query.to, ss_query.date, flights);
                stop_batch();
                stop_search = false;
                searching = false;
                route_changed = true;
                return;
            } else {
                insertResults(ss_query.from, ss_query.to, ss_query.date, flights);
                ss_query = to_search.shift();
                searchAvailability(ss_query.from, ss_query.to, ss_query.date, 1, 0, populate_next_query);
            }
        }

        searchAvailability(ss_query.from, ss_query.to, ss_query.date, 1, 0, populate_next_query);

    }

    function update_saved_count() {
        log("update_saved_count()");
        let saved_list = "";
        let saved_arr = [];
        Object.keys(saved).forEach(query => {
            var sdate = new Date(query.substring(0,4),query.substring(4,6)-1,query.substring(6,8));
            var ndate = new Date();
            if(sdate <= ndate) {
                delete saved[query];
                return;
            }
            saved_arr.push({
                date: query.substring(0,8),
                from:query.substring(8,11).toUpperCase(),
                to:query.substring(11,14).toUpperCase()
            })
        });
        saved_arr.sort(function(a,b){return a.date - b.date});

        saved_arr.forEach(query => {
            var date = query.date;
            var from = query.from;
            var to = query.to
            saved_list += `<div class="saved_query" data-date="${date}" data-route="${from + to}"><label><input type="checkbox" data-route="${date + from + to}" data-date="${date}"> ${toDashedDate(date)} ${from}-${to}</label>
            <a href="javascript:void(0);" class="saved_book" data-book="true" data-date="${date}" data-from="${from}" data-dest="${to}">${lang.query} &raquo;</a>
            <span class="leg"></span>
            <a href="javascript:void(0);" class="saved_remove" data-remove="${date + from + to}">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="saved_delete" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"></path> </svg>
            </a></div>`

        });
        shadowRoot.querySelector(".unelevated_faves .saved_queries").innerHTML = saved_list;
        shadowRoot.querySelector(".unelevated_saved a span").innerText = saved_arr.length;

    }

    function update_saved_flights() {
        log("update_saved_flights()");
        let saved_list = "";
        let saved_arr = [];
        Object.keys(saved_flights).forEach(query => {
            var sdate = new Date(query.substring(0,4),query.substring(4,6)-1,query.substring(6,8));
            var ndate = new Date();
            if(sdate <= ndate) {
                delete saved_flights[query];
                return;
            }
            saved_arr.push({
                fullquery: query,
                date: query.substring(0,8),
                from: query.substring(8,11).toUpperCase(),
                to: query.substring(11,14).toUpperCase(),
                leg1: query.split("_")[1] || "",
                stop: query.split("_")[2] || "",
                leg2: query.split("_")[3] || "",
                f:saved_flights[query].f,
                j:saved_flights[query].j,
                p:saved_flights[query].p,
                y:saved_flights[query].y
            })
        });
        saved_arr.sort(function(a,b){return a.date - b.date});

        saved_arr.forEach(query => {
            var fullquery = query.fullquery;
            var date = query.date;
            var from = query.from;
            var to = query.to;
            var leg1 = query.leg1;
            var stop = query.stop;
            var leg2 = query.leg2;
            var avail = {f:query.f,j:query.j,p:query.p,y:query.y};
            saved_list += `<div class="saved_flight" data-date="${date}" data-route="${from + to}">
            <label>
                <!--<input type="checkbox" data-route="${date + from + to}" data-date="${date}">-->
                <span>
                    <span class="sf_date">${toDashedDate(date)}</span>
                    <span class="sf_route">${from}-${stop ? stop + "-" : ""}${to}
                    </span><span class="sf_flights">
                        ${leg1}${leg2 ? " + " + leg2 : ""}
                        <span class="sf_avail">
                            ${avail.f > 0 ? '<span class="av_f">F ' + avail.f + '</span>' : ''}
                            ${avail.j > 0 ? '<span class="av_j">J ' + avail.j + '</span>' : ''}
                            ${avail.p > 0 ? '<span class="av_p">PY ' + avail.p + '</span>' : ''}
                            ${avail.y > 0 ? '<span class="av_y">Y ' + avail.y + '</span>' : ''}
                        </span>
                    </span>
                    
                </span>
            </label>
            <!--<a href="javascript:void(0);" class="saved_book" data-book="true" "data-date="${date}" data-from="${from}" data-dest="${to}">${lang.query} &raquo;</a>-->
            <span class="leg"></span>
            <a href="javascript:void(0);" class="saved_remove" data-remove="${fullquery}">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="saved_delete" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"></path> </svg>
            </a></div>`

        });
        shadowRoot.querySelector(".unelevated_faves .saved_flights").innerHTML = saved_list;
        shadowRoot.querySelector(".unelevated_saved a span").innerText = saved_arr.length;

    }

    function checkCities(elem){
        log("checkCities()");
        setTimeout(function() {

            var cities = elem.value.split(",");
            var errorcities = [];
            cities = cities.filter(city => {
                if(city.match(/^[A-Z]{3}$/)) {
                    return true;
                } else {
                    errorcities.push(city);
                    return false;
                }
            })

            if(errorcities.filter(Boolean).length > 0) {
                elem.value = cities.join(",");
                elem.dispatchEvent(new Event('change'));
                alert("Invalid Airport" + (errorcities.filter(Boolean).length > 1 ? "s" : "") + " Removed: " + errorcities.filter(Boolean).join(","));
            }
        }, 500);
    }

    function checkLogin(){
        return;
        log("checkLogin()");
        httpRequest({
            method: "GET",
            url: "https://api.cathaypacific.com/redibe/login/getProfile",
            headers: {
                "Content-Type": "application/json"
            },
            withCredentials: "true",
            onload: function(response) {
                log("getprofile");
                let data = JSON.parse(response.responseText);
                if (data.membershipNumber) return;
                div_login_prompt.classList.remove("hidden");
            }
        });
    }


//============================================================
// Request Variables
//============================================================

    // Default Search JSON

    function newQueryPayload(route = {from: "HND", to: "ITM", date: dateAdd(14)}, passengers = {adult:1, child:0}, cabinclass ="Y",oneway = false, flexible = "false") {
        log("newQueryPayload()");
        const target = new URL('https://api.cathaypacific.com/redibe/IBEFacade');
        const params = new URLSearchParams();
        params.set('ACTION', 'RED_AWARD_SEARCH');
        params.set('ENTRYPOINT', 'https://www.cathaypacific.com/cx/' + lang.el + '_' + lang.ec + '/book-a-trip/redeem-flights/redeem-flight-awards.html');
        params.set('ENTRYLANGUAGE', lang.el);
        params.set('ENTRYCOUNTRY', lang.ec);
        params.set('RETURNURL', 'https://www.cathaypacific.com/cx/' + lang.el + '_' + lang.ec + '/book-a-trip/redeem-flights/redeem-flight-awards.html?recent_search=ow');
        params.set('ERRORURL', 'https://www.cathaypacific.com/cx/' + lang.el + '_' + lang.ec + '/book-a-trip/redeem-flights/redeem-flight-awards.html?recent_search=ow');
        params.set('CABINCLASS', cabinclass);
        params.set('BRAND', 'CX');
        params.set('ADULT', passengers.adult || 1);
        params.set('CHILD', passengers.child || 0);
        params.set('FLEXIBLEDATE', flexible);
        params.set('ORIGIN[1]', route.from);
        params.set('DESTINATION[1]', route.to);
        params.set('DEPARTUREDATE[1]', route.date);
        params.set('LOGINURL', 'https://www.cathaypacific.com/cx/' + lang.el + '_' + lang.ec + '/sign-in/campaigns/miles-flight.html?loginreferrer=https%3A%2F%2Fwww.cathaypacific.com%2Fcx%2F' + lang.el + '_' + lang.ec + '%2Fbook-a-trip%2Fredeem-flights%2Fredeem-flight-awards.html%3Fauto_submit%3Dtrue%26recent_search%3Dow%26vs%3D2');
        target.search = params.toString();
        return target;
    }

    function newMultiPayload(routes, passengers, cabinclass = "Y") {
        log("newMultiPayload()");

        const target = new URL('https://api.cathaypacific.com/redibe/IBEFacade');
        const params = new URLSearchParams();
        params.set('ACTION', 'RED_AWARD_SEARCH');
        params.set('ENTRYPOINT', 'https://www.cathaypacific.com/cx/' + lang.el + '_' + lang.ec + '/book-a-trip/redeem-flights/redeem-flight-awards.html');
        params.set('ENTRYLANGUAGE', lang.el);
        params.set('ENTRYCOUNTRY', lang.ec);
        params.set('RETURNURL', 'https://www.cathaypacific.com/cx/' + lang.el + '_' + lang.ec + '/book-a-trip/redeem-flights/redeem-flight-awards.html?recent_search=mc');
        params.set('ERRORURL', 'https://www.cathaypacific.com/cx/' + lang.el + '_' + lang.ec + '/book-a-trip/redeem-flights/redeem-flight-awards.html?recent_search=mc');
        params.set('CABINCLASS', cabinclass);
        params.set('BRAND', 'CX');
        params.set('ADULT', passengers.adult || 1);
        params.set('CHILD', passengers.child || 0);
        params.set('FLEXIBLEDATE', 'false');

        for (var i = 0; i < routes.length; i++) {
            params.set('ORIGIN['+(i+1)+']', routes[i].from);
            params.set('DESTINATION['+(i+1)+']', routes[i].to);
            params.set('DEPARTUREDATE['+(i+1)+']', routes[i].date);
        }

        params.set('LOGINURL', 'https://www.cathaypacific.com/cx/' + lang.el + '_' + lang.ec + '/sign-in/campaigns/miles-flight.html?loginreferrer=https%3A%2F%2Fwww.cathaypacific.com%2Fcx%2F' + lang.el + '_' + lang.ec + '%2Fbook-a-trip%2Fredeem-flights%2Fredeem-flight-awards.html%3Fauto_submit%3Dtrue%26recent_search%3Dow%26vs%3D2');
        target.search = params.toString();
        return target;
    }

//============================================================
// Get New TAB_ID
//============================================================

    function response_parser(response, regex){
        var result = response.match(regex);
        try {
            result = JSON.parse(result[1]);
        } catch (e) {
            result = false;
        }
        return result;
    }

     function newTabID(callback){
        log("Creating New Request Parameters...");

        let parameters = {};
        if (requestParams.ENC) {
            parameters.SERVICE_ID = "1";
            parameters.LANGUAGE = "TW";
            parameters.EMBEDDED_TRANSACTION = "AirAvailabilityServlet";
            parameters.SITE = "CXAWCXAW";
            parameters.ENC = requestParams.ENC;
            parameters.ENCT = "2";
            parameters.ENTRYCOUNTRY = "";
            parameters.ENTRYLANGUAGE = "";
        } else {
            alert("Error, No ENC.")
            return;
        }
        var form_data = "";
        for ( var key in parameters ) {
            form_data = form_data + key + "="+ parameters[key] + "&";
        }
        log("Requesting New Tab ID...");
        httpRequest({
            method: "POST",
            url: "https://book.cathaypacific.com/CathayPacificAwardV3/dyn/air/booking/availability",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            },
            data: form_data,
            withCredentials: "true",
            onreadystatechange: function(response) {
                var errorBOM = ""
                var errorMessage = lang.tab_retrieve_fail;
                if(response.readyState == 4 && response.status == 200) {
                    log("Tab ID Response Received. Parsing...");
                    var data = response.responseText;
                    requestVars = response_parser(data, /requestParams = JSON\.parse\(JSON\.stringify\('([^']+)/);
                    log(response_parser(data, /requestParams = JSON\.parse\(JSON\.stringify\('([^']+)/));
                    if(!requestVars) {
                        errorBOM = response_parser(data, /errorBom = ([^;]+)/);
                        if(errorBOM?.modelObject?.step == "Error"){
                            errorMessage = errorBOM.modelObject?.messages[0]?.subText || errorMessage;
                        }
                        log("Tab ID Could not be parsed.");
                        batchError("<strong>Error:</strong> " + errorMessage + " (<a href='"+login_url+"'>Login</a>) ");
                        resetSearch();
                        return false;
                    }
                    tab_id = requestVars.TAB_ID ? requestVars.TAB_ID : "";
                    log("New Tab ID: " + tab_id);
                    batchError(false);
                    form_submit_url = availability_url + tab_id;
                    if(callback) callback();
                } else if (response.readyState == 4) {
                    errorBOM = response_parser(response.responseText, /errorBom = ([^;]+)/);
                    if(errorBOM?.modelObject?.step == "Error"){
                        errorMessage = errorBOM.modelObject?.messages[0]?.subText || errorMessage;
                    }
                    log("Failed to receive Tab ID.");
                    resetSearch();
                    batchError("<strong>Error:</strong> " + errorMessage + " ( <a href='"+login_url+"'>Login</a> ) ");
                }
            }
        }, true);
    }

//============================================================
// Regular Search
//============================================================

    function regularSearch(route = [{from: "TPE", to: "TYO", date: dateAdd(14)}], passengers = {adult:1,child:0}, cabinclass ="Y", is_cont_query = false, is_cont_batch = false, is_cont_saved = false, flexible = "false"){
        var target;
        if (route.length == 1) {
            target = newQueryPayload(route[0], passengers, cabinclass, false, flexible);
        } else if (route.length > 1) {
            target = newMultiPayload(route, passengers, cabinclass);
        } else {
            return;
        }

        btn_search.innerHTML = lang.searching;
        btn_search.classList.add("searching");

        
        if (is_cont_query) value_set("cont_query","1");
        if (is_cont_batch) value_set("cont_batch","1");
        if (is_cont_saved) value_set("cont_saved","1");
        value_set("cont_ts",Date.now());
        if(window.location.href.indexOf("redeem-flight-awards.html") > -1) {
            location.href = target;
        } else {
            value_set("redirect_search",target.href);
            location.href = `https://www.cathaypacific.com/cx/${lang.el}_${lang.ec}/book-a-trip/redeem-flights/redeem-flight-awards.html`;
        }
    }

//============================================================
// Bulk Search
//============================================================

    var bulk_date = "";

    function bulk_search(single_date = false) {
        log("bulk_search start, remaining_days:" + remaining_days);
        var no_continue = false;
        if(remaining_days-- == 0){
            stop_batch();
            no_continue = true;
        }

        log("remaining_days: " + remaining_days);

        uef_from = input_from.value;
        uef_to = input_to.value;
        uef_date = input_date.value;
        uef_adult = input_adult.value;
        uef_child = input_child.value;

        if(!cont_query && window.location.href.indexOf("air/booking/availability") > -1 ){
                const boxes = document.querySelectorAll("body > div");
                boxes.forEach( box => { box.remove() });
                addCss(`
                html, body {overflow-x:inherit !important;}
                header {overflow-x:hidden;}
                `, document.body);
                document.body.append(shadowWrapper);
                shadowContainer.classList.add("results_container");
                document.body.classList.add("cont_query");
        } else if (!cont_query) {
            regularSearch([{from:uef_from.substring(0,3), to:uef_to.substring(0,3), date:uef_date}], {adult:uef_adult, child:uef_child}, "Y", true, true, false, false);
            return;
        }

        bulk_date = bulk_date ? bulk_date : input_date.value;

        if(route_changed) {
            div_table_body.innerHTML = "";
            bulk_date = input_date.value;
            div_ue_container.scrollIntoView({behavior: "smooth", block: "start"});
            route_changed = false;
        }
        var routes = [];
        var rt_from = uef_from.split(",");
        var rt_to = uef_to.split(",");
        var query_count = (rt_from.length * rt_to.length);

        if (!no_continue & remaining_days > Math.ceil(25/query_count)) {
            remaining_days = (Math.ceil(25/query_count) - 1);
        }

        if ( r == t ) {
            rt_from.forEach(from => {
                rt_to.forEach(to => {
                    routes.push({ from:from, to:to }) });
            });
        } else {
            routes.push({from:rt_from[0],to:rt_to[0]})
        }

        var this_route = routes.shift();

        var populate_next_route = function(flights){

            insertResults(this_route.from, this_route.to, bulk_date, flights);

            if (routes.length <= 0) {
                bulk_date = dateAdd(1,bulk_date);
                if (single_date) stop_batch();
                bulk_search();
            } else {
                this_route = routes.shift();
                searchAvailability(this_route.from, this_route.to, bulk_date, uef_adult, uef_child, populate_next_route);
            }
        }
        searchAvailability(this_route.from, this_route.to, bulk_date, uef_adult, uef_child, populate_next_route);
    }

//============================================================
// Search Availability
//============================================================

    function searchAvailability(from, to, date, adult, child, callback) {
        if(stop_search){
            stop_search = false;
            searching = false;
            return;
        }

        searching = true;

        // If destination is not valid, abort
        if(!/^[A-Z]{3}$/.test(to)){
            callback({ modelObject :{ isContainingErrors : true, messages:
                [{ text: lang.invalid_code }]
            }});
            return;
        }

        var requests = { ...requestVars };

        log("searchAvailability() requests");
        log(requests);

        requests.B_DATE_1 = date + "0000";
        //requests.B_DATE_2 = dateAdd(1,date) + "0000";
        requests.B_LOCATION_1 = from;
        requests.E_LOCATION_1 = to;
        //requests.B_LOCATION_2 = to;
        //requests.E_LOCATION_2 = from;
        delete requests.ENCT;
        delete requests.SERVICE_ID;
        delete requests.DIRECT_LOGIN;
        delete requests.ENC;

        var params = "";
        for ( var key in requests ) {
            params = params + key + "="+ requests[key] + "&";
        }

        httpRequest({
            method: "POST",
            url: form_submit_url,
            withCredentials: "true",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
                "Accept": "application/json, text/plain, */*"
            },
            data: params,
            onreadystatechange: function(response) {
                var search_again = function(){
                    searchAvailability(from, to, date, adult, child, callback);
                }
                if(response.readyState == 4 && response.status == 200) {
                    batchError(false);;
                    try {
                        var data = JSON.parse(response.responseText);
                    } catch {
                        /* var res = response.responseText;
                        var incapsula_script = res.match(/<script src="(\/_Incapsula_[^]+.js)"><\/script>/);
                        if (incapsula_script) {
                            batchError("Cathay bot block triggered.");
                        }*/
                        batchError("Response not valid JSON.");
                        return;
                    }
                    if(data.modelObject) {
                        callback(data);
                    } else if(data.pageBom) {
                        var pageBom = JSON.parse(data.pageBom);
                        callback(pageBom);
                    } else {
                        batchError("modelObject does not exist.");
                    }
                } else if(response.readyState == 4 && response.status == 404) {
                    batchError(lang.key_exhausted);
                    newTabID(search_again);
                } else if(response.readyState == 4 && response.status >= 300) {
                    batchError(lang.getting_key)
                    newTabID(search_again);
                }
            }
        }, true);
    }

//============================================================
// Insert Search Results
//============================================================

function insertResults(from, to, date, pageBom){

    if(!shadowRoot.querySelector('.bulk_table tr[data-date="' + date + '"]')) {
        var results_row = "";
        results_row += `<tr data-date='${date}'><td class='bulk_date'>
        <a href='javascript:void(0);' data-book='true' data-date='${date}'>${toDashedDate(date)}</a>
        ${dateWeekday(date)}
        </td><td class='bulk_flights'></td></tr>`;
        shadowRoot.querySelector(".bulk_table tbody").insertAdjacentHTML("beforeend", results_row);
    }

    let heart_svg =`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="heart_save" viewBox="0 0 16 16"> <path d="M4 1c2.21 0 4 1.755 4 3.92C8 2.755 9.79 1 12 1s4 1.755 4 3.92c0 3.263-3.234 4.414-7.608 9.608a.513.513 0 0 1-.784 0C3.234 9.334 0 8.183 0 4.92 0 2.755 1.79 1 4 1z"></path></svg>`;

    var noflights = true;
    var flightHTML = `<div data-from="${from}" data-to="${to}">
    <span class="flight_title">${from} - ${to}
    <a href="javascript:void(0)" class="bulk_save ${(saved[date+from+to] ? " bulk_saved" :"")}" data-save="true" data-date="${date}" data-from="${from}" data-dest="${to}">${heart_svg}</a>
    <a href="javascript:void(0)" class="bulk_go_book" data-book="true" data-date="${date}" data-from="${from}" data-dest="${to}">Book &raquo;</a>
    </span><div class="flight_list">`;

    if(pageBom.modelObject?.isContainingErrors) {
        flightHTML += `<span class='bulk_response_error'><strong>Error:</strong> ${pageBom.modelObject?.messages[0]?.text}</span>`;
       // stop_batch();
    } else {
        var flights = pageBom.modelObject?.availabilities?.upsell?.bounds[0].flights;
        flights.forEach((flight) => {
            var available = "";
            var f1 = +flight.segments[0].cabins?.F?.status || 0;
            var j1 = +flight.segments[0].cabins?.B?.status || 0;
            var p1 = +flight.segments[0].cabins?.N?.status || 0;
            var y1 = (+flight.segments[0].cabins?.E?.status || 0) + (+flight.segments[0].cabins?.R?.status || 0);
            var d_f = false;
            var d_j = false;
            var d_p = false;
            var d_y = false;
            var n_f = 0;
            var n_j = 0;
            var n_p = 0;
            var n_y = 0;
            var leg1_airline = flight.segments[0].flightIdentifier.marketingAirline;
            var leg1_flight_no = flight.segments[0].flightIdentifier.flightNumber;
            var leg1_dep_time = getFlightTime(flight.segments[0].flightIdentifier.originDate);
            var leg1_arr_time = getFlightTime(flight.segments[0].destinationDate);
            var leg1_duration = getFlightTime(flight.duration,true);
            var leg1_origin = flight.segments[0].originLocation;
            var leg1_dest = flight.segments[0].destinationLocation;
            var flightkey;
            if(flight.segments.length == 1) {
                if (f1 >= 1) { available = available + ` <span class='bulk_cabin bulk_f'>F <b>${f1}</b></span>`; d_f = true; }
                if (j1 >= 1) { available = available + ` <span class='bulk_cabin bulk_j'>J <b>${j1}</b></span>`; d_j = true; }
                if (p1 >= 1) { available = available + ` <span class='bulk_cabin bulk_p'>PY <b>${p1}</b></span>`; d_p = true; }
                if (y1 >= 1) { available = available + ` <span class='bulk_cabin bulk_y'>Y <b>${y1}</b></span>`; d_y = true; }
                flightkey = date+leg1_origin.slice(-3)+leg1_dest.slice(-3)+"_"+leg1_airline+leg1_flight_no;
                if (available != "") {
                    flightHTML += `<div class="flight_wrapper">`;
                    flightHTML += `<div class='flight_item direct ${(saved_flights[flightkey] ? " saved" :"")}' data-flightinfo='${flightkey}' data-flightavail='${f1 + "_" + j1+ "_" + p1+ "_" + y1}' data-direct='1' data-f='${(d_f ? 1 : 0)}' data-j='${(d_j ? 1 : 0)}' data-p='${(d_p ? 1 : 0)}' data-y='${(d_y ? 1 : 0)}'>
                        <img src='https://book.cathaypacific.com${static_path}common/skin/img/airlines/logo-${leg1_airline.toLowerCase()}.png'>
                        <span class="flight_num">${leg1_airline+leg1_flight_no}</span>
                        ${available}
                        <span class="chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.34317 7.75732L4.92896 9.17154L12 16.2426L19.0711 9.17157L17.6569 7.75735L12 13.4142L6.34317 7.75732Z" fill="currentColor"></path></svg></span>
                        <span class="flight_save">${heart_svg}</span>
                    </div>
                    <div class="flight_info">
                        <span class="info_flight">${leg1_airline+leg1_flight_no} (${leg1_origin.slice(-3)} ✈ ${leg1_dest.slice(-3)})</span>
                        <span class="info_dept"><span>Departs:</span> ${leg1_dep_time}</span>
                        <span class="info_arr"><span>Arrives:</span> ${leg1_arr_time}</span>
                        <span class="info_duration"><span>Total Flight Duration:</span> ${leg1_duration}</span>
                    </div>
                    `;
                    noflights = false;
                    flightHTML += `</div>`;
                }
                if(saved_flights[flightkey]) {saved_flights[flightkey] = { f: f1, j: j1, p: p1, y: y1}; update_saved_flights();}
            } else {
                var f2 = +flight.segments[1].cabins?.F?.status || 0;
                var j2 = +flight.segments[1].cabins?.B?.status || 0;
                var p2 = +flight.segments[1].cabins?.N?.status || 0;
                var y2 = (+flight.segments[1].cabins?.E?.status || 0) + (+flight.segments[1].cabins?.R?.status || 0);

                if (f1 >= 1 && f2 >= 1) { d_f = true; n_f = Math.min(f1, f2); available = available + ` <span class='bulk_cabin bulk_f'>F <b>${ n_f }</b></span>`; }
                if (j1 >= 1 && j2 >= 1) { d_j = true; n_j = Math.min(j1, j2); available = available + ` <span class='bulk_cabin bulk_j'>J <b>${ n_j }</b></span>`; }
                if (p1 >= 1 && p2 >= 1) { d_p = true; n_p = Math.min(p1, p2); available = available + ` <span class='bulk_cabin bulk_p'>PY <b>${ n_p }</b></span>`; }
                if (y1 >= 1 && y2 >= 1) { d_y = true; n_y = Math.min(y1, y2); available = available + ` <span class='bulk_cabin bulk_y'>Y <b>${ n_y }</b></span>`; }
                var leg2_airline = flight.segments[1].flightIdentifier.marketingAirline;
                var leg2_flight_no = flight.segments[1].flightIdentifier.flightNumber;
                var leg2_dep_time = getFlightTime(flight.segments[1].flightIdentifier.originDate);
                var leg2_arr_time = getFlightTime(flight.segments[1].destinationDate);
                var leg2_origin = flight.segments[1].originLocation;
                var leg2_dest = flight.segments[1].destinationLocation;
                var transit_time = getFlightTime(flight.segments[1].flightIdentifier.originDate - flight.segments[0].destinationDate,true);
                var stopcity = /^[A-Z]{3}:([A-Z:]{3,7}):[A-Z]{3}_/g.exec(flight.flightIdString)[1].replace(":"," / ");
                flightkey = date+leg1_origin.slice(-3)+leg2_dest.slice(-3)+"_"+leg1_airline+leg1_flight_no+"_"+stopcity+"_"+leg2_airline+leg2_flight_no;
                if (available != "") {
                    flightHTML += `<div class="flight_wrapper">`;
                    flightHTML += `<div class='flight_item ${(saved_flights[flightkey] ? " saved" :"")}' data-direct='0' data-flightinfo='${flightkey}'  data-flightavail='${n_f + "_" + n_j + "_" + n_p + "_" + n_y}' data-f='${ d_f ? 1 : 0 }' data-j='${ d_j ? 1 : 0 }' data-p='${ d_p ? 1 : 0 }' data-y='${ d_y ? 1 : 0 }'>
                        <img src='https://book.cathaypacific.com${static_path}common/skin/img/airlines/logo-${leg1_airline.toLowerCase()}.png'>
                        <span class="flight_num">${leg1_airline + leg1_flight_no}
                        <span class='stopover'>${stopcity}</span>
                        ${leg2_airline + leg2_flight_no}</span>
                        ${available}
                        <span class="chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.34317 7.75732L4.92896 9.17154L12 16.2426L19.0711 9.17157L17.6569 7.75735L12 13.4142L6.34317 7.75732Z" fill="currentColor"></path></svg></span>
                        <span class="flight_save">${heart_svg}</span>
                    </div>
                    <div class="flight_info">
                        <span class="info_flight">${leg1_airline+leg1_flight_no} (${leg1_origin.slice(-3)} ✈ ${leg1_dest.slice(-3)})</span>
                        <span class="info_dept"><span>Departs:</span> ${leg1_dep_time}</span>
                        <span class="info_arr"><span>Arrives:</span> ${leg1_arr_time}</span>
                        <span class="info_transit"><span>Transit Time:</span> ${transit_time}</span>
                        <span class="info_flight">${leg2_airline+leg2_flight_no} (${leg2_origin.slice(-3)} ✈ ${leg2_dest.slice(-3)})</span>
                        <span class="info_dept"><span>Departs:</span> ${leg2_dep_time}</span>
                        <span class="info_arr"><span>Arrives:</span> ${leg2_arr_time}</span>
                        <span class="info_duration"><span>Total Flight Duration:</span> ${leg1_duration}</span>
                    </div>
                    `;
                    noflights = false;
                    flightHTML += `</div>`;
                }
                if(saved_flights[flightkey]) {saved_flights[flightkey] = { f: n_f,j: n_j,p: n_p, y: n_y }; update_saved_flights();}
            }
        });
    }
    flightHTML += "</div></div>"

    shadowRoot.querySelector('.bulk_table tr[data-date="' + date + '"] .bulk_flights').insertAdjacentHTML("beforeend", flightHTML);
    stickyFooter();
    if(autoScroll) shadowRoot.querySelector(".bulk_results").scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
}

window.addEventListener('wheel', function(){
    if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) {
        autoScroll = true;
    } else {
        autoScroll = false;
    }
});
window.addEventListener('touchmove', function(){
    if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) {
        autoScroll = true;
    } else {
        autoScroll = false;
    }
});




//============================================================
// Update Saved Flights
//============================================================
    
    function updateFlights(){
            let saved = value_get("saved_flights",{});
            unsafeWindow.ue_saved_flights = saved;
            unsafeWindow.ue_flights = {};
            let route, flightnum;
            Object.entries(saved).sort((a, b) => a[0].substring(0, 8) - b[0].substring(0, 8)).forEach(flight => {
                if(flight[0].split("_")[2]) return;
                route = flight[0].substring(8, 14);
                flightnum = flight[0].split("_")[1];
                unsafeWindow.ue_flights[route] = unsafeWindow.ue_flights[route] || [];
                unsafeWindow.ue_flights[route].push({
                    date: flight[0].substring(0, 8),
                    flight: flightnum,
                    y: flight[1].y,
                    j: flight[1].j,
                    f: flight[1].f
                });
            })
            unsafeWindow.populateFlights();
    }

    if(window.location.href.indexOf("https://cxplanner.jayliu.net/") > -1 || window.location.href.indexOf("Development/cx_planner") == 21) {
        unsafeWindow.ue_saved_flights = value_get("saved_flights",{});
        updateFlights();
        window.addEventListener("focus", function(e){
            unsafeWindow.ue_saved_flights = value_get("saved_flights",{});
            setTimeout(function() {
            unsafeWindow.ue_saved_flights = value_get("saved_flights",{});
              updateFlights(e);
            }, 500);
        });
    }


    unsafeWindow.searchAwards = function(e){
        let origin = e.target.closest("tr").querySelector(".rt-origin span");
        let dest = e.target.closest("tr").querySelector(".rt-dest span");

        if(!data.routes[[origin.innerText,dest.innerText].sort().join("-")]?.length) {
            alert("Invalid route.");
            return;
        }

        let saved = unsafeWindow.ue_flights || {};
        let target = newQueryPayload({from:origin.innerText, to:dest.innerText, date:dateAdd(14)}, {adult:1, child:0}, "Y", true, false);

        value_set("uef_from",origin.innerText);
        value_set("uef_to",dest.innerText);
        value_set("cont_ts",Date.now());
        value_set("cont_query","1");
        value_set("cont_batch","0");
        value_set("redirect_search",target.href);
        value_set("cxplanner_flights",fullRoute);

        const url = `https://www.cathaypacific.com/cx/${lang.el}_${lang.ec}/book-a-trip/redeem-flights/redeem-flight-awards.html`;
        window.open(url, "cxplanner", "noopener noreferrer");

        //location.href = `https://www.cathaypacific.com/cx/${lang.el}_${lang.ec}/book-a-trip/redeem-flights/redeem-flight-awards.html`;
        //console.log(target.href)

    };
//============================================================
// Sticky Footer
//============================================================

    function stickyFooter() {
        var bulkboxOffset = div_bulk_box.getBoundingClientRect();
        var ueformOffset = div_ue_container.getBoundingClientRect();
        //if (footerOffset.top < window.innerHeight - 55 || ueformOffset.top + div_ue_container.clientHeight > window.innerHeight - 72) {
        if(bulkboxOffset.bottom < window.innerHeight) {
            div_footer.classList.remove("bulk_sticky");
            shadowRoot.querySelector(".bulk_results").style.paddingBottom = "0px"
        } else {
            div_footer.classList.add("bulk_sticky");
            shadowRoot.querySelector(".bulk_results").style.paddingBottom = "65px"
        }
    }

//============================================================
// Enable Advanced Features
//============================================================

    //value_set("pKey","false");

    let pKey = value_get("pKey","");

    function is_valid_key(key){
        //var hash = encJS.MD5(encJS.AES.decrypt("U2FsdGVkX18+gMipG7SN/jcVZuccMP/M3IN/HG2brkhx0CoRJFkxcKSNyQounYc9XiF9Pk48buZ58RcxV6W5Rn3NGzEN3kz0sN0ulGThPwadtChhIC58c65+vqo4l4MT", key).toString(encJS.enc.Utf8) || "").toString();
        //return (hash == "68a1fc33f27f95281c831e99f5c4fabc");
        return (btoa(key) == "Q1gyMlVFQVM=");
    }

    function is_trial_key(key){
        return (btoa(key) == "Q1gwMzEyRlJFRQ==");
    }

    function check_key(key){
        if(is_valid_key(key)){
            const jsConfetti = new JSConfetti();
            jsConfetti.addConfetti({
                  confettiRadius: 3.5,
                  confettiNumber: 500,
            });
           pKey = value_set("pKey",key);
           advanced_features();
           input_to.value = "";
           input_from.value = "";
           elevate();
        } else if(is_trial_key(key)){
            if(Date.now() > 1679241599000) {
                alert("序號已過期");
                shadowRoot.querySelector("#activation_input").value = "";
            } else if(value_get("trial","")){
                alert("已經使用過試用序號");
            } else {
                alert("已解鎖 36 小時試用");
                const jsConfetti = new JSConfetti();
                jsConfetti.addConfetti({
                      confettiRadius: 3.5,
                      confettiNumber: 500,
                });
                value_set("trial",Date.now())
                advanced_features();
                input_to.value = "";
                input_from.value = "";
                elevate();
            }

        }
    }
    //GM.deleteValue("trial");
    if(is_valid_key(value_get("pKey","")) || (Date.now() - value_get("trial",0) < 60*60*36*1000)) {//60*60*24*1000)) {
        advanced_features();
    }

    function advanced_features(){
        //let code = "U2FsdGVkX18HnJPtvD6mz6QtAuSQH5QT2SPCHL7n5IyEvb/rBQgTAPy4LWR4oRODB+/F7QHVXYqM4V/";
        //code += "NDW1Fb0RAPbdiIPQY1A6sBgc+/JOQdpnjHb8mswg6lLoEsywchzBSKrzB7QDQr7/9A0aqXeWE80tnH9mHpxKDMBuo04c=";
        //eval(encJS.AES.decrypt(code, pKey).toString(encJS.enc.Utf8));
        let code = "c2hhZG93Q29udGFpbmVyLmNsYXNzTGlzdC5hZGQoImVsZXZhdGVkX29uIik7c2hhZG93Q29";
        code += "udGFpbmVyLmNsYXNzTGlzdC5yZW1vdmUoInVuZWxldmF0ZWRfY29udGFpbmVyIik7dCA9IHI7";
        eval(atob(code));
    }


    /*
    // ENCRYPT
    pKey = "";let code = ``;
    document.querySelector("body").insertAdjacentHTML("beforebegin", encJS.AES.encrypt(code, pKey).toString());
    // DECRYPT:
    eval(encJS.AES.decrypt("code_string", pkey).toString(encJS.enc.Utf8));
    */


//============================================================
// Check Version (Max once per day)
//============================================================

    let currentVersion = GM_info.script.version;
    let lastCheck = value_get("lastCheck",0)
    let latestVersion = value_get("latestVersion",currentVersion)

    function hasUpdate(newer, older) {
        let latest = newer.trim().split('.');
        let loaded = older.trim().split('.');
        for (let i = 0; i < Math.min(latest.length, loaded.length); i++) {
            latest[i] = Number(latest[i]) || 0;
            loaded[i] = Number(loaded[i]) || 0;
            if (latest[i] !== loaded[i]) {return (latest[i] > loaded[i] ? newer : false );};
        }
        return (latest.length > loaded.length ? newer : false);
    }

    function showUpdate(liveVersion){
        log("currentVersion: "+ currentVersion);
        log("metaData.version: "+ liveVersion);

        shadowRoot.querySelector(".unelevated_update a").href = value_get("update_link","https://cxplanner.jayliu.net/private.html");
                
        let newVersion = hasUpdate(liveVersion,currentVersion);
        if(newVersion){
            value_set("latestVersion",liveVersion);
            div_update.classList.remove("hidden");
            shadowRoot.querySelector("#upd_version").innerText = newVersion;
        };
    }

    function getLatest(date) {
        GM_xmlhttpRequest({
            method: 'GET',
            url: 'https://cxplanner.jayliu.net/latest_private.json?v='+date,
            onload: function(e) {
                const response = JSON.parse(e.responseText);
                const version = response.latest_version;
                const link = response.update_link;
                value_set("update_link", (link ? link : "https://cxplanner.jayliu.net/private.html"));
                //const key = /\/\/ @version +([0-9\.]+)/;
                //const version = e.responseText.match(key) ? e.responseText.match(key)[1] : "0";
                showUpdate(version);
            }
        })
    }

    function versionCheck(update, updateurl, metaData){
        let date = new Date();
        date = Math.floor(date.setHours(0,0,0)/1000);
        if (date > lastCheck || !lastCheck) {
            getLatest(date);
            lastCheck = value_set("lastCheck",date)
        } else {
            showUpdate(latestVersion)
        }
        //value_set("lastCheck",0);
    }

//============================================================
// Initialise
//============================================================

    function initSearchBox() {
        initCXvars();
        shadowContainer.appendChild(searchBox);
        assignElemets();
        if(r==t) elevate();
        addFormListeners();
        window.onscroll = function() { stickyFooter() };
        update_saved_count();
        update_saved_flights();
        autocomplete(input_from, "origins");
        autocomplete(input_to, "origins");
        getOrigins();
        versionCheck();

        if (cont_query) {
            reset_cont_vars();
            // If over 5 minutes since cont query, don't auto search
            if (Date.now() - cont_ts > 60*5*1000 && !debug) return;
            btn_batch.innerHTML = lang.searching_w_cancel;
            btn_batch.classList.add("bulk_searching");
            document.body.classList.add("cont_query");
            if(cont_saved){
                setTimeout(() => { saved_search(); }, "1000")
            } else {
                setTimeout(() => { bulk_click(cont_batch ? false : true); }, "1000")
            }
        }
    };

    function initCXplannerBox(){
        if(cxplanner_flights){
            const cxplanner_box = document.createElement("div");
            let html = "";
            cxplanner_flights.forEach(item=>{
                if(item) html += `<a href="#" class="planner_route" data-route="${item}">${item}</a>`;
            })
            cxplanner_box.innerHTML = "<div id='planner_routes'>"+html+"</div>";
            shadowContainer.appendChild(cxplanner_box);

            value_set("cxplanner_flights", [])
            cxplanner_box.addEventListener("click",function(e){
                if(e.target.dataset["route"]){
                    route_changed = true;
                    input_from.value = e.target.dataset["route"].split("-")[0];
                    input_to.value = e.target.dataset["route"].split("-")[1];
                }
            })
        }

    }
    

    if(window.location.href.indexOf("cathaypacific") > -1){
        initRoot();
    }

})();