1/* ============================================= 2 customize-filter.js 3 Filter Modal + Personalize Toast + Coachmark 4============================================= */ 5(function ($) { 6 'use strict'; 7 8 // å¤èªç³»æåï¼ç± _CustomizeFilter.cshtml å¨ #modal-filter è¼¸åº data-i18n-*ï¼LanguageMulti ç CustomizeFilter projectï¼ã 9 // 屬æ§ä¸å卿çºç©ºæéåè±æï¼é¿å partial æ²æ¸²ææé¡¯ç¤ºç©ºç½ã 10 function _t(key, fallback) { 11 var el = document.getElementById('modal-filter'); 12 var value = el && el.getAttribute('data-i18n-' + key); 13 return value || fallback; 14 } 15 16 /* ============================================= 17 ui-modal éç¨ééï¼äºä»¶å§æ´¾ï¼ 18 ============================================= */ 19 function openModal(modal) { 20 modal.style.display = 'flex'; 21 requestAnimationFrame(function () { 22 requestAnimationFrame(function () { modal.classList.add('is-open'); }); 23 }); 24 } 25 function closeModal(modal) { 26 modal.classList.remove('is-open'); 27 modal.addEventListener('transitionend', function handler(e) { 28 if (e.target === modal) { 29 modal.style.display = 'none'; 30 modal.removeEventListener('transitionend', handler); 31 } 32 }); 33 } 34 document.addEventListener('click', function (event) { 35 var trigger = event.target.closest('[data-ui-modal-open]'); 36 if (trigger) { 37 var modal = document.getElementById(trigger.getAttribute('data-ui-modal-open')); 38 if (modal) openModal(modal); 39 return; 40 } 41 var closeBtn = event.target.closest('.ui-modal__close, [data-ui-modal-close]'); 42 if (closeBtn) { 43 var m = closeBtn.closest('.ui-modal'); 44 if (m && m.id !== 'modal-filter') closeModal(m); 45 return; 46 } 47 if (event.target.classList.contains('ui-modal')) { 48 // modal-filter çèæ¯é»æç±å°å±¬ handler èçï¼è§¸ç¼ coachmarkï¼ 49 if (event.target.id === 'modal-filter') return; 50 closeModal(event.target); 51 } 52 }); 53 54 /* ============================================= 55 UIAccordion å ä»¶ 56 ============================================= */ 57 function Accordion($el) { 58 this.$el = $el; 59 this.multiple = $el.hasClass('ui-accordion--multiple'); 60 this.$triggers = this.$el.find('.ui-accordion__trigger'); 61 this.bindEvents(); 62 this.init(); 63 } 64 65 Accordion.prototype.init = function () { 66 this.$triggers.each(function () { 67 var $trigger = $(this); 68 if ($trigger.attr('aria-expanded') === 'true') { 69 var $item = $trigger.closest('.ui-accordion__item'); 70 var $panel = $item.find('.ui-accordion__panel'); 71 $item.addClass('is-open'); 72 $panel.height($panel[0].scrollHeight); 73 } 74 }); 75 }; 76 77 Accordion.prototype.bindEvents = function () { 78 var self = this; 79 this.$triggers.on('click', function () { 80 self.toggle($(this)); 81 }); 82 }; 83 84 Accordion.prototype.toggle = function ($trigger) { 85 var $item = $trigger.closest('.ui-accordion__item'); 86 var $panel = $item.find('.ui-accordion__panel'); 87 var isOpen = $item.hasClass('is-open'); 88 89 if (isOpen) { 90 $panel.height($panel[0].scrollHeight); 91 requestAnimationFrame(function () { 92 $item.removeClass('is-open'); 93 $panel.height(0); 94 }); 95 $trigger.attr('aria-expanded', 'false'); 96 } else { 97 if (!this.multiple) { 98 var $groupScope = this.$el.closest('.filter-accordion-grid').length 99 ? this.$el.closest('.filter-accordion-grid') 100 : this.$el; 101 102 $groupScope.find('.ui-accordion__item.is-open').each(function () { 103 var $otherItem = $(this); 104 var $otherPanel = $otherItem.find('.ui-accordion__panel'); 105 var $otherTrigger = $otherItem.find('.ui-accordion__trigger'); 106 107 $otherPanel.height($otherPanel[0].scrollHeight); 108 requestAnimationFrame(function () { 109 $otherItem.removeClass('is-open'); 110 $otherPanel.height(0); 111 }); 112 $otherTrigger.attr('aria-expanded', 'false'); 113 }); 114 } 115 116 $item.addClass('is-open'); 117 $panel.height($panel[0].scrollHeight); 118 $trigger.attr('aria-expanded', 'true'); 119 } 120 }; 121 122 window.UIAccordion = Accordion; 123 124 /* ============================================= 125 DOM Ready 126 ============================================= */ 127 $(function () {
128 var $modal = $('#modal-filter'); 129 if (!$modal.length) return; 130 131 /* --- Topics API è¨å®ï¼ééå¾ç«¯ proxy é¿å CORSï¼ --- */ 132 var TOPICS_API_URL = '/api/track/topics'; 133 var _topicsCache = null; // API è³æå¿«åï¼fetch 䏿¬¡å³å¯ï¼ 134 var _topicsLoading = false; 135 136 /* --- å¤èªç³»æ¬ä½å°æ --- */ 137 var _langFieldMap = { 138 'en': 'topicNameEn', 139 'en-us': 'topicNameEn', 140 'zh-tw': 'topicNameZhTw', 141 'zh-cn': 'topicNameZhCn', 142 'ja-jp': 'topicNameJa', 143 'ko-kr': 'topicNameKo', 144 'th': 'topicNameTh' 145 }; 146 var _currentLang = ($('#hidGlobalLang').val() || 'en').toLowerCase(); 147 148 function _getTopicLabel(topic) { 149 var field = _langFieldMap[_currentLang]; 150 return (field && topic[field]) || topic.topicNameEn || ''; 151 } 152 153 /* --- 馿¬¡ / åè¨ªçæ ç®¡çï¼localStorage æä¹ åï¼ --- */ 154 var STORAGE_KEY_APPLIED = 'adv_filter_applied'; // '1' = æ¾ Apply é 155 var STORAGE_KEY_PREFS = 'adv_filter_preferences'; // JSON é£åï¼topicIdï¼ 156 157 var _hasApplied = localStorage.getItem(STORAGE_KEY_APPLIED) === '1'; 158 var _savedSelections = (function () { 159 try { return JSON.parse(localStorage.getItem(STORAGE_KEY_PREFS)) || []; } 160 catch (e) { return []; } 161 })(); 162 var _isRevisit = false; 163 164 var _preferenceRevision = 0; 165 var _publishedIds = null; 166 var _confirmedSelections = _savedSelections.slice(); 167 var _savingPreferences = false; 168 var _tagRequests = new Set(); 169 170 function _consentGranted() { 171 return !!(window.AdvCookieConsent && window.AdvCookieConsent.isConsentGranted()); 172 } 173 174 function _publishPreferences(ids) { 175 _publishedIds = ids.slice(); 176 document.dispatchEvent(new CustomEvent('adv:preferences-changed', { detail: { topicIds: ids.slice() } })); 177 } 178 window.AdvHomePreferences = { 179 getSnapshot: function () { return _publishedIds === null ? null : _publishedIds.slice(); } 180 }; 181 182 function _persistPreferences() {
183 if (_savingPreferences || !_consentGranted()) return Promise.resolve(false); 184 _savingPreferences = true; 185 var revision = ++_preferenceRevision; 186 var ids = _savedSelections.slice(); 187 var $buttons = $modal.find('#btn-apply-preferences, #btn-revisit-done'); 188 $buttons.prop('disabled', true); 189 return _requestTags('POST', ids).then(function () { 190 if (revision !== _preferenceRevision || !_consentGranted()) return false; 191 _confirmedSelections = ids.slice(); 192 _serverTagsSynced = true; 193 try { 194 localStorage.setItem(STORAGE_KEY_APPLIED, '1'); 195 localStorage.setItem(STORAGE_KEY_PREFS, JSON.stringify(ids)); 196 } catch (e) { /* Preferences remain available for this page when storage is unavailable. */ } 197 _publishPreferences(ids); 198 return true; 199 }).catch(function () { 200 if (revision !== _preferenceRevision) return false; 201 _savedSelections = _confirmedSelections.slice(); 202 if (_consentGranted()) 203 $modal.find('.filter-focus__placeholder') 204 .text('Unable to save preferences. Please try again.').addClass('is-error').show(); 205 return false; 206 }).then(function (ok) { 207 _savingPreferences = false; 208 $buttons.prop('disabled', false); 209 return ok; 210 }); 211 } 212 213 var MA_TAGS_ENDPOINT = '/api/home/preferences'; 214 var MA_TAGS_WEBSITE = 'Corp'; 215 216 // Keep the SDK's visitor identity and API headers, with explicit status and cancellation handling. 217 function _requestTags(method, ids) { 218 if (!_consentGranted() || typeof AdvMarketingAutomationTracker === 'undefined') 219 return Promise.reject(new Error('Preferences are unavailable.')); 220 var controller = new AbortController(); 221 _tagRequests.add(controller); 222 var timer = setTimeout(function () { controller.abort(); }, 8000); 223 var timeout = new Promise(function (_, reject) { 224 controller.signal.addEventListener('abort', function () { reject(new Error('Preferences request cancelled.')); }); 225 }); 226 var request = Promise.resolve().then(function () { 227 return new AdvMarketingAutomationTracker().getVisitorId(); 228 }).then(function (visitorId) { 229 if (controller.signal.aborted || !_consentGranted()) throw new Error('Preferences request cancelled.'); 230 var options = { 231 method: method, signal: controller.signal, cache: 'no-store', 232 headers: { 'X-Requested-With': 'AI-Tags' } 233 }; 234 var endpoint = MA_TAGS_ENDPOINT; 235 if (method === 'POST') { 236 options.headers['Content-Type'] = 'application/json'; 237 options.body = JSON.stringify({ visitorId: visitorId, website: MA_TAGS_WEBSITE, 238 topicId: ids, visitedAt: new Date().toISOString() }); 239 } else endpoint += '?visitorId=' + encodeURIComponent(visitorId); 240 return fetch(endpoint, options); 241 }).then(function (response) { 242 if (method === 'GET' && response.status === 404) return []; 243 if (!response.ok) throw new Error('Preferences request failed.'); 244 return method === 'GET' ? response.json() : true; 245 }); 246 return Promise.race([request, timeout]).finally(function () { 247 clearTimeout(timer); 248 _tagRequests.delete(controller); 249 }); 250 } 251 252 var _serverTagsSynced = false; 253 var _serverSyncPromise = null; 254 255 function _syncTagsFromServer(done) { 256 if (_serverTagsSynced || !_consentGranted()) { done(); return; } 257 if (!_serverSyncPromise) { 258 var revision = _preferenceRevision; 259 var pending = _requestTags('GET').then(function (data) { 260 if (!Array.isArray(data) || !data.every(function (id) { return /^[0-9]{1,10}$/.test(String(id)); })) 261 throw new Error('Invalid preferences response.'); 262 if (revision !== _preferenceRevision || !_consentGranted()) return; 263 var ids = Array.from(new Set(data.map(String))).sort(); 264 _savedSelections = ids; 265 _confirmedSelections = ids.slice(); 266 _hasApplied = ids.length > 0;
267 _serverTagsSynced = true; 268 try { 269 localStorage.setItem(STORAGE_KEY_PREFS, JSON.stringify(ids)); 270 if (_hasApplied) localStorage.setItem(STORAGE_KEY_APPLIED, '1'); 271 else localStorage.removeItem(STORAGE_KEY_APPLIED); 272 } catch (e) { /* Storage is optional. */ } 273 _publishPreferences(ids); 274 }).catch(function () { 275 if (revision === _preferenceRevision) _publishPreferences([]); 276 }).then(function () { 277 if (_serverSyncPromise === pending) _serverSyncPromise = null; 278 }); 279 _serverSyncPromise = pending; 280 } 281 _serverSyncPromise.then(done); 282 } 283 284 var _lastConsent = null; 285 function _bootstrapServerSync() { 286 var allowed = _consentGranted(); 287 if (allowed === _lastConsent) return; 288 _lastConsent = allowed; 289 if (!allowed) { 290 ++_preferenceRevision; 291 _tagRequests.forEach(function (controller) { controller.abort(); }); 292 _serverSyncPromise = null; 293 _serverTagsSynced = false; 294 _publishPreferences([]); 295 return; 296 } 297 window.AdvCookieConsent.holdToast(); 298 _syncTagsFromServer(function () { window.AdvCookieConsent.releaseToast(); }); 299 } 300 document.addEventListener('cookieyes_consent_update', _bootstrapServerSync); 301 window.addEventListener('cookieyes_consent_update', _bootstrapServerSync); 302 _bootstrapServerSync(); 303 304 // åå¾ç®åææå¾é¸ç checkbox topicId 305 function _getCurrentSelections() { 306 var selections = []; 307 $modal.find('.ui-checkbox__input:checked').each(function () { 308 var id = $(this).attr('data-topic-id'); 309 if (id) selections.push(id); 310 }); 311 return selections.sort(); 312 } 313 314 // æ¯å°å ©çµ selections æ¯å¦ç¸å 315 function _selectionsChanged(a, b) { 316 if (a.length !== b.length) return true; 317 for (var i = 0; i < a.length; i++) { 318 if (a[i] !== b[i]) return true; 319 } 320 return false; 321 } 322 323 // è¼å ¥å²åçå¾é¸çæ å° UI 324 function _restoreSavedSelections() { 325 $modal.find('.ui-checkbox__input').prop('checked', false); 326 $modal.find('.ui-checkbox__input[data-topic-id]').each(function () { 327 var id = $(this).attr('data-topic-id');
328 if (_savedSelections.indexOf(id) > -1) { 329 $(this).prop('checked', true); 330 } 331 }); 332 // é建å³å´ chips è badge 333 rebuildFocusList(); 334 } 335 336 /* --- å¾ API è¼å ¥ Topics 並渲æ Accordion --- */ 337 function _fetchAndRenderTopics(callback) { 338 var $grid = $modal.find('.filter-accordion-grid'); 339 var $loading = $grid.find('.filter-loading'); 340 341 // å·²æå¿«å â ç´æ¥ç¨ 342 if (_topicsCache) { 343 _renderAccordions(_topicsCache); 344 if (callback) callback(); 345 return; 346 } 347 348 // æ£å¨è¼å ¥ä¸ â çå¾ 349 if (_topicsLoading) return; 350 _topicsLoading = true; 351 352 $loading.show(); 353 354 $.ajax({ 355 url: TOPICS_API_URL, 356 dataType: 'json', 357 timeout: 15000 358 }).done(function (data) { 359 _topicsCache = data || []; 360 $loading.hide(); 361 _renderAccordions(_topicsCache); 362 if (callback) callback(); 363 }).fail(function () { 364 $loading.hide(); 365 $grid.append('<p class="filter-loading" style="color:var(--color-text-secondary);">Failed to load topics. Please try again later.</p>'); 366 }).always(function () { 367 _topicsLoading = false; 368 }); 369 } 370 371 function _renderAccordions(topics) { 372 var $grid = $modal.find('.filter-accordion-grid'); 373 374 $grid.find('.ui-accordion, .filter-loading').remove(); 375 376 // ä¾ topicGroupName åçµ 377 var groups = []; 378 var groupMap = {}; 379 topics.forEach(function (t) { 380 var gName = t.topicGroupName || 'Other'; 381 if (!groupMap[gName]) { 382 groupMap[gName] = { name: gName, items: [] }; 383 groups.push(groupMap[gName]); 384 } 385 groupMap[gName].items.push(t); 386 }); 387 388 groups.sort(function (a, b) { return a.name.localeCompare(b.name); }); 389 var lang = ($('#hidGlobalLang').val() || 'en').toLowerCase(); 390 groups.forEach(function (g) { 391 g.items.sort(function (a, b) { 392 return _getTopicLabel(a).localeCompare(_getTopicLabel(b)); 393 }); 394 }); 395 396 var col1Groups = []; 397 var col2Groups = []; 398 groups.forEach(function (g, i) { 399 if (i % 2 === 0) { 400 col1Groups.push(g); 401 } else { 402 col2Groups.push(g); 403 } 404 }); 405 406 var $col1 = $('<div class="ui-accordion"></div>'); 407 var $col2 = $('<div class="ui-accordion"></div>'); 408 409 _buildColumn($col1, col1Groups); 410 _buildColumn($col2, col2Groups); 411 412 $grid.append($col1).append($col2); 413 414 // åå§å Accordion å ä»¶ 415 $grid.find('.ui-accordion').each(function () { 416 new UIAccordion($(this)); 417 }); 418 filterInited = true; 419 } 420 421 function _buildColumn($col, groups) { 422 groups.forEach(function (group) { 423 var safeGroupName = $('<span>').text(group.name).html(); 424 var html = '<div class="ui-accordion__item">' + 425 '<button class="ui-accordion__trigger" aria-expanded="false">' + 426 '<h4>' + safeGroupName + '</h4>' + 427 '</button>' + 428 '<div class="ui-accordion__panel">' + 429 '<div class="ui-accordion__content">' + 430 '<div class="ui-field-group">'; 431 432 group.items.forEach(function (topic) { 433 var label = _getTopicLabel(topic); 434 var safeLabel = $('<span>').text(label).html(); 435 html += '<label class="ui-checkbox">' + 436 '<input type="checkbox" class="ui-checkbox__input" data-topic-id="' + topic.topicId + '">' + 437 '<span class="ui-checkbox__box"></span>' + 438 '<span class="ui-checkbox__label">' + safeLabel + '</span>' + 439 '</label>'; 440 }); 441 442 html += '</div></div></div></div>'; 443 $col.append(html); 444 }); 445 } 446 447 // åæ Footer æ¨¡å¼ 448 function _setFooterMode(mode) { 449 // mode: 'first' (æ¥é© 1+2 æµç¨) | 'revisit' 450 $modal.find('.filter-step-footer').removeClass('is-active'); 451 if (mode === 'revisit') { 452 $modal.find('[data-step-footer="revisit"]').addClass('is-active'); 453 } else { 454 $modal.find('[data-step-footer="1"]').addClass('is-active'); 455 } 456 } 457 458 // éé»éé modalï¼ä¸è§¸ç¼ coachmarkï¼ 459 function _closeModalSilently() {
460 var $m = $('#modal-filter'); 461 $m.removeClass('is-open'); 462 $m.one('transitionend', function () { 463 $m.css('display', 'none'); 464 }); 465 } 466 467 /* --- Accordion åå§åï¼å»¶é²è³ Modal 馿¬¡éåï¼éé API åæ è¼å ¥ï¼ --- */ 468 var filterInited = false; 469 470 function _onModalReady() { 471 // æ¯æ¬¡éåå éç½® placeholder è¦åçæ 472 $modal.find('.filter-focus__placeholder') 473 .text(_t('no-topics-selected', 'No topics selected yet.')) 474 .removeClass('is-error'); 475 476 // 夿·é¦æ¬¡ vs å訪 477 if (_hasApplied) { 478 _isRevisit = true; 479 goToStep(1); 480 _setFooterMode('revisit'); 481 _restoreSavedSelections(); // â rebuildFocusList â updateFocusCount 482 } else { 483 _isRevisit = false; 484 goToStep(1); 485 _setFooterMode('first'); 486 rebuildFocusList(); 487 _hasApplied = true; 488 localStorage.setItem(STORAGE_KEY_APPLIED, '1'); 489 } 490 } 491 492 $(document).on('click', '[data-ui-modal-open="modal-filter"]', function () { 493 $('#preferences-toast').removeClass('is-visible'); 494 495 if (typeof window.closeCoachmark === 'function') { 496 window.closeCoachmark(); 497 } 498 499 if (!filterInited) { 500 _fetchAndRenderTopics(function () { 501 _syncTagsFromServer(function () { 502 _onModalReady(); 503 }); 504 }); 505 } else { 506 _onModalReady(); 507 } 508 }); 509 510 /* --- Mobile Tab åæ --- */ 511 $modal.on('click', '.filter-tabs__tab', function () { 512 var tab = $(this).data('tab'); 513 $modal.find('.filter-tabs__tab').removeClass('is-active'); 514 $(this).addClass('is-active'); 515 $modal.find('.filter-grid').attr('data-active-tab', tab); 516 }); 517 518 /* --- Collapse / Expand All --- */ 519 function _syncCollapseAllBtn() { 520 var $btn = $modal.find('.filter-collapse-all'); 521 var $visibleItems = $modal.find('.ui-accordion__item:visible'); 522 var allExpanded = $visibleItems.length > 0 && $visibleItems.filter('.is-open').length === $visibleItems.length; 523 524 $btn.attr('aria-pressed', allExpanded ? 'true' : 'false'); 525 $btn.find('.filter-collapse-all__label').text(allExpanded ? _t('collapse-
525all', 'Collapse All') : _t('expand-all', 'Expand All')); 526 $btn.find('i') 527 .removeClass('fa-chevron-up fa-chevron-down') 528 .addClass(allExpanded ? 'fa-chevron-up' : 'fa-chevron-down'); 529 } 530 531 $modal.on('click', '.filter-collapse-all', function () { 532 var $btn = $(this); 533 var isExpanding = $btn.attr('aria-pressed') === 'false'; 534 535 $modal.find('.ui-accordion__item:visible').each(function () { 536 var $item = $(this); 537 var $trigger = $item.find('.ui-accordion__trigger'); 538 var $panel = $item.find('.ui-accordion__panel'); 539 540 if (isExpanding) { 541 $item.addClass('is-open'); 542 $panel.height($panel[0].scrollHeight); 543 $trigger.attr('aria-expanded', 'true'); 544 } else { 545 $panel.height($panel[0].scrollHeight); 546 requestAnimationFrame(function () { 547 $item.removeClass('is-open'); 548 $panel.height(0); 549 }); 550 $trigger.attr('aria-expanded', 'false'); 551 } 552 }); 553 554 $btn.attr('aria-pressed', isExpanding ? 'true' : 'false'); 555 $btn.find('.filter-collapse-all__label').text(isExpanding ? _t('collapse-
555all', 'Collapse All') : _t('expand-all', 'Expand All')); 556 $btn.find('i') 557 .removeClass('fa-chevron-up fa-chevron-down') 558 .addClass(isExpanding ? 'fa-chevron-up' : 'fa-chevron-down'); 559 }); 560 561 /* --- ç§»é¤ chip --- */ 562 $modal.on('click', '.ui-tag__remove', function () { 563 var $chip = $(this).closest('.ui-tag'); 564 var topicId = $chip.attr('data-topic-id'); 565 if (topicId) { 566 $modal.find('.ui-checkbox__input[data-topic-id="' + topicId + '"]').prop('checked', false); 567 } else { 568 var name = $chip.attr('data-filter-name'); 569 $modal.find('.ui-checkbox').each(function () { 570 if ($(this).find('.ui-checkbox__label').text().trim() === name) { 571 $(this).find('.ui-checkbox__input').prop('checked', false); 572 } 573 }); 574 } 575 $chip.remove(); 576 // éæ°è¨ç®ææ L1 ç filter-count badge 577 $modal.find('.ui-accordion__item').each(function () { 578 var $item = $(this); 579 var count = $item.find('.ui-checkbox__input:checked').length; 580 var $badge = $item.find('.ui-accordion__trigger .filter-count'); 581 if (count > 0) { 582 if ($badge.length) { $badge.text(count); } 583 else { $item.find('.ui-accordion__trigger h4').append(' <span class="filter-count ui-badge ui-badge--soft">' + count + '</span>'); } 584 } else { 585 $badge.remove(); 586 } 587 }); 588 updateFocusCount(); 589 }); 590 591 /* --- Clear All --- */ 592 $modal.on('click', '.filter-focus__clear', function () { 593 $modal.find('.filter-focus__list .ui-tag').remove(); 594 $modal.find('.ui-checkbox__input[data-topic-id]').prop('checked', false); 595 $modal.find('.ui-accordion__trigger .filter-count').remove(); 596 updateFocusCount(); 597 }); 598 599 /* --- Topic Checkbox è®æ´ â é建 chips --- */ 600 $modal.on('change', '.ui-checkbox__input[data-topic-id]', function () { 601 rebuildFocusList(); 602 }); 603 604 function rebuildFocusList() { 605 var $list = $modal.find('.filter-focus__list'); 606 $list.find('.ui-tag').remove(); 607 608 // æ¶éææå¾é¸é ç®åå ¶ L1 (category) / L2 (name) 609 var checkedItems = []; 610 $modal.find('.ui-accordion__item').each(function () { 611 var $item = $(this); 612 var category = $.trim($item.find('.ui-accordion__trigger h4').contents().filter(function () { return this.nodeType === 3; }).text()); 613 var count = 0; 614 $item.find('.ui-checkbox__input:checked').each(function () { 615 var $cb = $(this); 616 checkedItems.push({ 617 category: category, 618 topicId: $cb.attr('data-topic-id') || '', 619 name: $.trim($cb.closest('.ui-checkbox').find('.ui-checkbox__label').text()) 620 }); 621 count++; 622 }); 623 var $badge = $item.find('.ui-accordion__trigger .filter-count'); 624 if (count > 0) { 625 if ($badge.length) { $badge.text(count); } 626 // 0831ï¼è¨æ¸ badge æ¹åå 件庫ç ui-badgeï¼filter-count ä¿çç¶ JS/CSS çé¤å 627 else { $item.find('.ui-accordion__trigger h4').append(' <span class="filter-count ui-badge ui-badge--soft">' + count + '</span>'); } 628 } else { 629 $badge.remove(); 630 } 631 }); 632 633 // ä¾ L1 忝æåºï¼ç¸å L1 åä¾ L2 忝æåº 634 checkedItems.sort(function (a, b) { 635 var cmp = a.category.localeCompare(b.category); 636 if (cmp !== 0) return cmp; 637 return a.name.localeCompare(b.name); 638 }); 639 640 // 便åºçµææ¸²æ chips
641 checkedItems.forEach(function (item) { 642 var safeName = $('<span>').text(item.name).html(); 643 var safeCat = $('<span>').text(item.category).html(); 644 var chip = '<div class="ui-tag" data-filter-name="' + safeName + '" data-topic-id="' + item.topicId + '">' + 645 '<span class="ui-tag__text">' + 646 '<span class="ui-tag__category">' + safeCat + '</span>' + 647 '<span class="ui-tag__name">' + safeName + '</span>' + 648 '</span>' + 649 '<button type="button" class="ui-tag__remove" aria-label="Remove"><i class="fa-regular fa-xmark" aria-hidden="true"></i></button>' + 650 '</div>'; 651 $list.append(chip); 652 }); 653 654 updateFocusCount(); 655 } 656 657 function updateFocusCount() { 658 var n = $modal.find('.filter-focus__list .ui-tag').length; 659 660 var $placeholder = $modal.find('.filter-focus__placeholder'); 661 if (n === 0) { 662 $placeholder.text(_t('no-topics-selected', 'No topics selected yet.')).removeClass('is-error').show(); 663 } else { 664 $placeholder.hide(); 665 } 666 667 // Clear Allï¼æé ç®æé¡¯ç¤ºï¼ç¡é ç®æé±è 668 $modal.find('.filter-focus__clear').css('display', n > 0 ? '' : 'none'); 669 670 var $tabBadges = $modal.find('[data-focus-count-tab]'); 671 $tabBadges.text(n); 672 $tabBadges.css('display', n > 0 ? 'inline-flex' : 'none'); 673 674 var $sidebarBadges = $modal.find('[data-focus-count]'); 675 $sidebarBadges.text(n); 676 $sidebarBadges.css('visibility', n > 0 ? 'visible' : 'hidden'); 677 } 678 679 /* --- Step åæ --- */ 680 function goToStep(stepNum) { 681 $modal.find('.filter-step').removeClass('is-active'); 682 $modal.find('.filter-step[data-step="' + stepNum + '"]').addClass('is-active'); 683 $modal.find('.filter-step-footer').removeClass('is-active'); 684 $modal.find('.filter-step-footer[data-step-footer="' + stepNum + '"]').addClass('is-active'); 685 } 686 687 688 /* --- æå°åéæ¿¾ --- */ 689 690 var _accordionStateBeforeSearch = null; 691 692 function _saveAccordionState() { 693 if (_accordionStateBeforeSearch) return; 694 _accordionStateBeforeSearch = []; 695 $modal.find('.ui-accordion__item').each(function () { 696 _accordionStateBeforeSearch.push($(this).hasClass('is-open')); 697 }); 698 _flattenForSearch(); 699 } 700 701 function _restoreAccordionState() { 702 if (!_accordionStateBeforeSearch) return; 703 $modal.find('.ui-accordion__item').each(function (i) { 704 var $item = $(this); 705 var $panel = $item.find('.ui-accordion__panel'); 706 var $trigger = $item.find('.ui-accordion__trigger'); 707 var wasOpen = _accordionStateBeforeSearch[i] || false; 708 709 $item.show(); 710 $item.find('.ui-field-group .ui-checkbox').show(); 711 712 if (wasOpen) { 713 $item.addClass('is-open'); 714 $panel.css('height', 'auto'); 715 $panel.height($panel[0].scrollHeight); 716 $trigger.attr('aria-expanded', 'true'); 717 } else { 718 $item.removeClass('is-open'); 719 $panel.height(0); 720 $trigger.attr('aria-expanded', 'false'); 721 } 722 }); 723 _accordionStateBeforeSearch = null; 724 } 725 726 function _expandItem($item) { 727 var $panel = $item.find('.ui-accordion__panel'); 728 var $trigger = $item.find('.ui-accordion__trigger'); 729 $item.addClass('is-open'); 730 $panel.css('height', 'auto'); 731 var h = $panel[0].scrollHeight; 732 $panel.height(h); 733 $trigger.attr('aria-expanded', 'true'); 734 } 735 736 function _collapseItem($item) { 737 var $panel = $item.find('.ui-accordion__panel'); 738 var $trigger = $item.find('.ui-accordion__trigger'); 739 $item.removeClass('is-open'); 740 $panel.height(0); 741 $trigger.attr('aria-expanded', 'false'); 742 } 743 744 function _flattenForSearch() { 745 var $grid = $modal.find('.filter-accordion-grid'); 746 var $accordions = $grid.find('.ui-accordion'); 747 if ($accordions.length < 2) return; 748 749 var $col1 = $accordions.eq(0); 750 var allItems = $grid.find('.ui-accordion__item').toArray(); 751 752 // ä¾ L1 header A-Z æåº 753 allItems.sort(function (a, b) { 754 var nameA = $(a).find('.ui-accordion__trigger h4').text().trim(); 755 var nameB = $(b).find('.ui-accordion__trigger h4').text().trim(); 756 return nameA.localeCompare(nameB); 757 }); 758 759 // å ¨é¨ç§»è³ col1ï¼col2 è®ç©ºï¼
760 allItems.forEach(function (item) { 761 $col1.append(item); 762 }); 763 } 764 765 $modal.on('input', '.filter-search .ui-input', function () { 766 var keyword = $(this).val().toLowerCase().trim(); 767 var $clearBtn = $modal.find('.filter-search .ui-input-group__clear'); 768 var $noResults = $modal.find('.filter-no-results'); 769 var $grid = $modal.find('.filter-accordion-grid'); 770 771 // æ¸ é¤æéï¼ææåæé¡¯ç¤ºï¼ç¡æåæé±è 772 $clearBtn.prop('hidden', keyword.length === 0); 773 774 // ç¡ééµå â å¾å¿«åéå»ºéæ¬ä½å±ï¼ä¿çå¾é¸çæ ï¼ 775 if (!keyword) { 776 var currentChecked = _getCurrentSelections(); 777 _renderAccordions(_topicsCache); 778 currentChecked.forEach(function (id) { 779 $modal.find('.ui-checkbox__input[data-topic-id="' + id + '"]').prop('checked', true); 780 }); 781 rebuildFocusList(); 782 _accordionStateBeforeSearch = null; 783 $noResults.hide(); 784 $grid.show(); 785 $grid.removeClass('search-mode'); 786 _syncCollapseAllBtn(); 787 return; 788 } 789 790 // 馿¬¡è¼¸å ¥æå²åå±éçæ ï¼ä¸¦åç¨æå°æ¨¡å¼ï¼item èªå左峿åï¼ 791 _saveAccordionState(); 792 $grid.addClass('search-mode'); 793 794 // å ç¢ºä¿ grid å¯è¦ï¼å¦å scrollHeight å¨ display:none ä¸çº 0 795 $noResults.hide(); 796 $grid.show(); 797 798 var totalMatch = false; 799 800 $modal.find('.ui-accordion__item').each(function () { 801 var $item = $(this); 802 var categoryText = $item.find('.ui-accordion__trigger h4').text().toLowerCase(); 803 var hasMatch = false; 804 805 // åªä¾ L2 æåéæ¿¾ï¼L1 åç¨±ä¸æ±ºå®æ¯å¦é¡¯ç¤ºå ¨é¨ L2 806 $item.find('.ui-field-group .ui-checkbox').each(function () { 807 var text = $(this).text().toLowerCase(); 808 if (text.indexOf(keyword) > -1) { 809 $(this).show(); 810 hasMatch = true; 811 } else { 812 $(this).hide(); 813 } 814 }); 815 816 if (hasMatch) { 817 $item.show(); 818 _expandItem($item); 819 } else { 820 $item.hide(); 821 _collapseItem($item); 822 } 823 824 if (hasMatch) totalMatch = true; 825 }); 826 827 // Rule 3ï¼å ¨é¨é½ä¸å¹é æé¡¯ç¤ºå¼å°è¨æ¯ 828 if (!totalMatch) { 829 $grid.hide(); 830 $noResults.show(); 831 } 832 833 _syncCollapseAllBtn(); 834 }); 835 836 /* --- æå°æ¡æ¸ 餿é --- */ 837 $modal.on('click', '.filter-search .ui-input-group__clear', function () { 838 var $input = $modal.find('.filter-search .ui-input'); 839 $input.val('').trigger('input'); 840 $input.focus(); 841 }); 842 843 /* ============================================================= 844 æ¥é©ä¸ï¼é£è¡åç« + Tooltip æåæç¤º 845 source: 'apply' | 'skip' | 'close' 846 ============================================================= */ 847 848 // Tooltip ææ¡ä¾è§¸ç¼ä¾æº 849 var _coachmarkContent = { 850 apply: { 851 title: _t('saved-title', 'Your preferences are saved!'), 852 desc: _t('saved-desc', 'Click Customize View anytime to update your preferences.') 853 }, 854 skip: { 855 title: _t('coachmark-title', 'Want to customize?'), 856 desc: _t('coachmark-desc', 'You can personalize your experience anytime by clicking the Customize View button.') 857 }, 858 close: { 859 title: _t('coachmark-title', 'Want to customize?'), 860 desc: _t('coachmark-desc', 'You can personalize your experience anytime by clicking the Customize View button.') 861 } 862 }; 863 864 function _closeModalAndShowCoachmark(source) { 865 var headerBtn = document.getElementById('header-customize'); 866 if (!headerBtn || headerBtn.getClientRects().length === 0) { 867 _closeModalSilently(); 868 if (source === 'apply') window.showPreferencesToast(); 869 return; 870 } 871 872 window._coachmarkSource = source || 'close'; 873
874 var $m = $('#modal-filter'); 875 $m.removeClass('is-open'); 876 $m.one('transitionend', function () { 877 $m.css('display', 'none'); 878 }); 879 880 var content = _coachmarkContent[window._coachmarkSource] || _coachmarkContent.close; 881 $('#customize-coachmark').find('.customize-coachmark__title').text(content.title); 882 $('#customize-coachmark').find('.customize-coachmark__desc').text(content.desc); 883 884 _startFlyAnimation(); 885 } 886 887 function _startFlyAnimation() { 888 var $flyIcon = $('#coachmark-fly-icon'); 889 var $overlay = $('#coachmark-overlay'); 890 var $headerBtn = $('#header-customize'); 891 892 window.scrollTo({ top: 0, behavior: 'smooth' }); 893 894 var btnRect = $headerBtn[0].getBoundingClientRect(); 895 var targetX = btnRect.left + btnRect.width / 2; 896 var targetY = btnRect.top + btnRect.height / 2; 897 898 var startX = window.innerWidth / 2; 899 var startY = window.innerHeight / 2; 900 901 $flyIcon.css({ 902 left: startX + 'px', 903 top: startY + 'px', 904 transform: 'translate(-50%, -50%) scale(0)', 905 opacity: 0 906 }); 907 908 $overlay.addClass('is-visible'); 909 910 requestAnimationFrame(function () { 911 $flyIcon.css({ 912 transition: 'opacity 0.2s ease, transform 0.3s ease', 913 opacity: 1, 914 transform: 'translate(-50%, -50%) scale(1.3)' 915 }); 916 917 setTimeout(function () { 918 $flyIcon.css({ 919 transition: 'left 0.6s cubic-bezier(0.4,0,0.2,1), top 0.6s cubic-bezier(0.4,0,0.2,1), transform 0.6s cubic-bezier(0.4,0,0.2,1), opacity 0.5s ease 0.3s', 920 left: targetX + 'px', 921 top: targetY + 'px', 922 transform: 'translate(-50%, -50%) scale(0.3)', 923 opacity: 0 924 }); 925 926 setTimeout(function () { 927 $flyIcon.css({ transition: 'none', opacity: 0, transform: 'scale(0)' });
928 _startCoachmarkTooltip(); 929 }, 650); 930 }, 350); 931 }); 932 } 933 934 function _startCoachmarkTooltip() { 935 var $overlay = $('#coachmark-overlay'); 936 var $headerCustomize = $('#header-customize'); 937 var $coachmark = $('#customize-coachmark'); 938 939 $headerCustomize.addClass('coachmark-active'); 940 $headerCustomize.find('a, i').css('pointer-events', 'none'); 941 942 $coachmark.removeClass('is-visible border-spin'); 943 void $coachmark[0].offsetWidth; 944 945 requestAnimationFrame(function () { 946 $coachmark.addClass('is-visible'); 947 _triggerBorderSpin(); 948 }); 949 } 950 951 function _triggerBorderSpin() { 952 var $coachmark = $('#customize-coachmark'); 953 $coachmark.removeClass('border-spin'); 954 void $coachmark[0].offsetWidth; 955 $coachmark.addClass('border-spin'); 956 $coachmark.one('animationend', function () { 957 $coachmark.removeClass('border-spin'); 958 if ($coachmark.hasClass('is-visible')) { 959 window._coachmarkSpinTimer = setTimeout(_triggerBorderSpin, 3000); 960 } 961 }); 962 } 963 window._triggerBorderSpin = _triggerBorderSpin; 964 965 $('#btn-apply-preferences').on('click', function () { 966 var hasChecked = $modal.find('.ui-checkbox__input[data-topic-id]:checked').length > 0; 967 968 if (!hasChecked) { 969 var $placeholder = $modal.find('.filter-focus__placeholder'); 970 $placeholder.text(_t('select-at-least-one', 'Please select at least one option.')).addClass('is-error').show(); 971 $placeholder[0].scrollIntoView({ behavior: 'smooth', block: 'nearest' }); 972 return; 973 } 974 975 _savedSelections = _getCurrentSelections(); 976 _hasApplied = true; 977 _persistPreferences().then(function (ok) { 978 if (ok) _closeModalAndShowCoachmark('apply'); 979 }); 980 }); 981 982 /* --- å訪 Done æéï¼å²åè®æ´ï¼ç©ºé¸ææ¢å¾©é è¨é¦é --- */ 983 $modal.on('click', '#btn-revisit-done', function () { 984 var current = _getCurrentSelections(); 985 var changed = _selectionsChanged(current, _savedSelections); 986 987 if (changed) { 988 // æè®æ´ â å²å â æ¥é©åæåéç¥ 989 _savedSelections = current; 990 _persistPreferences().then(function (ok) { 991 if (ok) { _closeModalSilently(); window.showPreferencesToast(); } 992 }); 993 } else { 994 // ç¡è®æ´ â éé»éé 995 _closeModalSilently(); 996 } 997 }); 998 999 /* --- å訪 Cancel æéï¼ä¸å²å â éé»éé --- */ 1000 $modal.on('click', '#btn-revisit-cancel', function () { 1001 _closeModalSilently(); 1002 }); 1003 1004 /* --- å¾é¸ä»»ä½ topic checkbox ææ¸ é¤ç´ åé¯èª¤ --- */ 1005 $modal.on('change', '.ui-checkbox__input[data-topic-id]', function () { 1006 if ($modal.find('.ui-checkbox__input[data-topic-id]:checked').length > 0) { 1007 $modal.find('.filter-focus__error').hide(); 1008 } 1009 }); 1010 1011 /* --- Skip æéï¼åªå¨é¦æ¬¡åºç¾ï¼ï¼ä¸å²å â éé â æ¥é©ä¸ï¼skipï¼ --- */ 1012 $modal.on('click', '#btn-skip', function () { 1013 _closeModalAndShowCoachmark('skip'); 1014 }); 1015 1016 /* --- X ééæéï¼é¦æ¬¡ â ä¸å²å â éé â æ¥é©ä¸ï¼closeï¼ï¼å訪 â éé»ééï¼å Cancelï¼ --- */ 1017 $modal.on('click', '.ui-modal__close', function (e) { 1018 e.stopPropagation(); 1019 if (_isRevisit) _closeModalSilently(); 1020 else _closeModalAndShowCoachmark('close'); 1021 }); 1022 1023 }); 1024 1025 /* ============================================= 1026 closeCoachmark + Got itï¼å ¨åï¼ 1027 ============================================= */ 1028 window._coachmarkSpinTimer = null; 1029 window._coachmarkSource = 'close'; 1030 1031 window.closeCoachmark = function () { 1032 var coachmark = document.getElementById('customize-coachmark');
1033 var overlay = document.getElementById('coachmark-overlay'); 1034 var headerBtn = document.getElementById('header-customize'); 1035 if (window._coachmarkSpinTimer) { 1036 clearTimeout(window._coachmarkSpinTimer); 1037 window._coachmarkSpinTimer = null; 1038 } 1039 if (coachmark) coachmark.classList.remove('is-visible', 'border-spin'); 1040 if (overlay) overlay.classList.remove('is-visible'); 1041 if (headerBtn) { 1042 headerBtn.classList.remove('coachmark-active'); 1043 // æ¢å¾© Customize View é£çµç黿 1044 $(headerBtn).find('a, i').css('pointer-events', ''); 1045 } 1046 }; 1047 1048 // Got it æéç handlerï¼å¾ inline onclick æ¹çºæ¤èçµ±ä¸ç®¡çï¼ 1049 window.handleCoachmarkGotIt = function () { 1050 window.closeCoachmark(); 1051 1052 // è¥ä¾èª Apply â 顯示æ¥é©å Preferences Applied toast 1053 if (window._coachmarkSource === 'apply') { 1054 window.showPreferencesToast(); 1055 } 1056 }; 1057 1058 // æ¥é©åï¼é¡¯ç¤º Preferences Applied toastï¼5 ç§å¾èªåæ¶å¤± 1059 window.showPreferencesToast = function () { 1060 var $toast = $('#preferences-toast'); 1061 $toast.addClass('is-visible'); 1062 setTimeout(function () { 1063 $toast.removeClass('is-visible'); 1064 }, 5000); 1065 }; 1066 1067 /* 黿é®ç½©ä¸éé coachmark â å å 許 Got it æééé */ 1068 $(function () { 1069 $('#coachmark-overlay').on('click', function (e) { 1070 e.stopPropagation(); 1071 // ä¸åä»»ä½äºï¼å¼·å¶ä½¿ç¨è é» Got it 1072 }); 1073 }); 1074 1075 /* ä¾ Personalize Toast ééæå¼å«ï¼å¨ jQuery IIFE å¤é¨ç¡æ³åå closureï¼ */ 1076 window.triggerCoachmarkFromToast = function () { 1077 $(function () { 1078 var content = { 1079 title: _t('coachmark-title', 'Want to customize?'), 1080 desc: _t('coachmark-desc', 'You can personalize your experience anytime by clicking the Customize View button.') 1081 }; 1082 $('#customize-coachmark').find('.customize-coachmark__title').text(content.title); 1083 $('#customize-coachmark').find('.customize-coachmark__desc').text(content.desc); 1084 window._coachmarkSource = 'close'; 1085
1086 var $overlay = $('#coachmark-overlay'); 1087 var $headerCustomize = $('#header-customize'); 1088 $headerCustomize.addClass('coachmark-active'); 1089 $headerCustomize.find('a, i').css('pointer-events', 'none'); 1090 $overlay.addClass('is-visible'); 1091 1092 var $coachmark = $('#customize-coachmark'); 1093 $coachmark.removeClass('is-visible border-spin'); 1094 void $coachmark[0].offsetWidth; 1095 $coachmark.addClass('is-visible'); 1096 1097 // åªæéæ¡æè½åç«éè¦çä¸ä¸å frame æéæ°ååï¼åç« restart çæ¨æºåæ³ï¼ã 1098 requestAnimationFrame(function () { 1099 if (window._triggerBorderSpin) { 1100 window._triggerBorderSpin(); 1101 } 1102 }); 1103 }); 1104 }; 1105 1106})(jQuery); 1107 1108/* ============================================= 1109 Personalize Toast 顯示æ¢ä»¶æ§å¶ 1110 æ¢ä»¶ï¼CookieYes functional+analytics åæã 1111 æªæ¾éééãæªæ¾é²å ¥ filterãfilter æªéå 1112============================================= */ 1113(function () { 1114 'use strict'; 1115 1116 var TOAST_DISMISSED_KEY = 'adv_personalize_toast_dismissed'; 1117 var FILTER_VISITED_KEY = 'adv_filter_visited'; 1118 var STORAGE_KEY_APPLIED = 'adv_filter_applied'; 1119 var COOKIEYES_COOKIE = 'cookieyes-consent'; 1120 var COOKIEYES_EVENT = 'cookieyes_consent_update'; 1121 1122 /* --- è®å CookieYes cookie ä¸æå® key çå¼ --- */ 1123 function getCookieYesValue(key) { 1124 var found = document.cookie.split(';').map(function (c) { 1125 return c.trim(); 1126 }).find(function (c) { 1127 return c.indexOf(COOKIEYES_COOKIE + '=') === 0; 1128 }); 1129 if (!found) return undefined; 1130 var raw = decodeURIComponent(found.split('=').slice(1).join('=')); 1131 var obj = {}; 1132 raw.split(',').forEach(function (pair) { 1133 var kv = pair.split(':'); 1134 if (kv.length === 2) obj[kv[0].trim()] = kv[1].trim(); 1135 }); 1136 return obj[key]; 1137 } 1138 1139 /* --- æ¢ä»¶ 1ï¼functional ä¸ analytics ååæ --- */ 1140 function isConsentGranted() { 1141 return getCookieYesValue('functional') === 'yes' && 1142 getCookieYesValue('analytics') === 'yes'; 1143 } 1144 1145 /* ============================================= 1146 Cookie Consentï¼Customize View æé顯示/é±è 1147 ============================================= */ 1148 function isCustomizeAllowed() { 1149 return isConsentGranted(); 1150 } 1151 1152 function updateCustomizeVisibility() { 1153 var btn = document.getElementById('header-customize'); 1154 if (!btn) return; 1155 if (isCustomizeAllowed()) { 1156 btn.style.display = ''; 1157 } else { 1158 btn.style.display = 'none'; 1159 var modal = document.getElementById('modal-filter'); 1160 if (modal && modal.classList.contains('is-open')) { 1161 modal.classList.remove('is-open'); 1162 modal.style.display = 'none'; 1163 } 1164 } 1165 } 1166 1167 // å ¬é APIï¼ä¾å ¶ä»æ¨¡çµæ¥è©¢ 1168 window.AdvCookieConsent = { 1169 getCookieYesValue: getCookieYesValue, 1170 isConsentGranted: isConsentGranted, 1171 isCustomizeAllowed: isCustomizeAllowed, 1172 refresh: function () { maybeShowToast(); }, 1173 holdToast: function (ms) { holdToast(ms); }, 1174 releaseToast: function () { releaseToast(); } 1175 }; 1176 1177 /* --- ç¶å夿·æ¯å¦æé¡¯ç¤º personalize toast --- */ 1178 function shouldShowToast() { 1179 if (_toastHeld) return false; // æ£å¨ç MA åæï¼å ä¸è¦è·³ 1180 if (!isCustomizeAllowed()) return false; // consent æªåæ 1181 if (localStorage.getItem(TOAST_DISMISSED_KEY)) return false; // å·²éééï¼æ Xï¼ 1182 if (localStorage.getItem(FILTER_VISITED_KEY)) return false; // å·²é²å ¥é filter 1183 if (localStorage.getItem(STORAGE_KEY_APPLIED)) return false; // å·² Apply éï¼é¸éåå¥½ï¼ 1184 var modal = document.getElementById('modal-filter'); 1185 if (modal && modal.classList.contains('is-open')) return false;
1185 // filter éåä¸ 1186 return true; 1187 } 1188 1189 function maybeShowToast() { 1190 var toast = document.getElementById('personalize-toast'); 1191 if (!toast) return; 1192 toast.style.display = shouldShowToast() ? 'flex' : 'none'; 1193 updateCustomizeVisibility(); 1194 } 1195 1196 1197 var HOLD_MAX_MS = 2500; 1198 var _toastHeld = false; 1199 var _toastHoldTimer = null; 1200 1201 function holdToast(ms) { 1202 _toastHeld = true; 1203 var toast = document.getElementById('personalize-toast'); 1204 if (toast) toast.style.display = 'none'; 1205 if (_toastHoldTimer) clearTimeout(_toastHoldTimer); 1206 _toastHoldTimer = setTimeout(releaseToast, ms || HOLD_MAX_MS); 1207 } 1208 1209 function releaseToast() { 1210 if (_toastHoldTimer) { clearTimeout(_toastHoldTimer); _toastHoldTimer = null; } 1211 if (!_toastHeld) return; 1212 _toastHeld = false; 1213 maybeShowToast(); 1214 } 1215 1216 function _scrollToTop() { 1217 var reduceMotion = window.matchMedia && 1218 window.matchMedia('(prefers-reduced-motion: reduce)').matches; 1219 try { 1220 window.scrollTo({ top: 0, left: 0, behavior: reduceMotion ? 'instant' : 'smooth' }); 1221 } catch (e) { 1222 window.scrollTo(0, 0); 1223 } 1224 } 1225 1226 /* --- ééæéï¼è¨éãå·²ééãâ æ°¸ä¸å顯示 â å åºé®ç½©èå°è©±æ¡ï¼åå¹³æ»æ²é --- */ 1227 document.addEventListener('click', function (e) { 1228 if (e.target.closest('#personalize-toast .personalize-toast__close')) { 1229 localStorage.setItem(TOAST_DISMISSED_KEY, '1'); 1230 var toast = document.getElementById('personalize-toast'); 1231 if (toast) toast.style.display = 'none'; 1232 1233 if (typeof window.triggerCoachmarkFromToast === 'function') { 1234 window.triggerCoachmarkFromToast(); 1235 } 1236 _scrollToTop(); 1237 } 1238 }); 1239 1240 /* --- éå filter modalï¼ä»»ä½å ¥å£ï¼ï¼è¨éãå·²é²å ¥ã並ç«å³é±è toast --- */ 1241 document.addEventListener('click', function (e) { 1242 if (e.target.closest('[data-ui-modal-open="modal-filter"]')) { 1243 localStorage.setItem(FILTER_VISITED_KEY, '1'); 1244 var toast = document.getElementById('personalize-toast'); 1245 if (toast) toast.style.display = 'none'; 1246 // åæé±è Preferences Applied toast 1247 var prefToast = document.getElementById('preferences-toast'); 1248 if (prefToast) prefToast.classList.remove('is-visible'); 1249 } 1250 }); 1251 1252 /* --- ç£è½ CookieYes åæè®æ´äºä»¶ï¼æ°è¨ªå®¢åæå¾ç«å³è§¸ç¼ï¼ --- */ 1253 document.addEventListener(COOKIEYES_EVENT, maybeShowToast); 1254 window.addEventListener(COOKIEYES_EVENT, maybeShowToast); 1255 1256 /* --- é é¢è¼å ¥æå¤æ·ï¼å訪è å·²æ consent cookieï¼ --- */ 1257 if (document.readyState === 'loading') { 1258 document.addEventListener('DOMContentLoaded', maybeShowToast); 1259 } else { 1260 maybeShowToast(); 1261 } 1262 1263}());
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.