diff --git a/assets/javascripts/app.js b/assets/javascripts/app.js index f5d33d6..b796393 100644 --- a/assets/javascripts/app.js +++ b/assets/javascripts/app.js @@ -403,483 +403,1181 @@ $(document).ready(function() { // }else{ // $('.header-buttom').appendTo($('.dropdowns')); // } - function forFreeGo() { - //表單legend - $("fieldset").each(function () { - const $fieldset = $(this); - const hasLegend = $fieldset.children("legend").length > 0; + function forFreeGo() { + setTimeout(function() { + $('h1, h2, h3, h4, h5, h6').each(function() { + var $heading = $(this); + + // 檢查是否為空 (去除空白後) + if ($.trim($heading.text()) === '' && $heading.children().length === 0) { + $heading.remove(); + } + }); + }, 500); + //zoom + $(function () { + $('.fnav a').each(function () { + const $a = $(this); - if (!hasLegend) { - // 嘗試抓最上層的群組標題,例如 "Apply" - const headingText = $fieldset.find(".form-group:first").text().trim() || "表單填寫"; + // 若已經有可見文字(非 icon),直接通過 + const visibleText = $a.clone() + .children('.icon, i, svg, .caret') + .remove() + .end() + .text() + .trim(); - // 插入 legend 為第一個元素 - $fieldset.prepend(`${headingText}`); - } - }); - - // 處理「上一頁」按鈕 - $(".cmdBackward").each(function () { - $(this) - .attr("title", "上一頁") - .attr("aria-label", "上一頁"); + if (visibleText.length > 0) return; - if ($(this).find(".sr-only").length === 0) { - $(this).append('上一頁'); - } + // 取得可用標籤文字(優先順序) + let label = + $a.attr('aria-label') || + $a.attr('title') || + $a.find('i[title]').attr('title') || + ''; - $(this).find("i").attr("aria-hidden", "true"); - $(this).find(".icon").attr("aria-hidden", "true"); - }); + label = $.trim(label); - // 處理「下一頁」按鈕 - $(".cmdForward").each(function () { - $(this) - .attr("title", "下一頁") - .attr("aria-label", "下一頁"); + // 若真的完全沒有,就給一個保底(避免空字串違規) + if (!label) { + label = '功能按鈕'; + } - if ($(this).find(".sr-only").length === 0) { - $(this).append('下一頁'); - } + // aria-label 一定要有 + $a.attr('aria-label', label); - $(this).find("i").attr("aria-hidden", "true"); - $(this).find(".icon").attr("aria-hidden", "true"); - }); + // icon 對螢幕閱讀器隱藏 + $a.find('i, svg').attr('aria-hidden', 'true'); - $('.pagination li.active a').attr('aria-current', 'page'); + // 若沒有隱藏文字才補 + if ($a.find('.sr-only, .visually-hidden').length === 0) { + $a.append(`${label}`); + } + }); + }); + //播放驗證碼語音 + $(function () { + $('.ask-question') + .find('.controls button.fas.fa-volume-up') + .each(function () { + const $btn = $(this); + + // 已經補過就不重複處理 + if ($btn.attr('aria-label')) return; + + $btn.attr({ + 'aria-label': '播放驗證碼語音', + 'aria-controls': 'captcha_audio', + 'role': 'button' + }); + }); + }); + + //刪除td的id + $('td[id]').removeAttr('id'); + + //刪除空的title + $('[class*="title"]').each(function () { + const $el = $(this); + + // 取純文字(忽略 HTML tag、空白、換行) + const text = $.trim($el.text()); + + if (text === '') { + $el.remove(); + } + }); + //表單legend + $("fieldset").each(function () { + const $fieldset = $(this); + const hasLegend = $fieldset.children("legend").length > 0; + + if (!hasLegend) { + // 嘗試抓最上層的群組標題,例如 "Apply" + const headingText = $fieldset.find(".form-group:first").text().trim() || "表單填寫"; + + // 插入 legend 為第一個元素 + $fieldset.prepend(`${headingText}`); + } + }); - $('path').each(function () { + // 處理「上一頁」按鈕 + $(".cmdBackward").each(function () { $(this) - .attr('aria-hidden', 'true') - .attr('focusable', 'false') - .attr('tabindex', '-1'); + .attr("title", "上一頁") + .attr("aria-label", "上一頁"); + + if ($(this).find(".sr-only").length === 0) { + $(this).append('上一頁'); + } + + $(this).find("i").attr("aria-hidden", "true"); + $(this).find(".icon").attr("aria-hidden", "true"); }); - $('.controlplay a[role="radio"]').on('click', function () { - // 取消所有的 aria-checked 並還原文字 - $('.controlplay a[role="radio"]').attr('aria-checked', 'false'); - $('.controlplay a[role="radio"] span').each(function () { - var text = $(this).text().replace('(已選取)', ''); - $(this).text(text); + // 處理「下一頁」按鈕 + $(".cmdForward").each(function () { + $(this) + .attr("title", "下一頁") + .attr("aria-label", "下一頁"); + + if ($(this).find(".sr-only").length === 0) { + $(this).append('下一頁'); + } + + $(this).find("i").attr("aria-hidden", "true"); + $(this).find(".icon").attr("aria-hidden", "true"); }); - - // 設定目前被點擊的按鈕為已選取 - $(this).attr('aria-checked', 'true'); - var selectedText = $(this).text().trim(); - $(this).find('span').text(selectedText + '(已選取)'); - }); - // 遍历所有的 .s-annc__tag-wrap 元素 - $('.s-annc__tag-wrap').each(function() { - // 遍历 .s-annc__tag-wrap 下的每个 a 元素 - $(this).find('a').each(function() { - // 如果 .s-annc__tag 元素为空或没有内容 - if ($(this).find('.s-annc__tag').is(':empty')) { - // 移除该 a 元素 - $(this).remove(); - } + + $('.pagination li.active a').attr('aria-current', 'page'); + + $('path').each(function () { + $(this) + .attr('aria-hidden', 'true') + .attr('focusable', 'false') + .attr('tabindex', '-1'); }); - }); - $('.w-ba-banner__caption li button').each(function (index) { - const $slide = $('.w-ba-banner__slide').eq(index); - const title = $slide.attr('data-cycle-title')?.trim(); + $('.controlplay a[role="radio"]').on('click', function () { + // 取消所有的 aria-checked 並還原文字 + $('.controlplay a[role="radio"]').attr('aria-checked', 'false'); + $('.controlplay a[role="radio"] span').each(function () { + var text = $(this).text().replace('(已選取)', ''); + $(this).text(text); + }); - if (title) { - const fullText = title + '的pager'; - $(this).attr('aria-label', fullText); - $(this).attr('title', fullText); + // 設定目前被點擊的按鈕為已選取 + $(this).attr('aria-checked', 'true'); + var selectedText = $(this).text().trim(); + $(this).find('span').text(selectedText + '(已選取)'); + }); + // 遍历所有的 .s-annc__tag-wrap 元素 + $('.s-annc__tag-wrap').each(function() { + // 遍历 .s-annc__tag-wrap 下的每个 a 元素 + $(this).find('a').each(function() { + // 如果 .s-annc__tag 元素为空或没有内容 + if ($(this).find('.s-annc__tag').is(':empty')) { + // 移除该 a 元素 + $(this).remove(); + } + }); + }); + + $('.w-ba-banner__caption li button').each(function (index) { + const $slide = $('.w-ba-banner__slide').eq(index); + const title = $slide.attr('data-cycle-title')?.trim(); + + if (title) { + const fullText = title + '的pager'; + $(this).attr('aria-label', fullText); + $(this).attr('title', fullText); + } + }); + // 當 focus 到 menu link 時,顯示對應 ul + $('.sitemenu-item > a').on('focus', function () { + $(this).siblings('ul').addClass('show'); + }); + + // 當 focus 離開最後一個子選單 link 時,移除 .show + $('.sitemenu-item').each(function () { + const $submenuLinks = $(this).find('ul a'); + + if ($submenuLinks.length > 0) { + $submenuLinks.last().on('blur', function () { + // 當最後一個子項 blur 時關閉 ul + $(this).closest('ul').removeClass('show'); + }); + } else { + // 若沒有子項,當主選單項 blur 也隱藏 + $(this).find('> a').on('blur', function () { + $(this).siblings('ul').removeClass('show'); + }); + } + }); + if (document.documentElement.lang === 'zh_tw') { + document.documentElement.lang = 'zh-Hant'; + } + //tab鍵按下 + $(document).on('keydown', function(e) { + if (e.key === "Tab" || e.keyCode === 9) { + $('.header-nav, .dropdowns').css('display', 'block'); } }); - // 當 focus 到 menu link 時,顯示對應 ul - $('.sitemenu-item > a').on('focus', function () { - $(this).siblings('ul').addClass('show'); - }); - // 當 focus 離開最後一個子選單 link 時,移除 .show - $('.sitemenu-item').each(function () { - const $submenuLinks = $(this).find('ul a'); + function handleHover() { + // 先統一移除舊事件,避免重複綁定 + $('li').off('mouseenter mouseleave focusin focusout'); - if ($submenuLinks.length > 0) { - $submenuLinks.last().on('blur', function () { - // 當最後一個子項 blur 時關閉 ul - $(this).closest('ul').removeClass('show'); - }); - } else { - // 若沒有子項,當主選單項 blur 也隱藏 - $(this).find('> a').on('blur', function () { - $(this).siblings('ul').removeClass('show'); - }); - } - }); - if (document.documentElement.lang === 'zh_tw') { - document.documentElement.lang = 'zh-Hant'; - } - //tab鍵按下 - $(document).on('keydown', function(e) { - if (e.key === "Tab" || e.keyCode === 9) { - $('.header-nav, .dropdowns').css('display', 'block'); - } - }); + if ($(window).width() > 769) { + // 1. 處理滑鼠 Hover + $('li').hover( + function() { $(this).children('ul').addClass('show'); }, + function() { $(this).children('ul').removeClass('show'); } + ); - //li被hover - function handleHover() { - if ($(window).width() > 769) { - $('li').off('mouseenter mouseleave').hover( - function() { - $(this).children('ul').addClass('show'); - }, - function() { - $(this).children('ul').removeClass('show'); + // 2. 處理鍵盤 Tab 聚焦 (關鍵修正) + $('li').on('focusin', function() { + // 當焦點進入 li 或其子元素時,顯示選單 + $(this).children('ul').addClass('show'); + }); + + $('li').on('focusout', function() { + var $this = $(this); + // 延遲偵測焦點是否真的「離開」了這個 li 區塊 + setTimeout(function() { + // 如果新的焦點不在當前這個 li 裡面,才移除 show + if ($this.find(':focus').length === 0) { + $this.children('ul').removeClass('show'); + } + }, 10); + }); } - ); - } else { - $('li').off('mouseenter mouseleave'); // 移除 hover 事件 } - } - // 先執行一次 - handleHover(); - - // 監聽視窗大小變化 - $(window).on('resize', function() { + // 初始化與 Resize 監聽保持不變 handleHover(); - }); - //刪除空的h1 - $('h1').each(function() { - if ($(this).text().trim() === '') { - $(this).remove(); - } - }); - //refresh_btn加上aria-label - $('#refresh_btn').each(function() { - var $this = $(this); - - // 如果 button 尚未有 aria-label,則新增 - if (!$this.attr('aria-label')) { - $this.attr('aria-label', '重新整理'); - } - }); - //無障礙會員表格刪除沒有顯示的th - $('.i-member-tr-head').each(function() { - if ($(this).css('display') === 'none') { - $(this).remove(); - } - }); - //無障礙公告a是空的 - $('.w-annc__subtitle').each(function () { - var $this = $(this); - var $link = $this.find('a'); - - // 檢查 a 是否存在,且去除空白後是否為空字串 - if ($link.length && $link.text().trim() === '') { - $this.remove(); // 移除外層 .w-annc__subtitle + $(window).on('resize', function() { + handleHover(); + }); + //刪除空的h1 + $('h1').each(function() { + if ($(this).text().trim() === '') { + $(this).remove(); } }); - //無障礙單位轉換 - $("[style*='font-size']").each(function() { - var fontSize = $(this).css("font-size"); - if (fontSize.includes("px")) { - var pxValue = parseFloat(fontSize); // 取得數值 - var emValue = pxValue / 16; // 假設 1em = 16px - $(this).css("font-size", emValue + "em"); + //refresh_btn加上aria-label + $('#refresh_btn').each(function() { + var $this = $(this); + + // 如果 button 尚未有 aria-label,則新增 + if (!$this.attr('aria-label')) { + $this.attr('aria-label', '重新整理'); + } + }); + //無障礙會員表格刪除沒有顯示的th + $('.i-member-tr-head').each(function() { + if ($(this).css('display') === 'none') { + $(this).remove(); + } + }); + //無障礙公告a是空的 + $('.w-annc__subtitle').each(function () { + var $this = $(this); + var $link = $this.find('a'); + + // 檢查 a 是否存在,且去除空白後是否為空字串 + if ($link.length && $link.text().trim() === '') { + $this.remove(); // 移除外層 .w-annc__subtitle } + }); + //無障礙單位轉換 + $(function() { + $('style:contains("font-size")').each(function() { + let css = $(this).html(); + css = css.replace(/font-size\s*:\s*([\d.]+)px\s*;?/gi, (_, px) => + `font-size: ${(parseFloat(px) / 16).toFixed(3)}em;` + ); + $(this).html(css); + }); + }); + + $("[style*='font-size']").each(function() { + var fontSize = $(this).css("font-size"); + if (fontSize.includes("px")) { + var pxValue = parseFloat(fontSize); // 取得數值 + var emValue = pxValue / 16; // 假設 1em = 16px + $(this).css("font-size", emValue + "em"); + } + }); + $("[style*='font-size']").each(function() { + var fontSize = $(this).css("font-size"); + if (fontSize.includes("pt")) { + var ptValue = parseFloat(fontSize); // 取得數值 + var emValue = ptValue / 12; // 1em = 12pt(一般轉換標準) + $(this).css("font-size", emValue + "em"); + } }); $("[style*='font-size']").each(function() { - var fontSize = $(this).css("font-size"); - if (fontSize.includes("pt")) { - var ptValue = parseFloat(fontSize); // 取得數值 - var emValue = ptValue / 12; // 1em = 12pt(一般轉換標準) - $(this).css("font-size", emValue + "em"); + var styleAttr = $(this).attr("style"); // 取得原始 style 屬性字串 + var match = styleAttr.match(/font-size\s*:\s*(\d+(?:\.\d+)?)pt/i); + if (match) { + var ptValue = parseFloat(match[1]); + var emValue = ptValue / 12; + // 替換 style 屬性字串中的 pt 為 em + var newStyle = styleAttr.replace(/font-size\s*:\s*\d+(?:\.\d+)?pt/i, `font-size: ${emValue}em`); + $(this).attr("style", newStyle); } - }); - $("[style*='font-size']").each(function() { - var styleAttr = $(this).attr("style"); // 取得原始 style 屬性字串 - var match = styleAttr.match(/font-size\s*:\s*(\d+(?:\.\d+)?)pt/i); - if (match) { - var ptValue = parseFloat(match[1]); - var emValue = ptValue / 12; - // 替換 style 屬性字串中的 pt 為 em - var newStyle = styleAttr.replace(/font-size\s*:\s*\d+(?:\.\d+)?pt/i, `font-size: ${emValue}em`); - $(this).attr("style", newStyle); + }); + //表格scope + $("table").each(function() { + $(this).find("tr").each(function(rowIndex) { + $(this).find("th").each(function(colIndex) { + if (rowIndex === 0) { + // 第一列的 th,適用於該欄 + $(this).attr("scope", "col"); + } else if (colIndex === 0) { + // 其他列的第一個 th,適用於該行 + $(this).attr("scope", "row"); + } + }); + }); + }); + $(".banner-pager button").addClass('banner-pagerbtn'); + $(".banner-pager button").attr("type","button"); + $(".banner-pager button").append('下一張'); + $("button").attr("role", "button"); + $("select").attr("title","選擇類別"); + $(".jarallax-video-audio").attr("role", "button"); + $('button').each(function() { + var $this = $(this); + if (!$this.attr('title') || $this.attr('title').trim() === '') { + $this.attr('title', '按鈕'); + } + }); + + function removeEmptyTitles() { + // 定義所有需要檢查的 class + const targetClasses = [ + '.w-annc__widget-title', + '.widget-title', + '.show-title', + '.event-annc-title', + '.annc-title', + '.sitemenu-title', + '.widget-link__widget-title' + ]; + + // 將陣列轉換為 jQuery 選擇器字串 + const selector = targetClasses.join(', '); + + $(selector).each(function() { + // 使用 $.trim 移除空格、換行符號 + // 然後檢查裡面是否完全沒有文字 + const textContent = $.trim($(this).text()); + + if (textContent === "") { + // 如果沒有文字,則將整個元素刪除 + $(this).remove(); + } + }); } - }); - //表格scope - $("table").each(function() { - $(this).find("tr").each(function(rowIndex) { - $(this).find("th").each(function(colIndex) { - if (rowIndex === 0) { - // 第一列的 th,適用於該欄 - $(this).attr("scope", "col"); - } else if (colIndex === 0) { - // 其他列的第一個 th,適用於該行 - $(this).attr("scope", "row"); + // 執行 function + $(document).ready(function() { + removeEmptyTitles(); + }); + $(".w-annc__img-wrap a").each(function () { + var $this = $(this); + // 確保 內沒有文字節點 (避免重複添加) + if ($this.text().trim() === "") { + $this.append('公告圖片'); + } + }); + $(".widget-link__widget-title").each(function () { + if ($(this).text().trim() === "") { + $(this).append('公告標題'); + } + }); + $(".sitemenu-title").each(function () { + if ($(this).text().trim() === "") { + $(this).append('次選單'); + } + }); + $(".annc-title").each(function () { + if ($(this).text().trim() === "") { + $(this).append('內頁公告標題'); + } + }); + $(".event-annc-title").each(function () { + if ($(this).text().trim() === "") { + $(this).append('內頁活動公告標題'); + } + }); + $(".show-title").each(function () { + if ($(this).text().trim() === "") { + $(this).append('內頁活動公告標題'); + } + }); + $(".w-annc__widget-title").each(function () { + if ($(this).text().trim() === "") { + $(this).append('公告標題'); + } + }); + $(".widget-title").each(function () { + if ($(this).text().trim() === "") { + $(this).append('網路資源標題'); + } + }); + $(".widget-title").each(function () { + if ($(this).text().trim() === "") { + $(this).append('網路資源標題'); + } + }); + $('input').each(function() { + var $this = $(this); + if (!$this.attr('title') || $this.attr('title').trim() === '') { + $this.attr('title', '網內搜尋'); + } + }); + $('.rucaptcha-image').each(function() { + var $button = $(this).next('button'); // 取得緊接在 .rucaptcha-image 之後的 button + if ($button.length && !$button.attr('aria-label')) { + $button.attr('aria-label', '播放驗證碼語音'); + } + }); + // img無障礙 + $(function() { + /** + * 圖片無障礙 AA 級補強 (人工檢測優化版) + */ + function fixImageAccessibility() { + $('img').each(function() { + var $img = $(this); + var $parentA = $img.closest('a'); // 取得父層連結 + var currentAlt = ($img.attr('alt') || '').trim(); + var currentTitle = ($img.attr('title') || '').trim(); + + // 判定該連結是否「僅靠圖片傳達目的」(連結內無文字) + var isLinkImage = $parentA.length > 0 && $parentA.text().replace(/\u00a0/g, '').trim() === ""; + + // --- 1. 處理「裝飾性圖片」標註 --- + if (currentAlt === "裝飾圖片" || currentTitle === "裝飾圖片") { + $img.attr('alt', ''); + $img.removeAttr('title'); + return; + } + + // --- 2. 解決【alt 與 title 相同】的問題 --- + if (currentAlt !== '' && currentAlt === currentTitle) { + $img.removeAttr('title'); + currentTitle = ''; + } + + // --- 3. 處理「缺失 alt」或「空的 alt」補強邏輯 --- + // 修改點:若是具有連結用途的圖片,alt 不可為空 + if (currentAlt === '') { + let finalAlt = ""; + + // A. 優先嘗試從父層 () 的 title 抓取 + var parentATitle = $parentA.attr('title'); + + // B. 嘗試抓取同區域內的標題文字 + var nearbyTitle = $parentA.find('h1, h2, h3, h4, h5, h6').first().text().trim(); + + if (parentATitle && parentATitle.trim() !== '') { + // 移除 title 內的視窗提示字眼,只取純文字作為 alt + finalAlt = parentATitle.replace(/\(在本視窗開啟\)|\(在新視窗開啟\)/g, '').trim(); + } else if (nearbyTitle !== '') { + finalAlt = nearbyTitle; + } else if (isLinkImage) { + // 如果是連結圖片且都抓不到文字,給予預設值「連結」避免檢測失敗 + finalAlt = "連結"; + } + + $img.attr('alt', finalAlt); + + // --- 4. 二次清理 --- + if (currentTitle !== '' && currentTitle === finalAlt) { + $img.removeAttr('title'); + } } }); - }); - }); - $(".banner-pager button").addClass('banner-pagerbtn'); - $(".banner-pager button").attr("type","button"); - $(".banner-pager button").append('下一張'); - $("button").attr("role", "button"); - $("select").attr("title","選擇類別"); - $(".jarallax-video-audio").attr("role", "button"); - $('button').each(function() { - var $this = $(this); - if (!$this.attr('title') || $this.attr('title').trim() === '') { - $this.attr('title', '按鈕'); - } - }); - $('img').each(function () { - const $img = $(this); - // 如果沒有 alt 或 alt 是空字串 - if (!$img.attr('alt') || $img.attr('alt').trim() === '') { - let altText = ''; - // 先抓 img 的 title - if ($img.attr('title')) { - altText = $img.attr('title'); - } - // 再抓外層 a 的 title - else if ($img.closest('a').attr('title')) { - altText = $img.closest('a').attr('title'); - } - // 如果都沒有,給預設文字 - else { - altText = '圖片'; - } - $img.attr('alt', altText); } - }); - $('img').each(function() { - var $this = $(this); - if (!$this.attr('alt') || $this.attr('alt').trim() === '') { - $this.attr('alt', '這是一張圖片'); - } - }); - $('img').each(function() { - var $this = $(this); - if (!$this.attr('title') || $this.attr('title').trim() === '') { - $this.attr('title', '這是一張圖片'); - } + + setTimeout(fixImageAccessibility, 500); }); - $('img').each(function() { - var $this = $(this); - - // 檢查 img 的 alt 屬性是否為 "裝飾圖片" - if ($this.attr('alt') === "裝飾圖片" || $this.attr('title') === "裝飾圖片") { - // 設定 alt 為空字串 - $this.attr('alt', ''); - - // 移除 title 屬性 - $this.removeAttr('title'); + // 有連結目的之所有a標籤的無障礙處理 + $(function() { + /** + * 全站無障礙 AA 級補強 (整合 HM1240400C 與 HM1240401C 規範) + */ + function fixAccessibilityAll() { + // --- 0. 環境與語言偵測 --- + var urlParams = new URLSearchParams(window.location.search); + var isEditMode = urlParams.get('editmode') === 'on'; + + var pageLang = $('html').attr('lang') || 'zh-Hant'; + var isEn = pageLang.toLowerCase().startsWith('en'); + + var i18n = { + langBtn: isEn ? 'Open language menu in this window' : '在本視窗開啟語言選單', + newWin: isEn ? '(Open in new window)' : '(在新視窗開啟)', + selfWin: isEn ? '(Open in this window)' : '(在本視窗開啟)', + link: isEn ? 'Link' : '連結', + openImg: isEn ? 'Open image' : '開啟圖片內容', + sort: isEn ? 'Sort' : '排序' + }; + + // --- Part 1: 語言選單修正 --- + var $langBtn = $('#languagebutton'); + if ($langBtn.length > 0) { + $langBtn.attr('title', i18n.langBtn); + $langBtn.removeAttr('aria-label'); + } + + // --- Part 2: 全站連結與圖片邏輯處理 --- + $('a').each(function() { + var $a = $(this); + + // 【條件判斷:若 a 標籤的 class 等於或包含 navbar-brand,則不執行此 function】 + if ($a.hasClass('navbar-brand')) { + return; + } + var href = ($a.attr('href') || '').toLowerCase(); + if (!href || href.startsWith('javascript:')) return; + + // 清理   與 空白 + var linkText = $a.text().replace(/\u00a0/g, '').trim(); + var $img = $a.find('img'); + var $icons = $a.find('i, svg'); // 偵測其他元素 (如 FontAwesome, SVG) + var currentTitle = ($a.attr('title') || '').trim(); + + // --- 【修正 1:隱藏空連結】 --- + if (!isEditMode && linkText === "" && $img.length === 0 && $icons.length === 0) { + $a.hide(); + return; + } + + // --- 【修正 2:符合 HM1240401C 的文字與標題防禦】 --- + // 若連結文字為空、或文字內容為無效的 URL 路徑,自動補上語意文字 + if (linkText === "" || linkText.startsWith('/') || linkText.includes('?')) { + var accessibleText = i18n.link; // 預設名稱 + + // 智慧判斷:若為排序連結,可顯示為「排序」 + if (href.includes('sort')) { + accessibleText = i18n.sort; + } + + var $span = $a.find('span'); + if ($span.length > 0) { + $span.text(accessibleText); + } else { + $a.append('' + accessibleText + ''); + $span = $a.find('span').last(); + } + + // 套用視覺隱藏但螢幕閱讀器可讀取之樣式 + $span.css({ + 'display': 'inline-block', + 'position': 'absolute', + 'width': '1px', + 'height': '1px', + 'padding': '0', + 'margin': '-1px', + 'overflow': 'hidden', + 'clip': 'rect(0, 0, 0, 0)', + 'white-space': 'nowrap', + 'border': '0' + }); + + linkText = accessibleText; // 更新變數以便後續比對 + } + + // A. 視窗動作規範 + var isNewWindow = ($a.attr('target') === '_blank'); + var windowTask = isNewWindow ? i18n.newWin : i18n.selfWin; + + // B. 偵測檔案類型 + var fileExt = ""; + var fileMatches = href.match(/\.(pdf|doc|docx|xls|xlsx|ppt|pptx|odt|ods|odp|zip|rar|jpg|png|csv)$/); + if (fileMatches) { + fileExt = fileMatches[1].toLowerCase(); + } + + // C. 智慧修正標題 (移除冗餘資訊) + var hasBadSymbol = currentTitle.includes('()') || + currentTitle.includes('另開新視窗') || + currentTitle.includes('Open in new window') || + currentTitle.includes('/uploads/'); + + // 如果 title 是 URL,視為壞標籤並重置 + if (currentTitle.startsWith('/') || currentTitle.includes('?')) { + currentTitle = ""; + } + + // D. 處理【HM1240400C】:圖片與文字毗鄰時,替代文字只能有一份 + if (($img.length > 0 || $icons.length > 0) && linkText !== "") { + if ($img.length > 0) { + // 強制圖片設為裝飾性 + $img.attr('alt', ''); + } + + // 針對「視覺隱藏但存在於 DOM」的文字 + var $hiddenSpans = $a.find('span').filter(function() { + return $(this).css('display') === 'none' || $(this).attr('style')?.includes('display: none'); + }); + + if ($hiddenSpans.length > 0) { + $hiddenSpans.each(function() { + var $span = $(this); + if ($span.text().includes('/uploads/')) { + $span.text(i18n.openImg); + } + + // 轉換為無障礙友善的隱藏樣式 + $span.css({ + 'display': 'inline-block', + 'position': 'absolute', + 'width': '1px', + 'height': '1px', + 'padding': '0', + 'margin': '-1px', + 'overflow': 'hidden', + 'clip': 'rect(0, 0, 0, 0)', + 'white-space': 'nowrap', + 'border': '0' + }); + }); + } + } + + // E. 重新計算標題與替代文字 (針對不同情境) + 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 if (linkText.length <= 8) { + $a.attr('title', linkText + " " + windowTask); + } else { + $a.attr('title', windowTask); + } + } + } + + // 【HM1240401C 補強】若連結包含其他多媒體內容 (如 i 標籤、SVG),需補齊 title 屬性 + var hasOtherContent = ($img.length > 0 || $icons.length > 0); + if (hasOtherContent && currentTitle === "") { + var newTitle = linkText + " " + windowTask; + $a.attr('title', newTitle.trim()); + } + + // F. 最終清理:移除冗餘 label + $a.removeAttr('aria-label'); + }); } + + // 延遲執行確保 DOM 載入完成 + setTimeout(function() { + fixAccessibilityAll(); + }, 800); + }); + + // if (location.href.search('editmode=on') === -1) { + // // 1. 處理圖片 alt 重複問題 (保持原邏輯但優化) + // $('a').has('img').each(function () { + // const $link = $(this); + // const linkText = $link.text().trim(); + + // $link.find('img').each(function () { + // const $img = $(this); + // const altText = $img.attr('alt')?.trim(); + + // if (altText && altText === linkText) { + // $img.attr('alt', ''); + // } + // }); + // }); + // } + $(function() { + function smartFixIcons() { + $('i').each(function() { + var $icon = $(this); + var $parent = $icon.parent(); + + // 1. 基本安全:所有圖示先對螢幕閱讀器隱藏 (避免讀出 Unicode 亂碼) + $icon.attr('aria-hidden', 'true'); + + // 2. 檢查:如果已經有 aria-label 或旁邊已有 sr-only,就不重複處理 + if ($icon.attr('aria-label') || $icon.next('.sr-only').length > 0 || $parent.find('.sr-only').length > 0) { + return; + } + + var cls = ($icon.attr('class') || '').toLowerCase(); + var labelText = ''; + + // --- 策略 A:常見資訊型圖示 (自動識別內容意義) --- + if (cls.includes('calendar')) labelText = '日期:'; + else if (cls.includes('user') || cls.includes('male')) labelText = '發布者:'; + else if (cls.includes('tag')) labelText = '標籤:'; + else if (cls.includes('clock') || cls.includes('time')) labelText = '時間:'; + else if (cls.includes('map-marker') || cls.includes('location')) labelText = '地點:'; + else if (cls.includes('phone')) labelText = '電話:'; + else if (cls.includes('envelope') || cls.includes('mail')) labelText = '信箱:'; + else if (cls.includes('folder')) labelText = '分類:'; + else if (cls.includes('eye')) labelText = '瀏覽次數:'; + else if (cls.includes('download')) labelText = '下載檔案:'; + + // --- 策略 B:功能性按鈕 (如果 i 放在