From c92545927a72388b506bb0bb6c4d337bee9a5b12 Mon Sep 17 00:00:00 2001 From: ken Date: Thu, 27 Aug 2026 18:03:00 +0800 Subject: [PATCH] update --- assets/javascripts/app.js | 439 ++++++++++++++++-- assets/stylesheets/template/base/_global.scss | 15 +- modules/ad_banner/_ad_banner_widget2.html.erb | 4 +- .../_ad_banner_widget2_video.html.erb | 4 +- modules/ad_banner/_ad_banner_widget3.html.erb | 51 +- modules/ad_banner/_ad_banner_widget5.html.erb | 8 +- .../_ad_banner_widget8_video.html.erb | 4 +- .../_ad_banner_widget9_video.html.erb | 4 +- modules/announcement/_annc_widget1.html.erb | 2 +- modules/announcement/_annc_widget15.html.erb | 2 +- modules/announcement/_annc_widget16.html.erb | 2 +- modules/announcement/_annc_widget18.html.erb | 2 +- modules/announcement/_annc_widget2.html.erb | 2 +- modules/announcement/_annc_widget20.html.erb | 2 +- modules/announcement/_annc_widget21.html.erb | 2 +- modules/announcement/_annc_widget23.html.erb | 2 +- modules/announcement/_annc_widget24.html.erb | 2 +- modules/announcement/_annc_widget25.html.erb | 2 +- modules/announcement/_annc_widget26.html.erb | 2 +- modules/announcement/_annc_widget3.html.erb | 2 +- modules/announcement/_annc_widget4.html.erb | 2 +- modules/announcement/annc_index18.html.erb | 2 +- modules/announcement/annc_index19.html.erb | 2 +- modules/announcement/annc_index5.html.erb | 2 +- modules/announcement/annc_index6.html.erb | 2 +- modules/archive/archive_index1.html.erb | 3 +- modules/archive/archive_index12.html.erb | 3 +- modules/archive/archive_index17.html.erb | 3 +- modules/archive/archive_index2.html.erb | 3 +- modules/archive/archive_index3.html.erb | 3 +- modules/archive/archive_index4.html.erb | 3 +- modules/archive/archive_index6.html.erb | 3 +- .../_annc_widget1.html.erb | 2 +- .../_annc_widget15.html.erb | 2 +- .../_annc_widget16.html.erb | 2 +- .../_annc_widget18.html.erb | 2 +- .../_annc_widget2.html.erb | 2 +- .../_annc_widget3.html.erb | 2 +- .../_annc_widget4.html.erb | 2 +- modules/custom_announcement/show.html.erb | 2 +- .../_event_news_widget1.html.erb | 2 +- .../_event_news_widget15.html.erb | 2 +- .../_event_news_widget2.html.erb | 2 +- .../_event_news_widget20.html.erb | 2 +- .../_event_news_widget21.html.erb | 2 +- .../_event_news_widget22.html.erb | 2 +- .../_event_news_widget3.html.erb | 2 +- .../_event_news_widget4.html.erb | 2 +- modules/video_pro/_video_pro_widget1.html.erb | 2 +- modules/video_pro/_video_pro_widget2.html.erb | 2 +- modules/video_pro/_video_pro_widget3.html.erb | 2 +- modules/video_pro/_video_pro_widget4.html.erb | 2 +- modules/video_pro/_video_pro_widget5.html.erb | 2 +- modules/video_pro/_video_pro_widget6.html.erb | 2 +- modules/video_pro/video_pro_index1.html.erb | 2 +- modules/video_pro/video_pro_index2.html.erb | 2 +- .../video_pro/video_pro_index2_tag.html.erb | 2 +- modules/video_pro/video_pro_index3.html.erb | 2 +- modules/video_pro/video_pro_index4.html.erb | 2 +- .../video_pro_index_post_agency1.html.erb | 2 +- .../web_resource/_web_res_widget1.html.erb | 2 +- .../web_resource/_web_res_widget10.html.erb | 2 +- .../web_resource/_web_res_widget11.html.erb | 2 +- .../web_resource/_web_res_widget12.html.erb | 2 +- .../web_resource/_web_res_widget2.html.erb | 4 +- .../web_resource/_web_res_widget3.html.erb | 2 +- .../web_resource/_web_res_widget4.html.erb | 2 +- .../web_resource/_web_res_widget5.html.erb | 2 +- .../web_resource/_web_res_widget6.html.erb | 2 +- .../web_resource/_web_res_widget7.html.erb | 4 +- .../web_resource/_web_res_widget8.html.erb | 4 +- .../web_resource/_web_res_widget9.html.erb | 2 +- 72 files changed, 519 insertions(+), 151 deletions(-) diff --git a/assets/javascripts/app.js b/assets/javascripts/app.js index efa7d40..e57237d 100644 --- a/assets/javascripts/app.js +++ b/assets/javascripts/app.js @@ -905,7 +905,7 @@ var isLinkImage = $parentA.length > 0 && $parentA.text().replace(/\u00a0/g, '').trim() === ""; // --- 1. 處理「裝飾性圖片」標註 --- - if (currentAlt === "announcement image" || currentAlt === "裝飾圖片" || currentTitle === "裝飾圖片" || currentAlt === "這是一張圖片" || currentTitle === "這是一張圖片") { + if (currentAlt === "announcement image" || currentAlt === "裝飾圖片" || currentTitle === "裝飾圖片" || currentAlt === "這是一張圖片" || currentTitle === "這是一張圖片"||currentAlt === "This is a image" ||currentTitle === "This is a image") { $img.attr('alt', ''); $img.removeAttr('title'); return; @@ -1107,56 +1107,59 @@ $(function() { } } - // E. 重新計算標題 (關鍵邏輯:若 hasOriginalTitle 為真,則不進入此區塊) - if (!hasOriginalTitle) { - if (linkText === "" && $img.length > 0) { - var rawAlt = ($img.attr('alt') || '').trim(); - var forbidden = ['這是一張圖片', '圖片', 'image', 'photo', '圖', '']; - var imgAlt = forbidden.some(function(txt) { return rawAlt.toLowerCase() === txt; }) - ? ($a.attr('aria-label') || i18n.link) : rawAlt; - - var fileInfo = fileExt ? '[' + fileExt.toUpperCase() + '] ' : ''; - $a.attr('title', (fileInfo + imgAlt + " " + windowTask).trim()); - $img.attr('alt', imgAlt); - } - else if (linkText !== "") { - if (fileExt !== "" || hasBadSymbol || currentTitle === "" || currentTitle.includes('/uploads/')) { - if (fileExt !== "") { - $a.attr('title', linkText + " ." + fileExt + " " + windowTask); - } else { - $a.attr('title', windowTask); - } + // E. 重新計算標題 (關鍵邏輯:若 hasOriginalTitle 為真,則不進入此區塊) + if (!hasOriginalTitle) { + if (linkText === "" && $img.length > 0) { + var rawAlt = ($img.attr('alt') || '').trim(); + var forbidden = ['這是一張圖片', '圖片', 'image', 'photo', '圖', '']; + var imgAlt = forbidden.some(function(txt) { return rawAlt.toLowerCase() === txt; }) + ? ($a.attr('aria-label') || i18n.link) : rawAlt; + + var fileInfo = fileExt ? '[' + fileExt.toUpperCase() + '] ' : ''; + $a.attr('title', (fileInfo + imgAlt + " " + windowTask).trim()); + $img.attr('alt', imgAlt); + } + else if (linkText !== "") { + if (fileExt !== "" || hasBadSymbol || currentTitle === "" || currentTitle.includes('/uploads/')) { + if (fileExt !== "") { + $a.attr('title', linkText + " ." + fileExt + " " + windowTask); + } else { + $a.attr('title', windowTask); } } } + } - // ✅ 符合 HM1240404E:title 不應與連結文字或圖片 alt 重複 - if (hasOriginalTitle && currentTitle !== "") { - var titleWithoutWindow = currentTitle - .replace('(在本視窗開啟)', '') - .replace('(在新視窗開啟)', '') - .replace('Open in this window', '') - .replace('Open in new window', '') - .replace('另開新視窗前往', '') - .replace('另開新視窗', '') - .trim(); + // ✅ 符合 HM1240404E:title 不應與連結文字或圖片 alt 重複 + if (hasOriginalTitle && currentTitle !== "") { + var titleWithoutWindow = currentTitle + .replace('(在本視窗開啟)', '') + .replace('(在新視窗開啟)', '') + .replace('Open in this window', '') + .replace('Open in new window', '') + .replace('另開新視窗前往', '') + .replace('另開新視窗', '') + .trim(); - // ✅ title 去掉開窗字串後,若與連結文字或圖片原始 alt 相同,則只保留開窗提示 - if ( - (titleWithoutWindow === linkText && linkText !== i18n.link) || - (imgAltOriginal !== '' && titleWithoutWindow === imgAltOriginal) - ) { - $a.attr('title', windowTask); - } - - // ✅ 最終清理:若 title 出現空括號 () 則移除並補上正確開窗提示 - var finalTitle = ($a.attr('title') || '').trim(); - if (finalTitle.includes('()')) { - finalTitle = finalTitle.replace(/\(\)/g, '').trim(); - $a.attr('title', finalTitle + ' ' + windowTask); - } + // ✅ title 去掉開窗字串後,若與連結文字或圖片原始 alt 相同,則只保留開窗提示 + // ✅ 新增:若 linkText 包含副檔名且 title 也是同樣檔名,也只保留開窗提示 + if ( + (titleWithoutWindow === linkText && linkText !== i18n.link) || + (imgAltOriginal !== '' && titleWithoutWindow === imgAltOriginal) || + (fileExt !== '' && linkText.toLowerCase().endsWith('.' + fileExt) && titleWithoutWindow === linkText) + ) { + $a.attr('title', windowTask); + return; // ✅ 加上 return,避免繼續執行後面的清理邏輯造成二次污染 } + // ✅ 最終清理:若 title 出現空括號 () 則移除並補上正確開窗提示 + var finalTitle = ($a.attr('title') || '').trim(); + if (finalTitle.includes('()')) { + finalTitle = finalTitle.replace(/\(\)/g, '').trim(); + $a.attr('title', finalTitle + ' ' + windowTask); + } + } + // F. 最終清理:移除冗餘 label,避免螢幕閱讀器唸兩次 $a.removeAttr('aria-label'); }); @@ -2455,7 +2458,276 @@ $(document).ready(function() { init(); }); +// 1. OrbitBar 基礎與搜尋選單邏輯 +$(document).ready(function() { + const isEn = document.documentElement.lang === 'en'; + const $orbitLabel = $('label[for="open-orbit-nav"]'); + const $searchArea = $('.orbit-bar-search-sign-language'); + const $firstInput = $('#q'); // 搜尋框通常是選單內第一個獲焦元素 + // 初始化屬性 + if (isEn) { + $orbitLabel.attr({ title: 'Site search and language menu', 'aria-label': 'Site search and language menu' }); + } else { + $orbitLabel.attr({ title: '全站搜尋及語言切換選單', 'aria-label': '全站搜尋及語言切換選單' }); + } + $orbitLabel.attr({ 'role': 'button', 'aria-haspopup': 'true', 'aria-expanded': 'false', 'tabindex': '0' }); + + // --- 【修正重點 1】處理 Label 的焦點與按鍵行為 --- + $orbitLabel.on('focusin', function(e) { + var relatedTarget = e.relatedTarget; + // 如果是從選單內部退回來的 (Shift+Tab) + if (relatedTarget && ($searchArea.find(relatedTarget).length > 0)) { + $searchArea.removeClass('show'); + $(this).attr('aria-expanded', 'false'); + } else { + // 正向進入 + $searchArea.addClass('show'); + $(this).attr('aria-expanded', 'true'); + } + }); + + // 強制偵測:在 Label 上按下 Shift+Tab 代表要離開這整塊,立即收合 + $orbitLabel.on('keydown', function(e) { + if (e.shiftKey && e.keyCode === 9) { // Shift + Tab + $searchArea.removeClass('show'); + $searchArea.find('ul').removeClass('show'); + $(this).attr('aria-expanded', 'false'); + } + }); + + // --- 【修正重點 2】處理選單內第一個元素的 Shift+Tab --- + $firstInput.on('keydown', function(e) { + if (e.shiftKey && e.keyCode === 9) { // 在第一個輸入框按 Shift + Tab + // 讓它自然回到 Label,Label 的 focusin 會接手處理收合 + // 這裡不做額外動作,交給上面的 focusin 邏輯 + } + }); + + // --- 【修正重點 3】處理離開大區塊(往後 Tab)的狀態清除 --- + $searchArea.on('focusout', function() { + var $thisArea = $(this); + setTimeout(function() { + if (!$thisArea.is(':focus-within') && !$(document.activeElement).is($orbitLabel)) { + $thisArea.removeClass('show'); + $thisArea.find('ul').removeClass('show'); + $orbitLabel.attr('aria-expanded', 'false'); + $('#languagebutton').attr('aria-expanded', 'false'); + } + }, 150); + }); + + // 點擊行為 + $orbitLabel.on('click', function() { + var isOpen = $searchArea.hasClass('show'); + $searchArea.toggleClass('show', !isOpen); + $(this).attr('aria-expanded', !isOpen); + }); + + // RWD 位置搬移 + function moveSearch() { + if ($(window).width() < 767) { + $searchArea.insertAfter('label[for="open-orbit-nav"]'); + } else { + $searchArea.appendTo('.orbit-bar-inner'); + } + } + moveSearch(); + $(window).resize(moveSearch); +}); + +// 2. 語言按鈕與手機版選單補強 +$(function() { + function fixLanguageA11y() { + var isEn = document.documentElement.lang === 'en'; + var correctTitle = isEn ? 'Open language menu' : '在本視窗開啟語言選單'; + var $desktopBtn = $('#languagebutton'); + var $langLi = $('#language-li'); + var $langUl = $('#language-li ul'); + + // ============================================= + // ✅ 新增 GN1210101E:繁體中文 li 補上可聚焦 + // 桌面版 + 手機版都處理 + // ============================================= + var $activeLis = $langUl.find('li.active') + .add($('#language-li-ul').find('li.active')); + + $activeLis.each(function() { + var $li = $(this); + if ($li.find('a, button').length === 0) { + var activeText = $li.text().trim(); + $li.html( + '' + + activeText + + '' + ); + } + }); + + // ============================================= + // Desktop 語言按鈕 + // ============================================= + if ($desktopBtn.length > 0) { + $desktopBtn.attr({ + 'aria-expanded': 'false', + 'role': 'button', + 'aria-haspopup': 'true', + 'title': correctTitle + }); + $desktopBtn.removeAttr('aria-label'); + + function openLangMenu() { + $langUl.addClass('show'); + $desktopBtn.attr('aria-expanded', 'true'); + $('.orbit-bar-search-sign-language').addClass('show'); + } + function closeLangMenu() { + $langUl.removeClass('show'); + $desktopBtn.attr('aria-expanded', 'false'); + } + + // 原有:focus 展開 + $desktopBtn.on('focus', function() { + openLangMenu(); + }); + + // ✅ 新增 GN1210100E:Enter / Space 切換、Escape 收合 + $desktopBtn.on('keydown', function(e) { + if (e.which === 13 || e.which === 32) { + e.preventDefault(); + $langUl.hasClass('show') ? closeLangMenu() : openLangMenu(); + } + if (e.which === 27) { + closeLangMenu(); + $desktopBtn.focus(); + } + }); + + // 原有:click 切換 + $desktopBtn.on('click', function(e) { + e.preventDefault(); + $langUl.hasClass('show') ? closeLangMenu() : openLangMenu(); + }); + + // ✅ 新增:選單內 Escape 跳回按鈕 + $langUl.find('a').on('keydown', function(e) { + if (e.which === 27) { + e.preventDefault(); + closeLangMenu(); + $desktopBtn.focus(); + } + }); + + // 原有:焦點離開整個 language-li 時收合 + $langLi.on('focusout', function() { + var $container = $(this); + setTimeout(function() { + if (!$container.is(':focus-within')) { + closeLangMenu(); + } + }, 150); + }); + } + + // ============================================= + // 手機版選單補強(原有邏輯完整保留) + // ============================================= + var $mobileBtn = $('.mobile-button'); + if ($mobileBtn.length > 0) { + $mobileBtn.attr({ 'role': 'button', 'tabindex': '0', 'aria-haspopup': 'true', 'aria-expanded': 'false' }); + $mobileBtn.on('focus', function() { + $(this).closest('li').find('ul').addClass('show'); + $(this).attr('aria-expanded', 'true'); + }); + $mobileBtn.on('keydown', function(e) { + if (e.which === 13 || e.which === 32) { + e.preventDefault(); + var $ul = $(this).closest('li').find('ul'); + $ul.toggleClass('show'); + $(this).attr('aria-expanded', $ul.hasClass('show') ? 'true' : 'false'); + } + }); + $('#language-li-ul').on('focusout', function() { + var $container = $(this); + setTimeout(function() { + if (!$container.is(':focus-within')) { + $container.find('ul').removeClass('show'); + $mobileBtn.attr('aria-expanded', 'false'); + } + }, 150); + }); + } + + // ============================================= + // 原有:MutationObserver 強制鎖定 title + // ============================================= + var target = document.getElementById('languagebutton'); + if (target) { + var observer = new MutationObserver(function() { + if (target.getAttribute('title') !== correctTitle) { + target.setAttribute('title', correctTitle); + } + }); + observer.observe(target, { attributes: true, attributeFilter: ['title'] }); + } + } + + setTimeout(fixLanguageA11y, 600); +}); +// rwd左上選單無障礙 +$(function() { + /** + * 修正:當焦點離開整個區塊(含語言選單最後一項)時,關閉大選單 + */ + function fixSearchMenuFocusOut() { + var $searchArea = $('.orbit-bar-search-sign-language'); + var $orbitLabel = $('label[for="open-orbit-nav"]'); + var $langBtn = $('#languagebutton'); + + // 使用 focusout 監聽整個搜尋/語言區域 + $searchArea.on('focusout', function(e) { + // 關鍵:使用 setTimeout 讓 document.activeElement 抓到下一個獲焦元素 + setTimeout(function() { + // 如果目前的焦點不在 $searchArea 裡面,也不是回到觸發的 $orbitLabel 上 + if (!$searchArea.is(':focus-within') && !$(document.activeElement).is($orbitLabel)) { + + // 1. 移除大區塊顯示 + $searchArea.removeClass('show'); + + // 2. 移除子層語言選單顯示 + $searchArea.find('ul').removeClass('show'); + + // 3. 狀態重置為「折疊」供語音報讀 + $orbitLabel.attr('aria-expanded', 'false'); + $langBtn.attr('aria-expanded', 'false'); + } + }, 50); + }); + + // 桌面版 Language 按鈕特殊處理:焦點移入時 + $langBtn.on('focus', function() { + // 確保父層搜尋區塊是顯示的 (針對從別處直接 tab 過來的情況) + $searchArea.addClass('show'); + $(this).attr('aria-expanded', 'true'); + }); + + // 針對手機版 mobile-button (span) 同理處理 + $('.mobile-button').on('focusout', function() { + var $mobileParent = $('#language-li-ul'); + setTimeout(function() { + if (!$mobileParent.is(':focus-within')) { + $mobileParent.find('ul').removeClass('show'); + $('.mobile-button').attr('aria-expanded', 'false'); + } + }, 50); + }); + } + + setTimeout(fixSearchMenuFocusOut, 600); +}); $(document).ready(function () { $('.controlplay a[role="radio"]').on('click', function () { @@ -2622,7 +2894,7 @@ $(function () { }); //萬用表格無障礙 function fixSearchInputA11y() { - $('input[type="search"], input[type="text"]').each(function () { + $('input[type="search"], input.input-search, .search-container input, .search-box input').each(function () { var $input = $(this); // 已有對應 label 就跳過 @@ -2702,7 +2974,7 @@ function fixSortLinkA11y() { }); } function fixSearchInputA11y() { - $('input[type="search"], input[type="text"]').each(function () { + $('input[type="search"], input.input-search, .search-container input, .search-box input').each(function () { var $input = $(this); // 已有對應 label 就跳過 @@ -2847,9 +3119,6 @@ $(function() { var target = document.getElementById('gallery-theater-stage'); if (!target) return; - // 頁面載入時若已存在 #gallery-theater-stage,則還原 z-index - $('.verticalhome').css('z-index', 'auto'); - var observer = new MutationObserver(function() { var display = target.style.display; if (display === 'block') { @@ -2864,4 +3133,76 @@ $(function() { attributeFilter: ['style'] }); }); +$(document).ready(function() { + + function removeFakeLinks() { + $('a.fake_link').remove(); + } + + // 初始執行一次 + removeFakeLinks(); + + // Shift+Tab 時也執行 + $(document).on('keydown', function(e) { + if (e.key === 'Tab' && e.shiftKey) { + removeFakeLinks(); + } + }); + + // MutationObserver 監聽 DOM 變化,fake_link 一出現就移除 + var observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + mutation.addedNodes.forEach(function(node) { + // 直接是 fake_link + if ($(node).is('a.fake_link')) { + $(node).remove(); + } + // 或其子元素內含 fake_link + if ($(node).find) { + $(node).find('a.fake_link').remove(); + } + }); + }); + }); + + observer.observe(document.body, { + childList: true, + subtree: true + }); + +}); + +$(function() { + function fixMemberAcademicLinkA11y() { + setTimeout(function() { + $('.i-member-item').each(function() { + var $item = $(this); + + // ✅ 取得該成員的姓名 + var memberName = $item.find('.member-data-value-name a').text().trim(); + if (!memberName) return; + + // ✅ 找到學術歷程連結,補上包含姓名的 title + $item.find('.member-data-value-11 a').each(function() { + var $a = $(this); + var currentTitle = $a.attr('title') || ''; + + // ✅ 移除舊的開窗字串,組合新 title + var titleWithoutWindow = currentTitle + .replace('另開新視窗前往', '') + .replace('在新視窗開啟', '') + .replace('在本視窗開啟', '') + .trim(); + + var isNewWindow = $a.attr('target') === '_blank'; + var windowText = isNewWindow ? ' 在新視窗開啟' : ' 在本視窗開啟'; + + $a.attr('title', memberName + ' ' + titleWithoutWindow + windowText); + }); + }); + }, 500); + } + + fixMemberAcademicLinkA11y(); +}); }(jQuery, window)); diff --git a/assets/stylesheets/template/base/_global.scss b/assets/stylesheets/template/base/_global.scss index 8bac898..5c60378 100644 --- a/assets/stylesheets/template/base/_global.scss +++ b/assets/stylesheets/template/base/_global.scss @@ -6,6 +6,11 @@ .fa-classic, .fa-regular, .fa-solid, .far, .fas ,.fa{ font-family: var(--fa-style-family, "FontAwesome")!important; } +li#language-li { + @media(max-width:767px){ + display: none !important; + } +} #orbit-bar .orbit-bar-inner > label:focus, #orbit-bar .orbit-bar-inner > label.focus{ .orbit-bar-search-sign-language{ display: block @@ -26,11 +31,11 @@ } } } -label[for="open-orbit-login"] { - @media(max-width:768px){ - display: none!important; - } -} +// label[for="open-orbit-login"] { +// @media(max-width:768px){ +// display: none!important; +// } +// } [accesskey="U"],[accesskey="W"]{ @media(max-width:768px){ color: #666!important; diff --git a/modules/ad_banner/_ad_banner_widget2.html.erb b/modules/ad_banner/_ad_banner_widget2.html.erb index d64448c..4e1a9c7 100644 --- a/modules/ad_banner/_ad_banner_widget2.html.erb +++ b/modules/ad_banner/_ad_banner_widget2.html.erb @@ -29,7 +29,9 @@ - +