https://2p4c.tw/wp-includes/js/jquery/jquery.min.js
https://2p4c.tw/wp-includes/js/jquery/jquery-migrate.min.js
(function() {
if (window.location.pathname.startsWith('/en/')) {
var topBar = document.getElementById('p4c-top-bar');
if (topBar) topBar.style.display = 'none';
}
})();
(function() {// ─── 設定區 ───────────────────────────────────────────────────
const DEPART_TS = new Date(2026, 4, 7, 19, 0).getTime();const MAIN_CSV = "https://docs.google.com/spreadsheets/d/e/2PACX-1vQDVRNBB3Q69rAD0gz8vkxrxrlpmrakHyXtc6HaK9z3yMtfwSJnMx2ipsdsbT3cc3rPwNmRV9C8_7xt/pub?gid=0&single=true&output=csv";
const DREAM_CSV = "https://docs.google.com/spreadsheets/d/e/2PACX-1vToUrDFMBiciaod5nOENBw5TFWc0md48wUsiyl-8J7FPxMzevKLQehq6pMjG0sjfPEQ3wjTI9nf5fHw/pub?gid=0&output=csv";
// ─────────────────────────────────────────────────────────────let masterRows = [], logIdx = 0;// ── 倒數計時 ──
function tick() {
const now = Date.now();
const diff = Math.abs(DEPART_TS - now);
const d = Math.floor(diff / 86400000);
const h = Math.floor((diff % 86400000) / 3600000);
const m = Math.floor((diff % 3600000) / 60000);
const s = Math.floor((diff % 60000) / 1000);
const el = document.getElementById('sh28-main-timer');
if (el) el.innerHTML = `${d}<span class="sh-u">D</span>${String(h).padStart(2,'0')}<span class="sh-u">H</span>${String(m).padStart(2,'0')}<span class="sh-u">M</span>${String(s).padStart(2,'0')}<span class="sh-u">S</span>`;
}
setInterval(tick, 1000); tick();// ── 打字機 ──
const textToType = "挑戰不出包環遊世界!";
const typeTarget = document.getElementById('sh28-type-target');
let typeIdx = 0;
function typeWriter() {
if (!typeTarget) return;
if (typeIdx < textToType.length) {
typeTarget.innerHTML += textToType.charAt(typeIdx++);
setTimeout(typeWriter, 150);
}
}
setTimeout(typeWriter, 500);// ── CSV 載入 ──
function fetchCSV(url, callback) {
const urlWithTs = url + (url.includes('?') ? '&' : '?') + 't=' + Date.now();
Papa.parse(urlWithTs, {
download: true,
header: false,
skipEmptyLines: true,
withCredentials: false,
complete: function(results) {
callback(null, results.data);
},
error: function(err) {
callback(err, null);
}
});
}// ── 初始化引擎 ──
function initEngine() {
if (typeof Papa === 'undefined' || typeof gsap === 'undefined') {
setTimeout(initEngine, 200);
return;
}// 載入主表
fetchCSV(MAIN_CSV, function(err, data) {
if (err || !data) {
showCsvError('sh28-log-list', '旅遊日誌');
return;
}
masterRows = data.slice(1); // 去表頭
processMaster(masterRows);
});// 載入 Bucket List
Papa.parse(DREAM_CSV + '&t=' + Date.now(), {
download: true,
header: true,
skipEmptyLines: true,
withCredentials: false,
complete: function(results) {
if (!results.data || results.data.length === 0) {
showCsvError('sh28-d-tr1', 'Bucket List');
return;
}
renderDreams(results.data);
},
error: function() {
showCsvError('sh28-d-tr1', 'Bucket List');
}
});
}function showCsvError(containerId, label) {
const el = document.getElementById(containerId);
if (el) el.innerHTML = `<div class="sh-csv-error">⚠ 無法載入${label}資料<br><small>請確認 Google Sheet 已設定為「發布到網頁」</small></div>`;
}// ── 處理主表 ──
function processMaster(rows) {
const countrySet = new Set();
const citySet = new Set();
let fisc = { food:0, stay:0, trans:0, visa:0, ent:0, other:0 };const trackCities = [];
let prevCity = null;rows.forEach((r) => {
const country = (r[1] || '').trim();
const city = (r[2] || '').trim();if (country) countrySet.add(country);
if (city) citySet.add(city);fisc.food += parseInt(r[4]) || 0;
fisc.stay += parseInt(r[5]) || 0;
fisc.trans += parseInt(r[6]) || 0;
fisc.visa += parseInt(r[7]) || 0;
fisc.ent += parseInt(r[8]) || 0;
fisc.other += parseInt(r[9]) || 0;if (city && city !== prevCity) {
trackCities.push({ city, country });
prevCity = city;
}
});document.getElementById('sh28-count-co').innerText = countrySet.size;
document.getElementById('sh28-count-ci').innerText = citySet.size;// 建立路線 HTML
const trackBox = document.getElementById('sh28-horizon-track');
let trackH = '';trackCities.forEach((item, idx) => {
const isLast = idx === trackCities.length - 1;
if (isLast) {
trackH += `<div class="sh-j-node sh-j-current" id="sh28-act-node">
<div class="sh-j-tag">CURRENT</div>
<div class="sh-j-city" style="font-size:2rem;">${item.city}</div>
<span style="font-family:'Montserrat'; font-size:0.7rem; font-weight:800; letter-spacing:2px; text-transform:uppercase; margin-top:5px;">${item.country}</span>
</div>`;
} else {
trackH += `<div class="sh-j-node sh-j-past">
<div class="sh-j-city">${item.city}</div>
<span style="font-family:'Montserrat'; font-size:0.6rem; opacity:0.4; margin-top:5px; font-weight:800; text-transform:uppercase; letter-spacing:2px;">${item.country}</span>
</div>`;
}
});// NEXT STOP
const lastRow = rows[rows.length - 1];
const nextStop = (lastRow[15] || '').trim();
if (nextStop) {
trackH += `<div class="sh-j-node" style="opacity:0.3">
<div class="sh-j-tag" style="background:#ccc; color:#fff !important;">NEXT STOP</div>
<div class="sh-j-city">${nextStop}</div>
</div>`;
}trackBox.innerHTML = trackH;setTimeout(() => {
const node = document.getElementById('sh28-act-node');
if (node) {
trackBox.scrollTo({
left: node.offsetLeft - (trackBox.offsetWidth / 2) + (node.offsetWidth / 2),
behavior: 'smooth'
});
}
}, 800);// 財務總計
const total = Object.values(fisc).reduce((a, b) => a + b, 0);
const totalEl = document.getElementById('sh28-total-spend');
if (typeof gsap !== 'undefined') {
gsap.to({ val: 0 }, {
val: total, duration: 3, ease: "expo.out",
onUpdate: function() { totalEl.innerText = `$${Math.floor(this.targets()[0].val).toLocaleString()}`; }
});
} else {
totalEl.innerText = `$${total.toLocaleString()}`;
}// 支出比例條
const labs = ["FOOD","STAY","TRANSIT","VISA","ENT","MISC"];
const keys = ["food","stay","trans","visa","ent","other"];
let bH = '';
keys.forEach((k, i) => {
const p = total > 0 ? Math.round((fisc[k] / total) * 100) : 0;
bH += `<div class="sh-bar-row"><span style="width:60px;">${labs[i]}</span><div class="sh-bar-track"><div class="sh-bar-fill" style="width:${p}%"></div></div><span style="width:30px; text-align:right;">${p}%</span></div>`;
});
document.getElementById('sh28-budget-bars').innerHTML = bH;sh28RenderLogs();
}// ── 渲染日誌 ──
window.sh28RenderLogs = function() {
const container = document.getElementById('sh28-log-list');
const btn = document.getElementById('sh28-load-btn');
const reversed = [...masterRows].reverse();
const batch = reversed.slice(logIdx, logIdx + 5);batch.forEach(r => {
const d1 = new Date("2026-05-07");
const d2 = new Date(r[0]);
const dayNum = isNaN(d2) ? '?' : Math.ceil((d2 - d1) / 86400000) + 1;
const dayCost = (parseInt(r[4])||0) + (parseInt(r[5])||0) + (parseInt(r[6])||0) +
(parseInt(r[7])||0) + (parseInt(r[8])||0) + (parseInt(r[9])||0);const div = document.createElement('div');
div.className = 'sh-log-card';
div.innerHTML = `
<div class="sh-log-day-box">
<div class="sh-log-day-num">D${dayNum}</div>
<span class="sh-log-date-val">${r[0] ? r[0].replace(/-/g, '/') : '--'}</span>
</div>
<div>
<p class="sh-log-quote">「${r[3] || '旅程本身就是答案。'}」</p>
<span style="font-weight:700; font-size:0.85rem; letter-spacing:1px; text-transform:uppercase; font-family:'Montserrat', sans-serif;">
<span style="color:#F4C842; margin-right:5px;">■</span> ${r[2] || ''}, ${r[1] || ''}
</span>
<br>
<span class="sh-day-cost-tag">DAILY SPEND:$${dayCost.toLocaleString()}</span>
<p style="font-size:0.9rem; opacity:0.6; margin-top:15px; line-height:1.8;">${r[10] || ''}</p>
</div>`;
container.appendChild(div);
});logIdx += 5;
if (logIdx >= masterRows.length && btn) btn.style.display = 'none';
};// ── Bucket List ──
function renderDreams(data) {
const trs = [
document.getElementById('sh28-d-tr1'),
document.getElementById('sh28-d-tr2'),
document.getElementById('sh28-d-tr3')
];
if (!trs[0]) return;const validTasks = data.filter(d => d.Task && d.Task.trim());
if (validTasks.length === 0) return;const doneCount = validTasks.filter(d => String(d.Status) === "1").length;
const pct = Math.round((doneCount / validTasks.length) * 100);const barEl = document.getElementById('sh28-d-bar');
const pctEl = document.getElementById('sh28-d-percent');
if (barEl) setTimeout(() => { barEl.style.width = pct + '%'; }, 200);
if (pctEl) pctEl.innerText = pct + '%';validTasks.forEach((d, i) => {
const isDone = String(d.Status) === "1";
const taskName = d.Task.trim();
const storageKey = 'sh_heart_v28_' + taskName.replace(/[^a-z0-9]/gi, '_');
let userLiked = false;
try { userLiked = localStorage.getItem(storageKey) === 'true'; } catch(e) {}const chip = document.createElement('div');
chip.className = `sh-chip ${isDone ? 'is-done' : ''} ${userLiked ? 'user-liked' : ''}`;if (isDone) {
chip.innerHTML = `<span>${taskName}</span>`;
} else {
const baseCount = taskName.length * 7 + 25 + (userLiked ? 1 : 0);
chip.innerHTML = `<span>${taskName}</span><div class="sh-chip-like"><span class="sh-h-icon">${userLiked ? '❤️' : '♡'}</span><span class="sh-h-count">${baseCount}</span></div>`;chip.addEventListener('click', function() {
if (dragState.hasMoved) return;
const icon = this.querySelector('.sh-h-icon');
const count = this.querySelector('.sh-h-count');
const cur = parseInt(count.innerText);
if (this.classList.contains('user-liked')) {
this.classList.remove('user-liked');
icon.innerText = '♡';
count.innerText = cur - 1;
try { localStorage.removeItem(storageKey); } catch(e) {}
} else {
this.classList.add('user-liked');
icon.innerText = '❤️';
count.innerText = cur + 1;
try { localStorage.setItem(storageKey, 'true'); } catch(e) {}
}
});
}trs[i % 3].appendChild(chip);
});// 拖拽
const scrollEl = document.getElementById('sh28-drag-cloud');
const dragState = { active: false, startX: 0, scrollL: 0, hasMoved: false };scrollEl.addEventListener('mousedown', (e) => {
dragState.active = true;
dragState.startX = e.pageX - scrollEl.offsetLeft;
dragState.scrollL = scrollEl.scrollLeft;
dragState.hasMoved = false;
});
scrollEl.addEventListener('mousemove', (e) => {
if (!dragState.active) return;
const walk = (e.pageX - scrollEl.offsetLeft - dragState.startX) * 2;
if (Math.abs(walk) > 5) dragState.hasMoved = true;
scrollEl.scrollLeft = dragState.scrollL - walk;
});
scrollEl.addEventListener('mouseleave', () => { dragState.active = false; });
scrollEl.addEventListener('mouseup', () => { dragState.active = false; });
scrollEl.addEventListener('click', (e) => {
if (dragState.hasMoved) { e.preventDefault(); e.stopPropagation(); }
}, true);
}// ── 安全啟動 ──
if (document.readyState === 'complete' || document.readyState === 'interactive') {
setTimeout(initEngine, 1);
} else {
document.addEventListener('DOMContentLoaded', initEngine);
}})();
document.addEventListener('DOMContentLoaded', function() {var links = document.querySelectorAll('link[rel="alternate"][hreflang]');
var targetUrl = null;
var targetText = null;var isEnglish = window.location.pathname.indexOf('/en/') !== -1;
var currentLang = isEnglish ? 'en' : 'zh-TW';links.forEach(function(link) {
var lang = link.getAttribute('hreflang');
var href = link.getAttribute('href');
if (!href || lang === 'x-default') return;if (currentLang === 'zh-TW' && lang === 'en') {
targetUrl = href;
targetText = 'EN';
} else if (currentLang === 'en' && (lang === 'zh-TW' || lang === 'zh')) {
targetUrl = href;
targetText = '中';
}
});if (!targetUrl) return;var btn = document.createElement('a');
btn.className = 'p4c-lang-float';
btn.href = targetUrl;
btn.title = currentLang === 'zh-TW' ? 'Read in English' : '閱讀中文版本';
btn.textContent = targetText;
btn.style.display = 'flex';document.body.appendChild(btn);
});
function genesisBlocksShare( url, title, w, h ){
var left = ( window.innerWidth / 2 )-( w / 2 );
var top = ( window.innerHeight / 2 )-( h / 2 );
return window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=600, height=600, top='+top+', left='+left);
}
var ConverlyCustomData = {channelId: null};
const loadScriptsTimer=setTimeout(loadScripts,5*1000);const userInteractionEvents=['click', 'mousemove', 'keydown', 'touchstart', 'touchmove', 'wheel'];userInteractionEvents.forEach(function(event){window.addEventListener(event,triggerScriptLoader,{passive:!0})});function triggerScriptLoader(){loadScripts();clearTimeout(loadScriptsTimer);userInteractionEvents.forEach(function(event){window.removeEventListener(event,triggerScriptLoader,{passive:!0})})}
function loadScripts(){document.querySelectorAll("script[data-type='lazy']").forEach(function(elem){elem.setAttribute("src",elem.getAttribute("data-src"))})}
var rtocScrollAnimation = {"rtocScrollAnimation":"on"};
var rtocBackButton = {"rtocBackButton":"off"};
var rtocBackDisplayPC = {"rtocBackDisplayPC":""};
var rtocOpenText = {"rtocOpenText":"OPEN"};
var rtocCloseText = {"rtocCloseText":"CLOSE"};
//# sourceURL=rtoc_js-js-extra
https://2p4c.tw/wp-content/plugins/rich-table-of-content/js/rtoc_common.js
https://2p4c.tw/wp-content/plugins/rich-table-of-content/js/rtoc_scroll.js
https://2p4c.tw/wp-content/plugins/rich-table-of-content/js/rtoc_highlight.js
https://2p4c.tw/wp-content/plugins/genesis-blocks/dist/assets/js/dismiss.js
https://2p4c.tw/wp-content/plugins/breeze/assets/js/js-front-end/breeze-lazy-load.min.js
document.addEventListener("DOMContentLoaded", function () {
if ( "function" !== typeof window.LazyLoad && "function" !== typeof LazyLoad ) {
return;
}var breezeLazyLoad = window.LazyLoad || LazyLoad;window.lazyLoadInstance = new breezeLazyLoad({
elements_selector: ".br-lazy",
data_src: "breeze",
data_srcset: "brsrcset",
data_sizes: "brsizes",
class_loaded: "br-loaded",
threshold: 300,
});
});
//# sourceURL=breeze-lazy-js-after
var csLocalize = {"siteSchemeMode":"light","siteSchemeToogle":""};
//# sourceURL=csco-scripts-js-extra
https://2p4c.tw/wp-content/themes/swyft/assets/js/scripts.js
window.addEventListener("DOMContentLoaded",(e=>{document.querySelectorAll('img[loading="lazy"]').forEach((e=>{e.getBoundingClientRect().top<=window.innerHeight&&(e.loading="eager")}))}));
const loadScriptsTimer=setTimeout(loadScripts,5*1000);const userInteractionEvents=["mouseover","keydown","touchstart","touchmove","wheel"];userInteractionEvents.forEach(function(event){window.addEventListener(event,triggerScriptLoader,{passive:!0})});function triggerScriptLoader(){loadScripts();clearTimeout(loadScriptsTimer);userInteractionEvents.forEach(function(event){window.removeEventListener(event,triggerScriptLoader,{passive:!0})})}function loadScripts(){document.querySelectorAll("script,iframe[data-type='lazy']").forEach(function(elem){elem.setAttribute("src",elem.getAttribute("data-src"))})}
document.addEventListener("DOMContentLoaded", function () {
const firstSlideImg = document.querySelector(
".cs-hero-type-1_item[data-swiper-slide-index='0'] img"
);if (firstSlideImg) {
firstSlideImg.loading = "eager"; // 讓它成為優先載入圖
firstSlideImg.decoding = "async";
firstSlideImg.removeAttribute("loading"); // 確保 lazy 被移除
}
});