Plugin Directory

Changeset 3769546


Ignore:
Timestamp:
10/27/2026 09:16:23 PM (4 days ago)
Author:
zoobu
Message:

Release 5.3.0

Location:
cookieflame
Files:
2 added
18 edited
1 copied

Legend:

Unmodified
Added
Removed
  • cookieflame/tags/5.3.0/assets/css/admin.css

    r3766806 r3769546  
    394394}
    395395
    396 /* Source column */
     396/* Status column: one label, then the actions, one per line */
    397397.cf-admin-source-cell {
    398398  font-size: 12px;
    … …  
    403403  margin-top: 1px;
    404404}
     405.cf-admin-status-line + .cf-admin-status-line {
     406  margin-top: 3px;
     407}
     408.cf-admin-status-where {
     409  color: #646970;
     410  line-height: 1.4;
     411}
    405412.cf-admin-source-link {
    406   font-size: 11px;
    407   color: #646970;
     413  font-size: 12px;
     414  color: #2271b1;
    408415  text-decoration: none;
    409416  cursor: pointer;
    410417  display: inline-block;
    411   margin-top: 2px;
    412418  transition: color .15s;
    413419}
    414420.cf-admin-source-link:hover {
    415   color: #2271b1;
     421  color: #135e96;
    416422  text-decoration: underline;
     423}
     424.cf-admin-source-link-main {
     425  font-weight: 600;
    417426}
    418427
    … …  
    474483  align-items: center;
    475484  gap: 4px;
    476   font-size: 11px;
    477   font-weight: 600;
     485  font-size: 12px;
     486  font-weight: 600;
     487  line-height: 20px;
     488  padding: 0 8px;
     489  border-radius: 3px;
    478490}
    479491.cf-admin-badge-known,
    480492.cf-admin-badge-ai {
    481   color: #00a32a;
     493  color: #00701f;
     494  background: #e6f4e9;
    482495}
    483496.cf-admin-badge-edited {
    484   color: #2271b1;
     497  color: #135e96;
     498  background: #e9f1fa;
    485499}
    486500.cf-admin-badge-locked {
    487   color: #996800;
     501  color: #8a5b00;
     502  background: #fcf3e1;
    488503}
    489504.cf-admin-badge-unclassified {
    490   color: #d63638;
     505  color: #8a5b00;
     506  background: #fff;
     507  box-shadow: inset 0 0 0 1px #dba617;
    491508}
    492509
    … …  
    859876  -webkit-user-select: text;
    860877}
     878.cf-admin-help-list p {
     879  margin: 0 0 8px;
     880  line-height: 1.5;
     881}
     882.cf-admin-help-list p:last-child {
     883  margin-bottom: 0;
     884}
    861885@keyframes cf-admin-fade-in {
    862886  from { opacity: 0; }
    … …  
    19421966}
    19431967
    1944 /* Lock toggle (unstyled button) */
    1945 .cf-admin-lock-toggle {
    1946   background: none;
    1947   border: none;
    1948   cursor: pointer;
     1968/* Lock as correct: a labelled button that fills in when locked */
     1969.cf-admin-source-cell .button.cf-admin-lock-toggle {
     1970  display: inline-flex;
     1971  align-items: center;
     1972  gap: 3px;
     1973  font-size: 12px;
     1974  min-height: 24px;
     1975  line-height: 22px;
     1976  padding: 0 8px 0 6px;
     1977}
     1978.cf-admin-lock-toggle .dashicons {
    19491979  font-size: 14px;
    1950   padding: 0 2px;
    1951   vertical-align: middle;
     1980  width: 14px;
     1981  height: 14px;
     1982}
     1983.cf-admin-source-cell .button.cf-admin-lock-toggle.is-locked {
     1984  background: #fcf3e1;
     1985  border-color: #dba617;
     1986  color: #8a5b00;
    19521987}
    19531988
    … …  
    19992034  font-size: 12px;
    20002035  padding: 8px 12px;
     2036}
     2037
     2038/* Remove…: asks why, in a bar under the row */
     2039.cf-admin-inline-bar-remove {
     2040  background: #f6f7f7;
     2041  border-left: 3px solid #8c8f94;
     2042  font-size: 13px;
     2043  padding: 10px 14px;
     2044}
     2045.cf-admin-remove-choices {
     2046  display: flex;
     2047  flex-wrap: wrap;
     2048  align-items: stretch;
     2049  gap: 8px;
     2050  margin-top: 8px;
     2051}
     2052.cf-admin-remove-choices .button {
     2053  display: flex;
     2054  flex-direction: column;
     2055  align-items: flex-start;
     2056  gap: 2px;
     2057  max-width: 380px;
     2058  height: auto;
     2059  padding: 6px 12px;
     2060  line-height: 1.4;
     2061  text-align: left;
     2062  white-space: normal;
     2063}
     2064.cf-admin-remove-choices .button span,
     2065.cf-admin-remove-hint {
     2066  font-size: 12px;
     2067  color: #646970;
     2068}
     2069.cf-admin-remove-hint {
     2070  display: block;
     2071  margin-top: 2px;
     2072}
     2073.cf-admin-remove-cancel {
     2074  align-self: center;
     2075  margin-left: 4px;
    20012076}
    20022077
    … …  
    20532128}
    20542129
    2055 /* Not-active indicator label */
    2056 .cf-admin-not-active-label {
    2057   font-size: 11px;
    2058   color: #646970;
    2059   display: inline-block;
    2060   margin-top: 3px;
    2061 }
    2062 
    20632130/* Origin column cell */
    20642131.cf-admin-origin-cell {
    20652132  font-size: 12px;
    2066 }
    2067 
    2068 /* Ignore link — muted by default */
    2069 .cf-admin-ignore-cookie.cf-admin-source-link {
    2070   color: #888;
    20712133}
    20722134
  • cookieflame/tags/5.3.0/assets/js/admin.js

    r3766806 r3769546  
    148148   * Queue a DELETE for a single cookie via the REST endpoint.
    149149   */
    150   function queueDelete(cookieName, opts, onDone) {
     150  function queueDelete(cookieName, opts, onDone, onFail) {
    151151    // opts is optional: queueDelete(name, fn) or queueDelete(name, {dismissed:true}, fn)
    152     if (typeof opts === "function") { onDone = opts; opts = {}; }
     152    if (typeof opts === "function") { onFail = onDone; onDone = opts; opts = {}; }
    153153    opts = opts || {};
    154154    fetch(cookieflameAdmin.cookieUrl + "/" + encodeURIComponent(cookieName), {
    … …  
    164164    .catch(function (err) {
    165165      cookieflameNotice("Failed to remove \"" + cookieName + "\": " + err.message, "error");
     166      if (onFail) onFail();
    166167    });
    167168  }
    … …  
    463464  }
    464465
     466  /**
     467   * Whether a cookie in the admin's own browser is another consent tool's
     468   * cookie when that tool doesn't run on this site (its plugin isn't active
     469   * and its script hasn't been seen on the site's pages). Browser extensions
     470   * that get around consent banners write these. Same rules as the visitor
     471   * check in cookieconsent.js, without the page context.
     472   */
     473  function isVisitorOnlyCookie(name) {
     474    var rules = cookieflameAdmin && cookieflameAdmin.visitorCookieRules;
     475    if (!rules || !rules.tools) return false;
     476    if (rules.allow && rules.allow.indexOf(name) !== -1) return false;
     477    var domains = cookieflameAdmin.siteScriptDomains || [];
     478    for (var t = 0; t < rules.tools.length; t++) {
     479      var tool = rules.tools[t], hit = (tool.n || []).indexOf(name) !== -1;
     480      for (var p = 0; !hit && p < (tool.p || []).length; p++) {
     481        if (name.indexOf(tool.p[p]) === 0) hit = true;
     482      }
     483      if (!hit) continue;
     484      for (var d = 0; d < domains.length; d++) {
     485        for (var k = 0; k < (tool.d || []).length; k++) {
     486          var host = String(domains[d]).toLowerCase(), dom = tool.d[k];
     487          if (host === dom || host.slice(-dom.length - 1) === "." + dom) return false;
     488        }
     489      }
     490      return true;
     491    }
     492    return false;
     493  }
     494
    465495  /* ═══════════════════════════════════════════
    466496     RENDER SCAN TABLE
    … …  
    650680    var CAT_ORDER  = { necessary: 0, preferences: 1, analytics: 2, marketing: 3, unclassified: 4 };
    651681
     682    var ignoredSet = {};
     683    var ignoredList = (cookieflameAdmin && cookieflameAdmin.ignoredCookies) || [];
     684    for (var il = 0; il < ignoredList.length; il++) {
     685      if (ignoredList[il] && ignoredList[il].name) ignoredSet[ignoredList[il].name] = true;
     686    }
     687
    652688    // Pre-process: build row data objects for sorting
    653689    var rowData = [];
    … …  
    660696      var custom = findCustom(name);
    661697      var builtin = identifyBuiltIn(name);
     698
     699      // Cookies you haven't classified stay out of the table when they are on
     700      // the Permanently Ignored list, or when only your own browser carries
     701      // them and they belong to a consent tool that doesn't run on this site.
     702      if (!custom && ignoredSet[name]) continue;
     703      if (!custom && !beaconData[name] && !cookieOriginMap[name] && isVisitorOnlyCookie(name)) continue;
    662704
    663705      // Determine source (same logic as before)
    … …  
    794836        divider.innerHTML = '<td colspan="7">' +
    795837          '<span class="cf-admin-cat-dot" style="background:' + (CAT_COLORS[lastCat] || "#a7aaad") + '"></span>' +
    796           '<span class="cf-admin-cat-label">' + esc(lastCat.charAt(0).toUpperCase() + lastCat.slice(1)) + '</span>' +
     838          '<span class="cf-admin-cat-label">' + esc(lastCat === "unclassified" ? "Not identified yet" : lastCat.charAt(0).toUpperCase() + lastCat.slice(1)) + '</span>' +
    797839          '<span class="cf-admin-cat-count">(' + catCount + ')</span>' +
    798840          '</td>';
    … …  
    800842      }
    801843
    802       // Source column content
    803       var removeTip = 'Removes this cookie from the Cookie Manager. It will reappear as a new unclassified cookie if a visitor still has it in their browser. Use this if you classified it incorrectly and want to start again.';
    804       var ignoreTip = 'Permanently hides this cookie from the Cookie Manager. Visitor sessions will silently skip it and it will not trigger email notifications. Use this for cookies that are not from your site, such as those set by browser extensions. If you later reinstall the service that set it, Start Cookie Discovery will detect it again and automatically remove it from the ignore list. You can also restore ignored cookies manually from the Ignored Cookies panel at the bottom of this page.';
    805       var removeHtml = '<br><a href="#" class="cf-admin-delete-cookie cf-admin-source-link" data-name="' + esc(rd.name) + '">Remove</a>' +
    806         ' <button type="button" class="cf-admin-help-btn cf-admin-help-btn-sm" ' +
    807         'data-help-title="Remove cookie" data-help="' + esc(removeTip) + '">?</button>' +
    808         '<br><a href="#" class="cf-admin-ignore-cookie cf-admin-source-link" data-name="' + esc(rd.name) + '">Ignore permanently</a>' +
    809         ' <button type="button" class="cf-admin-help-btn cf-admin-help-btn-sm" ' +
    810         'data-help-title="Ignore permanently" data-help="' + esc(ignoreTip) + '">?</button>';
    811 
    812       var sourceHtml;
    813 
    814       // Helper: lock badge + toggle for classified sources
    815       function _lockBadgeHtml(src, lock, name) {
    816         var badge, lockIcon, lockTip, aiLabel, reIdLabel;
    817         // Determine badge
    818         if (lock === "locked") {
    819           badge = '<span class="cf-admin-badge-locked">&#128274; Locked</span>';
    820         } else if (lock === "edited") {
    821           badge = '<span class="cf-admin-badge-edited">&#9998; Customised</span>';
    822         } else {
    823           // Managed: show original source label
    824           if (src === "ai") { aiLabel = "Auto-Identified"; }
    825           else if (src === "discovered") { aiLabel = "Classified"; }
    826           else if (src === "manual") { aiLabel = "Manual"; }
    827           else if (src === "custom") { aiLabel = "Edited"; }
    828           else { aiLabel = "Classified"; }
    829           badge = '<span class="cf-admin-badge-ai">&#10003; ' + aiLabel + '</span>';
    830         }
    831         // Lock toggle button
    832         lockIcon = lock === "locked" ? "&#128274;" : "&#128275;";
    833         lockTip = lock === "locked"
    834           ? "This cookie is locked. Auto-Identify All will skip it. Click to unlock."
    835           : "Click to lock this cookie. Locking prevents Auto-Identify All from changing your classification.";
    836         badge += ' <button type="button" class="cf-admin-lock-toggle" data-name="' + esc(name) + '" title="' + esc(lockTip) + '" style="opacity:' + (lock === "locked" ? '1' : '0.4') + ';">' + lockIcon + '</button>';
    837         // Re-identify vs Auto-Identify label
    838         reIdLabel = (src === "ai" || lock === "edited") ? "Re-identify" : "Auto-Identify";
    839         if (cookieflameAdmin.isPremium) {
    840           var greyStyle = lock === "locked" ? ' style="color:#999;cursor:default;"' : '';
    841           badge += '<br><a href="#" class="cf-admin-identify-link cf-admin-ai-row-btn cf-admin-source-link" data-name="' + esc(name) + '"' + greyStyle + '>' + reIdLabel + '</a>';
    842         }
    843         return badge;
    844       }
    845 
    846       if (rd.source === "custom") {
    847         sourceHtml = _lockBadgeHtml("custom", rd.lock, rd.name);
    848         sourceHtml += '<br><a href="#" class="cf-admin-revert-btn cf-admin-source-link" data-name="' + esc(rd.name) + '">Revert to default</a>';
    849         sourceHtml += removeHtml;
    850       } else if (rd.source === "ai") {
    851         sourceHtml = _lockBadgeHtml("ai", rd.lock, rd.name);
    852         sourceHtml += removeHtml;
    853       } else if (rd.source === "manual") {
    854         sourceHtml = _lockBadgeHtml("manual", rd.lock, rd.name);
    855         sourceHtml += removeHtml;
    856       } else if (rd.source === "discovered") {
    857         sourceHtml = _lockBadgeHtml("discovered", rd.lock, rd.name);
    858         sourceHtml += removeHtml;
    859       } else if (rd.source === "builtin") {
    860         sourceHtml = '<span class="cf-admin-badge-known">&#10003; Known</span>';
    861         sourceHtml += '<br><a href="#" class="cf-admin-identify-link cf-admin-ai-row-btn cf-admin-source-link" data-name="' + esc(rd.name) + '">Auto-Identify</a>' + proRowPill;
    862         sourceHtml += removeHtml;
    863       } else if (rd.isBeacon) {
    864         sourceHtml = '<span class="cf-admin-badge-unclassified">&#9888; Reported</span>';
    865         sourceHtml += '<br><a href="#" class="cf-admin-ai-row-btn cf-admin-source-link" data-name="' + esc(rd.name) + '">Auto-Identify</a>' + proRowPill;
    866         sourceHtml += removeHtml;
    867       } else {
    868         sourceHtml = '<span class="cf-admin-badge-unclassified">&#9888; Unclassified</span>';
    869         sourceHtml += '<br><a href="#" class="cf-admin-ai-row-btn cf-admin-source-link" data-name="' + esc(rd.name) + '">Auto-Identify</a>' + proRowPill;
    870         sourceHtml += removeHtml;
    871       }
    872 
    873       // "Not active" label in Source column
    874       if (!rd.inBrowser) {
    875         var notActiveLabel = rd.isBeacon ? "Reported by visitors" : "Detected, not active";
    876         var notActiveTip = rd.isBeacon
    877           ? "This cookie was found by visitors browsing your site. It is not set in your admin browser because marketing and analytics scripts do not run in the admin area. Classify it so CookieFlame manages it correctly when visitors encounter it."
    878           : "This cookie was found during a discovery scan on your site but is not set in your browser right now. This is normal. You are viewing the Cookie Manager in the admin area where marketing and analytics scripts do not run. The cookie will be set for visitors on the frontend when they interact with the service or consent to its category. You should still classify it so CookieFlame manages it correctly when visitors encounter it.";
    879         sourceHtml += '<br><span class="cf-admin-not-active-label">' + notActiveLabel +
    880           ' <button type="button" class="cf-admin-help-btn cf-admin-help-btn-sm" ' +
    881           'data-help-title="' + esc(notActiveLabel) + '" data-help="' + esc(notActiveTip) + '">?</button></span>';
    882       }
    883 
    884       // Residual cookie badge (deactivated/removed plugin)
     844      // Status column: one label, then the actions (5.3.0).
    885845      var residual = residualMap[rd.name];
    886       if (residual) {
    887         sourceHtml += '<br><span class="cf-admin-badge-residual" data-residual-name="' + esc(rd.name) + '">&#128337; Residual</span>' +
    888           '<span class="cf-admin-residual-from">from ' + esc(residual.plugin) + '</span>';
    889       }
     846      var sourceHtml = statusCellHtml(rd, beaconData[rd.name], residual, proRowPill);
    890847
    891848      // Recommended label below category dropdown
    … …  
    978935    }
    979936
    980     // Bind remove buttons for manually added cookies
    981     // Bind per-row delete buttons (inline confirmation, no confirm() dialog)
    982     var delBtns = tbody.querySelectorAll(".cf-admin-delete-cookie");
    983     for (var rb = 0; rb < delBtns.length; rb++) {
    984       delBtns[rb].addEventListener("click", function (e) {
    985         e.preventDefault();
    986         var link = this;
    987         var n = link.getAttribute("data-name");
    988         var tr = link.closest("tr");
    989 
    990         // If already showing confirmation, ignore
    991         if (link.getAttribute("data-confirming")) return;
    992         link.setAttribute("data-confirming", "1");
    993 
    994         // Replace link text with inline confirmation
    995         var origHtml = link.innerHTML;
    996         link.style.display = "none";
    997         var confirmSpan = document.createElement("span");
    998         confirmSpan.className = "cf-admin-meta-hint";
    999         confirmSpan.innerHTML = 'Remove <strong>' + esc(n) + '</strong>? ' +
    1000           '<a href="#" class="cf-admin-confirm-yes cf-admin-link-danger">Yes, remove</a>' +
    1001           ' / <a href="#" class="cf-admin-confirm-no cf-admin-link-cancel">Cancel</a>';
    1002         link.parentNode.insertBefore(confirmSpan, link.nextSibling);
    1003 
    1004         confirmSpan.querySelector(".cf-admin-confirm-yes").addEventListener("click", function (ev) {
    1005           ev.preventDefault();
    1006           var source = tr ? (tr.getAttribute("data-source") || "") : "";
    1007           // Track as dismissed for builtin cookies (hides from table)
    1008           if (source === "builtin" && dismissedCookies.indexOf(n) === -1) {
    1009             dismissedCookies.push(n);
    1010           }
    1011           // Use DELETE endpoint
    1012           var delOpts = source === "builtin" ? { dismissed: true } : {};
    1013           queueDelete(n, delOpts, function () {
    1014             // Remove residual explanation bar if present
    1015             var resBar = tbody.querySelector('.cf-admin-residual-bar[data-residual-for="' + n + '"]');
    1016             if (resBar) resBar.remove();
    1017             if (tr) tr.remove();
    1018             cookieflameNotice("\"" + n + "\" removed.", "success");
    1019           });
    1020         });
    1021 
    1022         confirmSpan.querySelector(".cf-admin-confirm-no").addEventListener("click", function (ev) {
    1023           ev.preventDefault();
    1024           confirmSpan.remove();
    1025           link.style.display = "";
    1026           link.removeAttribute("data-confirming");
    1027         });
    1028       });
     937    // Bind each row's Remove… link and Lock as correct button
     938    var statusCells = tbody.querySelectorAll("td.cf-admin-source-cell");
     939    for (var cellIdx = 0; cellIdx < statusCells.length; cellIdx++) {
     940      bindStatusCell(statusCells[cellIdx], statusCells[cellIdx].closest("tr"));
    1029941    }
    1030942
    … …  
    1035947        e.preventDefault();
    1036948        identifySingleWithAi(this);
    1037       });
    1038     }
    1039 
    1040     // Bind per-row "Ignore permanently" buttons
    1041     var ignoreBtns = tbody.querySelectorAll(".cf-admin-ignore-cookie");
    1042     for (var ig = 0; ig < ignoreBtns.length; ig++) {
    1043       ignoreBtns[ig].addEventListener("click", function (e) {
    1044         e.preventDefault();
    1045         var link = this;
    1046         var n = link.getAttribute("data-name");
    1047         var tr = link.closest("tr");
    1048 
    1049         if (link.getAttribute("data-confirming")) return;
    1050         link.setAttribute("data-confirming", "1");
    1051 
    1052         // Check beacon context for confidence-aware message.
    1053         var bc = getBeaconContext(n);
    1054         var isLikelyExtension = bc && bc.pre_existing;
    1055         var confirmMsg;
    1056         if (isLikelyExtension) {
    1057           confirmMsg = 'This appears to be a browser extension cookie. ' +
    1058             'It will be permanently hidden from notifications and your cookie declaration.';
    1059         } else {
    1060           confirmMsg = 'This cookie and the script that sets it will continue to fire without visitor consent. ' +
    1061             'It will not appear in your cookie declaration or notifications.';
    1062         }
    1063 
    1064         link.style.display = "none";
    1065         var confirmSpan = document.createElement("span");
    1066         confirmSpan.className = "cf-admin-meta-hint";
    1067         confirmSpan.innerHTML = '<span class="cf-admin-link-warning">' + esc(confirmMsg) + '</span><br>' +
    1068           '<a href="#" class="cf-admin-ignore-yes cf-admin-link-danger">Yes, ignore permanently</a>' +
    1069           ' / <a href="#" class="cf-admin-ignore-no cf-admin-link-cancel">Cancel</a>';
    1070         link.parentNode.insertBefore(confirmSpan, link.nextSibling);
    1071 
    1072         confirmSpan.querySelector(".cf-admin-ignore-yes").addEventListener("click", function (ev) {
    1073           ev.preventDefault();
    1074 
    1075           // AJAX call to add to ignored list.
    1076           fetch(cookieflameAdmin.ignoredCookiesUrl, {
    1077             method: "POST",
    1078             headers: { "Content-Type": "application/json", "X-WP-Nonce": cookieflameAdmin.nonce },
    1079             credentials: "same-origin",
    1080             body: JSON.stringify({ action: "ignore", name: n })
    1081           })
    1082             .then(function (r) { return r.json(); })
    1083             .then(function (data) {
    1084               if (data.success) {
    1085                 // Update local state.
    1086                 cookieflameAdmin.ignoredCookies = data.ignored || [];
    1087                 // Remove from in-memory data.
    1088                 savedCustom = savedCustom.filter(function (c) { return c.name !== n; });
    1089                 if (deletedCookies.indexOf(n) === -1) deletedCookies.push(n);
    1090                 if (dismissedCookies.indexOf(n) === -1) dismissedCookies.push(n);
    1091                 // Remove row.
    1092                 var resBarIg = tbody.querySelector('.cf-admin-residual-bar[data-residual-for="' + n + '"]');
    1093                 if (resBarIg) resBarIg.remove();
    1094                 if (tr) tr.remove();
    1095                 // Refresh ignored section.
    1096                 renderIgnoredSection();
    1097                 cookieflameNotice("\"" + n + "\" permanently ignored.", "success");
    1098               }
    1099             })
    1100             .catch(function () {
    1101               cookieflameNotice("Could not ignore cookie. Please try again.", "error");
    1102               confirmSpan.remove();
    1103               link.style.display = "";
    1104               link.removeAttribute("data-confirming");
    1105             });
    1106         });
    1107 
    1108         confirmSpan.querySelector(".cf-admin-ignore-no").addEventListener("click", function (ev) {
    1109           ev.preventDefault();
    1110           confirmSpan.remove();
    1111           link.style.display = "";
    1112           link.removeAttribute("data-confirming");
    1113         });
    1114       });
    1115     }
    1116 
    1117     // Bind lock toggle buttons (padlock icon on classified cookies)
    1118     var lockBtns = tbody.querySelectorAll(".cf-admin-lock-toggle");
    1119     for (var lb = 0; lb < lockBtns.length; lb++) {
    1120       lockBtns[lb].addEventListener("click", function (e) {
    1121         e.preventDefault();
    1122         var cookieName = this.getAttribute("data-name");
    1123         var tr = this.closest("tr");
    1124         if (!tr) return;
    1125         var curLock = tr.getAttribute("data-lock") || "";
    1126         var newLock = curLock === "locked" ? "" : "locked";
    1127         tr.setAttribute("data-lock", newLock);
    1128 
    1129         // Update button icon, tooltip and opacity
    1130         this.innerHTML = newLock === "locked" ? "&#128274;" : "&#128275;";
    1131         this.title = newLock === "locked"
    1132           ? "This cookie is locked. Auto-Identify All will skip it. Click to unlock."
    1133           : "Click to lock this cookie. Locking prevents Auto-Identify All from changing your classification.";
    1134         this.style.opacity = newLock === "locked" ? "1" : "0.4";
    1135 
    1136         // Update badge
    1137         var sourceCell = tr.querySelector(".cf-admin-source-cell");
    1138         if (sourceCell) {
    1139           var badge = sourceCell.querySelector("[class^='cf-admin-badge']");
    1140           if (badge) {
    1141             if (newLock === "locked") {
    1142               badge.className = "cf-admin-badge-locked";
    1143               badge.innerHTML = "&#128274; Locked";
    1144             } else {
    1145               var src = tr.getAttribute("data-source") || "";
    1146               var label = src === "ai" ? "Auto-Identified" : src === "discovered" ? "Classified" : src === "manual" ? "Manual" : "Edited";
    1147               badge.className = "cf-admin-badge-ai";
    1148               badge.innerHTML = "&#10003; " + label;
    1149             }
    1150           }
    1151           // Update Re-identify link grey state
    1152           var reIdLink = sourceCell.querySelector(".cf-admin-identify-link");
    1153           if (reIdLink) {
    1154             reIdLink.style.color = newLock === "locked" ? "#999" : "";
    1155             reIdLink.style.cursor = newLock === "locked" ? "default" : "";
    1156           }
    1157         }
    1158 
    1159         queueSave(cookieName, { lock: newLock });
    1160949      });
    1161950    }
    … …  
    1169958        pluginCount + " CookieFlame, " +
    1170959        thirdPartyCount + " third-party, " +
    1171         unknownCount + " unclassified).";
     960        unknownCount + " not identified yet).";
    1172961    }
    1173962
    … …  
    1184973    // Render the permanently ignored cookies section.
    1185974    renderIgnoredSection();
     975  }
     976
     977  /* ═══════════════════════════════════════════
     978     STATUS COLUMN (5.3.0)
     979     One label for where the cookie stands, then its actions: Lock as
     980     correct (Pro), Auto-Identify or Re-identify, Revert to default,
     981     and one Remove… link that asks why before it removes or ignores
     982     the cookie. The explanations live in each link's hover text and
     983     the Status column's help.
     984  ═══════════════════════════════════════════ */
     985
     986  var LOCK_TIP_OFF = "Press this once you've checked the vendor, category and purpose are right. CookieFlame won't change them after that, even when you re-identify.";
     987  var LOCK_TIP_ON  = "You've checked this cookie. CookieFlame won't change its vendor, category or purpose, even when you re-identify. Click to unlock.";
     988
     989  function statusLabelHtml(source, lock) {
     990    if (source === "unknown")  return '<span class="cf-admin-badge-unclassified">Not identified yet</span>';
     991    if (source === "builtin")  return '<span class="cf-admin-badge-known">Known</span>';
     992    if (source === "manual")   return '<span class="cf-admin-badge-edited">Added by you</span>';
     993    if (source === "custom" || lock === "edited") return '<span class="cf-admin-badge-edited">Changed by you</span>';
     994    if (source === "discovered") return '<span class="cf-admin-badge-known">Classified</span>';
     995    return '<span class="cf-admin-badge-ai">Auto-Identified</span>';
     996  }
     997
     998  function lockButtonInner(locked) {
     999    return '<span class="dashicons dashicons-' + (locked ? "lock" : "unlock") + '" aria-hidden="true"></span>' +
     1000      (locked ? "Locked by you" : "Lock as correct");
     1001  }
     1002
     1003  // Where a cookie nobody has identified yet came from. Pro 1.5.1+ marks
     1004  // the cookies its clean scan added with found_by "clean_scan".
     1005  function whereFoundText(rd, beacon) {
     1006    if (beacon && beacon.found_by === "clean_scan") return "Found by your scan";
     1007    if (beacon) return "Reported by visitors";
     1008    if (rd.inBrowser) return "Seen in your browser";
     1009    return "";
     1010  }
     1011
     1012  function statusCellHtml(rd, beacon, residual, proPill) {
     1013    var name = esc(rd.name);
     1014    var classified = ["ai", "manual", "discovered", "custom"].indexOf(rd.source) !== -1;
     1015    var line = function (html, cls, hidden) {
     1016      return '<div class="cf-admin-status-line' + (cls ? " " + cls : "") + '"' + (hidden ? ' style="display:none;"' : "") + '>' + html + '</div>';
     1017    };
     1018
     1019    var html = line(statusLabelHtml(rd.source, rd.lock));
     1020    var where = rd.source === "unknown" ? whereFoundText(rd, beacon) : "";
     1021    if (where) html += line(esc(where), "cf-admin-status-where");
     1022
     1023    // Locking only matters to Pro, which is what re-identifies cookies.
     1024    if (classified && cookieflameAdmin.isPremium) {
     1025      var locked = rd.lock === "locked";
     1026      html += line('<button type="button" class="button button-small cf-admin-lock-toggle' + (locked ? " is-locked" : "") + '" data-name="' + name + '"' +
     1027        ' aria-pressed="' + (locked ? "true" : "false") + '" title="' + esc(locked ? LOCK_TIP_ON : LOCK_TIP_OFF) + '">' + lockButtonInner(locked) + '</button>');
     1028    }
     1029
     1030    if (rd.source === "unknown") {
     1031      html += line('<a href="#" class="cf-admin-ai-row-btn cf-admin-source-link cf-admin-source-link-main" data-name="' + name + '" title="Ask CookieFlame\'s service what this cookie is and fill in its details.">Auto-Identify</a>' + proPill);
     1032    } else if (rd.source === "builtin") {
     1033      html += line('<a href="#" class="cf-admin-identify-link cf-admin-ai-row-btn cf-admin-source-link" data-name="' + name + '" title="Ask CookieFlame\'s service what this cookie is.">Auto-Identify</a>' + proPill);
     1034    } else if (cookieflameAdmin.isPremium) {
     1035      var reLabel = (rd.source === "ai" || rd.lock === "edited") ? "Re-identify" : "Auto-Identify";
     1036      html += line('<a href="#" class="cf-admin-identify-link cf-admin-ai-row-btn cf-admin-source-link" data-name="' + name + '" title="Ask CookieFlame\'s service about this cookie again. Your own changes are only ever suggested, never replaced.">' + reLabel + '</a>',
     1037        "cf-admin-status-reidentify", rd.lock === "locked");
     1038    }
     1039
     1040    if (rd.source === "custom") {
     1041      html += line('<a href="#" class="cf-admin-revert-btn cf-admin-source-link" data-name="' + name + '" title="Go back to the vendor, category and purpose from CookieFlame\'s built-in list.">Revert to default</a>');
     1042    }
     1043    html += line('<a href="#" class="cf-admin-remove-ask cf-admin-source-link" data-name="' + name + '" title="Asks whether your site no longer uses it, or it isn\'t from your site.">Remove&hellip;</a>');
     1044
     1045    if (residual) {
     1046      html += line('<span class="cf-admin-badge-residual" data-residual-name="' + name + '">&#128337; Residual</span>' +
     1047        '<span class="cf-admin-residual-from">from ' + esc(residual.plugin) + '</span>');
     1048    }
     1049    return html;
     1050  }
     1051
     1052  function setLockState(tr, lock) {
     1053    var locked = lock === "locked";
     1054    tr.setAttribute("data-lock", lock);
     1055    var btn = tr.querySelector(".cf-admin-lock-toggle");
     1056    if (btn) {
     1057      btn.classList.toggle("is-locked", locked);
     1058      btn.setAttribute("aria-pressed", locked ? "true" : "false");
     1059      btn.title = locked ? LOCK_TIP_ON : LOCK_TIP_OFF;
     1060      btn.innerHTML = lockButtonInner(locked);
     1061    }
     1062    // A locked cookie isn't re-identified, so the link goes while it's locked.
     1063    var reId = tr.querySelector(".cf-admin-status-reidentify");
     1064    if (reId) reId.style.display = locked ? "none" : "";
     1065  }
     1066
     1067  function bindStatusCell(cell, tr) {
     1068    if (!cell || !tr) return;
     1069    var removeLink = cell.querySelector(".cf-admin-remove-ask");
     1070    if (removeLink) {
     1071      removeLink.addEventListener("click", function (e) {
     1072        e.preventDefault();
     1073        openRemoveBar(tr);
     1074      });
     1075    }
     1076    var lockBtn = cell.querySelector(".cf-admin-lock-toggle");
     1077    if (lockBtn) {
     1078      lockBtn.addEventListener("click", function (e) {
     1079        e.preventDefault();
     1080        var newLock = (tr.getAttribute("data-lock") || "") === "locked" ? "" : "locked";
     1081        setLockState(tr, newLock);
     1082        queueSave(tr.getAttribute("data-cookie-name"), { lock: newLock });
     1083      });
     1084    }
     1085  }
     1086
     1087  /**
     1088   * Remove… asks why, in a bar under the row. "My site doesn't use it any
     1089   * more" removes the cookie, so it comes back if it's seen again. "It's
     1090   * not from my site" puts it on the Permanently Ignored list.
     1091   */
     1092  function openRemoveBar(tr) {
     1093    var n = tr.getAttribute("data-cookie-name");
     1094    var tbody = tr.parentNode;
     1095    if (!n || !tbody) return;
     1096    var key = CSS.escape(n);
     1097    var open = tbody.querySelector('.cf-admin-remove-bar[data-remove-for="' + key + '"]');
     1098    if (open) { open.remove(); return; }
     1099
     1100    var bc = getBeaconContext(n);
     1101    var extHint = (bc && bc.pre_existing)
     1102      ? '<em class="cf-admin-remove-hint">This one was already in a visitor\'s browser before your page loaded, which usually means an extension.</em>'
     1103      : "";
     1104
     1105    var bar = document.createElement("tr");
     1106    bar.className = "cf-admin-remove-bar";
     1107    bar.setAttribute("data-remove-for", n);
     1108    bar.innerHTML = '<td colspan="7" class="cf-admin-inline-bar-remove">' +
     1109      '<strong>Why are you removing <code>' + esc(n) + '</code>?</strong>' +
     1110      '<div class="cf-admin-remove-choices">' +
     1111        '<button type="button" class="button cf-admin-remove-unused"><strong>My site doesn\'t use it any more</strong>' +
     1112          '<span>Taken off your list. If your site sets it again, it comes back to be identified.</span></button>' +
     1113        '<button type="button" class="button cf-admin-remove-notmine"><strong>It\'s not from my site</strong>' +
     1114          '<span>From a visitor\'s browser extension or another website. Hidden for good, and CookieFlame stops managing it. You can bring it back from Permanently Ignored at the bottom of this page.</span>' +
     1115          extHint + '</button>' +
     1116        '<a href="#" class="cf-admin-remove-cancel cf-admin-link-cancel">Cancel</a>' +
     1117      '</div></td>';
     1118    tbody.insertBefore(bar, tr.nextSibling);
     1119
     1120    var buttons = bar.querySelectorAll("button");
     1121    var busy = function (on) {
     1122      for (var i = 0; i < buttons.length; i++) buttons[i].disabled = on;
     1123    };
     1124    var gone = function (msg) {
     1125      var extras = tbody.querySelectorAll('[data-remove-for="' + key + '"], [data-residual-for="' + key + '"], [data-suggestion-for="' + key + '"]');
     1126      for (var i = 0; i < extras.length; i++) extras[i].remove();
     1127      tr.remove();
     1128      cookieflameNotice(msg, "success");
     1129    };
     1130
     1131    bar.querySelector(".cf-admin-remove-unused").addEventListener("click", function () {
     1132      busy(true);
     1133      var source = tr.getAttribute("data-source") || "";
     1134      // A Known cookie still in your browser would show again at once, so it's hidden until seen again.
     1135      if (source === "builtin" && dismissedCookies.indexOf(n) === -1) dismissedCookies.push(n);
     1136      queueDelete(n, source === "builtin" ? { dismissed: true } : {}, function () {
     1137        gone("\"" + esc(n) + "\" removed.");
     1138      }, function () { busy(false); });
     1139    });
     1140
     1141    bar.querySelector(".cf-admin-remove-notmine").addEventListener("click", function () {
     1142      busy(true);
     1143      fetch(cookieflameAdmin.ignoredCookiesUrl, {
     1144        method: "POST",
     1145        headers: { "Content-Type": "application/json", "X-WP-Nonce": cookieflameAdmin.nonce },
     1146        credentials: "same-origin",
     1147        body: JSON.stringify({ action: "ignore", name: n })
     1148      })
     1149        .then(function (r) { return r.json(); })
     1150        .then(function (data) {
     1151          if (!data || !data.success) throw new Error("not ignored");
     1152          cookieflameAdmin.ignoredCookies = data.ignored || [];
     1153          savedCustom = savedCustom.filter(function (c) { return c.name !== n; });
     1154          if (deletedCookies.indexOf(n) === -1) deletedCookies.push(n);
     1155          if (dismissedCookies.indexOf(n) === -1) dismissedCookies.push(n);
     1156          gone("\"" + esc(n) + "\" moved to Permanently Ignored.");
     1157          renderIgnoredSection();
     1158        })
     1159        .catch(function () {
     1160          busy(false);
     1161          cookieflameNotice("Could not ignore cookie. Please try again.", "error");
     1162        });
     1163    });
     1164
     1165    bar.querySelector(".cf-admin-remove-cancel").addEventListener("click", function (e) {
     1166      e.preventDefault();
     1167      bar.remove();
     1168    });
    11861169  }
    11871170
    … …  
    15831566          }
    15841567
     1568          // It's identified now, so where it came from no longer matters.
     1569          var where = sourceCell.querySelector(".cf-admin-status-where");
     1570          if (where) where.remove();
     1571
    15851572          // Update AI link to "Re-identify" and remove lock toggle if present.
    15861573          var aiLink = sourceCell.querySelector(".cf-admin-identify-link, .cf-admin-ai-row-btn");
    … …  
    16151602    if (aiBtn) aiBtn.addEventListener("click", function (e) { e.preventDefault(); identifySingleWithAi(this); });
    16161603
    1617     // Delete (Remove) link — inline confirmation flow
    1618     var delBtn = cell.querySelector(".cf-admin-delete-cookie");
    1619     if (delBtn) {
    1620       delBtn.addEventListener("click", function (e) {
    1621         e.preventDefault();
    1622         var link = this;
    1623         var n = link.getAttribute("data-name");
    1624         if (link.getAttribute("data-confirming")) return;
    1625         link.setAttribute("data-confirming", "1");
    1626         link.style.display = "none";
    1627         var confirmSpan = document.createElement("span");
    1628         confirmSpan.className = "cf-admin-meta-hint";
    1629         confirmSpan.innerHTML = 'Remove <strong>' + esc(n) + '</strong>? ' +
    1630           '<a href="#" class="cf-admin-confirm-yes cf-admin-link-danger">Yes, remove</a>' +
    1631           ' / <a href="#" class="cf-admin-confirm-no cf-admin-link-cancel">Cancel</a>';
    1632         link.parentNode.insertBefore(confirmSpan, link.nextSibling);
    1633         confirmSpan.querySelector(".cf-admin-confirm-yes").addEventListener("click", function (ev) {
    1634           ev.preventDefault();
    1635           var source = tr ? (tr.getAttribute("data-source") || "") : "";
    1636           if (source === "builtin" && dismissedCookies.indexOf(n) === -1) dismissedCookies.push(n);
    1637           var delOpts = source === "builtin" ? { dismissed: true } : {};
    1638           queueDelete(n, delOpts, function () {
    1639             var tbody = tr ? tr.parentNode : null;
    1640             if (tbody) {
    1641               var resBar = tbody.querySelector('.cf-admin-residual-bar[data-residual-for="' + n + '"]');
    1642               if (resBar) resBar.remove();
    1643             }
    1644             if (tr) tr.remove();
    1645             cookieflameNotice("\"" + n + "\" removed.", "success");
    1646           });
    1647         });
    1648         confirmSpan.querySelector(".cf-admin-confirm-no").addEventListener("click", function (ev) {
    1649           ev.preventDefault();
    1650           confirmSpan.remove();
    1651           link.style.display = "";
    1652           link.removeAttribute("data-confirming");
    1653         });
    1654       });
    1655     }
    1656 
    1657     // Ignore link — inline confirmation flow
    1658     var ignoreBtn = cell.querySelector(".cf-admin-ignore-cookie");
    1659     if (ignoreBtn) {
    1660       ignoreBtn.addEventListener("click", function (e) {
    1661         e.preventDefault();
    1662         var link = this;
    1663         var n = link.getAttribute("data-name");
    1664         if (link.getAttribute("data-confirming")) return;
    1665         link.setAttribute("data-confirming", "1");
    1666         link.style.display = "none";
    1667         var confirmSpan = document.createElement("span");
    1668         confirmSpan.className = "cf-admin-meta-hint";
    1669         confirmSpan.innerHTML = 'Permanently ignore <strong>' + esc(n) + '</strong>? ' +
    1670           '<a href="#" class="cf-admin-ignore-yes cf-admin-link-warning">Yes, ignore permanently</a>' +
    1671           ' / <a href="#" class="cf-admin-ignore-no cf-admin-link-cancel">Cancel</a>';
    1672         link.parentNode.insertBefore(confirmSpan, link.nextSibling);
    1673         confirmSpan.querySelector(".cf-admin-ignore-yes").addEventListener("click", function (ev) {
    1674           ev.preventDefault();
    1675           fetch(cookieflameAdmin.ignoredCookiesUrl, {
    1676             method: "POST",
    1677             headers: { "Content-Type": "application/json", "X-WP-Nonce": cookieflameAdmin.nonce },
    1678             credentials: "same-origin",
    1679             body: JSON.stringify({ action: "ignore", name: n })
    1680           })
    1681             .then(function (r) { return r.json(); })
    1682             .then(function (data) {
    1683               if (data.success) {
    1684                 cookieflameAdmin.ignoredCookies = data.ignored || [];
    1685                 savedCustom = savedCustom.filter(function (c) { return c.name !== n; });
    1686                 if (deletedCookies.indexOf(n) === -1) deletedCookies.push(n);
    1687                 if (dismissedCookies.indexOf(n) === -1) dismissedCookies.push(n);
    1688                 var tbody = tr ? tr.parentNode : null;
    1689                 if (tbody) {
    1690                   var resBar = tbody.querySelector('.cf-admin-residual-bar[data-residual-for="' + n + '"]');
    1691                   if (resBar) resBar.remove();
    1692                 }
    1693                 if (tr) tr.remove();
    1694                 renderIgnoredSection();
    1695                 cookieflameNotice("\"" + n + "\" permanently ignored.", "success");
    1696               }
    1697             })
    1698             .catch(function () {
    1699               cookieflameNotice("Could not ignore cookie. Please try again.", "error");
    1700               confirmSpan.remove();
    1701               link.style.display = "";
    1702               link.removeAttribute("data-confirming");
    1703             });
    1704         });
    1705         confirmSpan.querySelector(".cf-admin-ignore-no").addEventListener("click", function (ev) {
    1706           ev.preventDefault();
    1707           confirmSpan.remove();
    1708           link.style.display = "";
    1709           link.removeAttribute("data-confirming");
    1710         });
    1711       });
    1712     }
    1713 
    1714     // Lock toggle
    1715     var lockBtn = cell.querySelector(".cf-admin-lock-toggle");
    1716     if (lockBtn) {
    1717       lockBtn.addEventListener("click", function (e) {
    1718         e.preventDefault();
    1719         var n = this.getAttribute("data-name");
    1720         var currentLock = tr.getAttribute("data-lock") || "";
    1721         var newLock = currentLock === "locked" ? "" : "locked";
    1722         tr.setAttribute("data-lock", newLock);
    1723         this.innerHTML = newLock === "locked" ? "&#128274;" : "&#128275;";
    1724         this.title = newLock === "locked"
    1725           ? "This cookie is locked. Auto-Identify All will skip it. Click to unlock."
    1726           : "Click to lock this cookie. Locking prevents Auto-Identify All from changing your classification.";
    1727         this.style.opacity = newLock === "locked" ? "1" : "0.4";
    1728         var badge = cell.querySelector("[class^='cf-admin-badge']");
    1729         if (badge) {
    1730           if (newLock === "locked") {
    1731             badge.className = "cf-admin-badge-locked";
    1732             badge.innerHTML = "&#128274; Locked";
    1733           } else {
    1734             var src = tr.getAttribute("data-source") || "";
    1735             var label = src === "ai" ? "Auto-Identified" : src === "discovered" ? "Classified" : src === "manual" ? "Manual" : "Edited";
    1736             badge.className = "cf-admin-badge-ai";
    1737             badge.innerHTML = "&#10003; " + label;
    1738           }
    1739         }
    1740         var reIdLink = cell.querySelector(".cf-admin-identify-link");
    1741         if (reIdLink) {
    1742           reIdLink.style.color = newLock === "locked" ? "#999" : "";
    1743           reIdLink.style.cursor = newLock === "locked" ? "default" : "";
    1744         }
    1745         queueSave(n, { lock: newLock });
    1746       });
    1747     }
     1604    // Remove… link and Lock as correct button
     1605    bindStatusCell(cell, tr);
    17481606
    17491607    // Residual badge toggle
    … …  
    20481906          if (badge) {
    20491907            badge.className = "cf-admin-badge-ai";
    2050             badge.innerHTML = "&#10003; Auto-Identified";
     1908            badge.textContent = "Auto-Identified";
    20511909          }
    20521910          // Remove any confirmed tick left over.
    … …  
    20881946        lockPrompt.setAttribute("data-suggestion-for", n);
    20891947        lockPrompt.innerHTML = '<td colspan="7" class="cf-admin-inline-bar-lock">' +
    2090           'Lock this cookie to prevent future suggestions? ' +
     1948          'Lock it as correct, so CookieFlame stops suggesting changes? ' +
    20911949          '<a href="#" class="cf-admin-lock-yes cf-admin-link-cancel" style="margin-left:4px;font-weight:600;">Yes</a>' +
    20921950          ' / <a href="#" class="cf-admin-lock-no cf-admin-meta-hint" style="margin-left:4px;">No</a>' +
    … …  
    20991957        lockPrompt.querySelector(".cf-admin-lock-yes").addEventListener("click", function (ev) {
    21001958          ev.preventDefault();
    2101           cookieRow.setAttribute("data-lock", "locked");
     1959          setLockState(cookieRow, "locked");
    21021960          queueSave(n, { lock: "locked" });
    2103           // Update padlock visuals.
    2104           var lockBtn = cookieRow.querySelector(".cf-admin-lock-toggle");
    2105           if (lockBtn) { lockBtn.innerHTML = "&#128274;"; lockBtn.style.opacity = "1"; }
    2106           var badge = cookieRow.querySelector("[class^='cf-admin-badge']");
    2107           if (badge) { badge.className = "cf-admin-badge-locked"; badge.innerHTML = "&#128274; Locked"; }
    2108           var reIdLink = cookieRow.querySelector(".cf-admin-identify-link");
    2109           if (reIdLink) { reIdLink.style.color = "#999"; reIdLink.style.cursor = "default"; }
    21101961          lockPrompt.remove();
    21111962        });
    … …  
    22122063          if (badge) {
    22132064            badge.className = "cf-admin-badge-ai";
    2214             badge.innerHTML = "&#10003; Auto-Identified";
     2065            badge.textContent = "Auto-Identified";
    22152066          }
    22162067        }
    … …  
    22992150
    23002151    if (expanded) {
    2301       h += '<p class="cf-admin-ignored-desc">These cookies have been permanently excluded from passive monitoring. They will never trigger notifications or appear in your cookie declaration. Click Restore to start monitoring a cookie again.</p>';
    2302       h += '<table class="widefat striped cf-admin-ignored-table"><thead><tr><th>Cookie</th><th>Ignored</th><th></th></tr></thead><tbody>';
     2152      h += '<p class="cf-admin-ignored-desc">These cookies have been permanently excluded from passive monitoring. They will never trigger notifications or appear in your cookie declaration. Cookies with a reason were ignored automatically because they came with a visitor\'s browser, such as another consent tool\'s cookie written by a browser extension. Click Restore to start monitoring a cookie again.</p>';
     2153      h += '<table class="widefat striped cf-admin-ignored-table"><thead><tr><th>Cookie</th><th>Why</th><th>Ignored</th><th></th></tr></thead><tbody>';
    23032154      for (var i = 0; i < ignored.length; i++) {
    23042155        var ig = ignored[i];
    … …  
    23062157        h += '<tr data-ignored-name="' + esc(ig.name) + '">';
    23072158        h += '<td><code>' + esc(ig.name) + '</code></td>';
     2159        h += '<td>' + esc(ig.reason || "Ignored by you") + '</td>';
    23082160        h += '<td>' + esc(when) + '</td>';
    23092161        h += '<td><button type="button" class="button button-small cf-admin-restore-ignored" data-name="' + esc(ig.name) + '">Restore</button></td>';
    … …  
    23442196          .then(function (data) {
    23452197            if (data.success) {
     2198              // The server remembers restored automatic entries; mirror that so
     2199              // the Cookie Manager shows the cookie again without a reload.
     2200              var rules = cookieflameAdmin.visitorCookieRules;
     2201              var wasAuto = (cookieflameAdmin.ignoredCookies || []).some(function (ig) { return ig.name === n && ig.auto; });
     2202              if (wasAuto && rules && rules.allow && rules.allow.indexOf(n) === -1) rules.allow.push(n);
    23462203              cookieflameAdmin.ignoredCookies = data.ignored || [];
    23472204              renderIgnoredSection();
    … …  
    27122569            tr.setAttribute("data-source", "discovered");
    27132570            tr.className = "";
    2714             // Update source cell badge + lock icon after save completes
     2571            // Update the status label; it no longer needs identifying
    27152572            var srcCell = tr.querySelector(".cf-admin-source-cell");
    27162573            if (srcCell) {
    … …  
    27182575              if (badge) {
    27192576                badge.className = "cf-admin-badge-known";
    2720                 badge.innerHTML = "&#10003; Classified";
     2577                badge.textContent = "Classified";
    27212578              }
     2579              var where = srcCell.querySelector(".cf-admin-status-where");
     2580              if (where) where.remove();
    27222581            }
    27232582          }
  • cookieflame/tags/5.3.0/assets/js/cookieconsent-discovery.js

    r3766806 r3769546  
    11/**
    2  * CookieFlame Discovery Bundle v5.1.1
     2 * CookieFlame Discovery Bundle v5.3.0
    33 * ─────────────────────────────────────────────────────────────
    44 * Admin-only script detection and discovery bar. Loaded
    … …  
    287287      var seen = {};
    288288      var hasBeforeSnapshot = Object.keys(beforeCookieSet).length > 0;
     289      // The Cookie Manager doesn't list cookies that came with the browser
     290      // (another consent tool's cookie, or one a browser extension wrote) or
     291      // ignored ones, so they aren't counted here either.
     292      var visitor = _cc.visitorCookies ? _cc.visitorCookies(current) : {};
     293      var ignoredNames = (window.cookieflameAdmin && window.cookieflameAdmin.ignoredCookieNames) || [];
    289294      for (var i = 0; i < current.length; i++) {
    290295        var name = current[i];
    … …  
    292297        seen[name] = true;
    293298        if (isSafeForCounter(name)) continue;
     299        if (visitor[name] || ignoredNames.indexOf(name) !== -1) continue;
    294300        if (hasBeforeSnapshot && beforeCookieSet[name]) {
    295301          existing.push(name);
  • cookieflame/tags/5.3.0/assets/js/cookieconsent-discovery.min.js

    r3766806 r3769546  
    11/**
    2  * CookieFlame Discovery Bundle v5.1.1
     2 * CookieFlame Discovery Bundle v5.3.0
    33 * ─────────────────────────────────────────────────────────────
    44 * Admin-only script detection and discovery bar. Loaded
    … …  
    1313 * the core bundle despite the declared WordPress dependency.
    1414 */
    15 !function(){"use strict";var e=window.__ccInternal;if(e){var t=e.scriptLoaded,o=e.scriptLoadedUrls,n=e.hasCookie,i={"Google Analytics":function(){return t("googletagmanager.com/gtag")||t("google-analytics.com")||t("analytics.google.com")||n("_ga")},"Google Ads":function(){return t("googleads.g.doubleclick.net")||t("googleadservices.com")||t("googlesyndication.com")||n("_gcl_au")||n("_gcl_aw")},"Google Tag Manager":function(){return t("googletagmanager.com/gtm.js")||!!window.google_tag_manager},"Microsoft Ads":function(){return t("bat.bing.com")||!!window.uetq||n("_uetsid")||n("_uetvid")},"Meta / Facebook":function(){return t("connect.facebook.net")||!!window.fbq||n("_fbp")||n("_fbc")},"Twitter / X":function(){return t("ads-twitter.com")||t("static.ads-twitter.com")||!!window.twq||n("_twpid")},LinkedIn:function(){return t("snap.licdn.com")||t("ads.linkedin.com")||!!window._linkedin_data_partner_ids||n("li_sugr")||n("bcookie")||n("lidc")},Pinterest:function(){return t("s.pinimg.com/ct")||t("pintrk")||!!window.pintrk||n("_pin_unauth")},Reddit:function(){return t("alb.reddit.com")||t("events.reddit.com")||!!window.rdt||n("_rdt_uuid")},TikTok:function(){return t("analytics.tiktok.com")||!!window.ttq}};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",a):setTimeout(a,100)}function a(){var t=window.__ccDiscovery;if(t&&t.active&&(!document.body||!document.body.classList.contains("wp-admin"))){var n=function(){try{var e=sessionStorage.getItem("cookieflame_discovery_services");return e?JSON.parse(e):{}}catch(e){return{}}}(),a={};try{var r=localStorage.getItem("cookieflame_discovery_before");if(r)for(var c=JSON.parse(r),s=0;s<c.length;s++)a[c[s]]=!0}catch(e){}var d=["wordpress_","wp-settings","wp_lang","PHPSESSID","cc_consent","cc_anon_id","cookieflame_","woocommerce_","wp_woocommerce_"],l=document.createElement("div");l.id="cf-admin-discovery-bar",l.style.cssText="position:fixed;top:0;left:0;right:0;z-index:999999;background:#1d2327;color:#f0f0f1;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:14px;display:flex;align-items:center;justify-content:space-between;padding:10px 20px;box-shadow:0 2px 8px rgba(0,0,0,.3);gap:16px;flex-wrap:wrap;";var m=document.createElement("div");m.style.cssText="display:flex;align-items:center;gap:12px;flex:1;min-width:200px;";var g=document.createElement("span");g.style.cssText="width:10px;height:10px;border-radius:50%;background:#00ba37;display:inline-block;flex-shrink:0;animation:cf-admin-pulse 2s infinite;";var p=document.createElement("span");p.textContent="Cookie Discovery Active",p.style.fontWeight="600";var f=document.createElement("span");f.id="cf-admin-discovery-count",f.style.cssText="background:#2271b1;color:#fff;padding:2px 10px;border-radius:12px;font-size:13px;font-weight:600;cursor:help;",f.textContent="scanning...",m.appendChild(g),m.appendChild(p),m.appendChild(f);var u=document.createElement("span");u.style.cssText="color:#a7aaad;font-size:13px;display:inline-flex;flex-direction:column;gap:2px;";var h=document.createElement("span");h.textContent="All cookies are temporarily accepted. Browse your pages, then click Finish.";var v=document.createElement("span");v.textContent="Tip: Browse by clicking links on the site. Typed URLs may skip discovery.",v.style.cssText="font-size:11px;opacity:0.75;",u.appendChild(h),u.appendChild(v),C(),setTimeout(C,500),setTimeout(C,2e3);var y=document.createElement("button");y.textContent="Finish Discovery",y.style.cssText="background:#00ba37;color:#fff;border:none;padding:8px 20px;border-radius:4px;font-size:14px;font-weight:600;cursor:pointer;white-space:nowrap;",y.onmouseover=function(){y.style.background="#009929"},y.onmouseout=function(){y.style.background="#00ba37"},l.appendChild(m),l.appendChild(u),l.appendChild(y);var k=document.createElement("style");k.textContent="@keyframes cf-admin-pulse{0%,100%{opacity:1}50%{opacity:.4}}#cf-admin-discovery-bar~*{margin-top:0!important;}body.admin-bar #cf-admin-discovery-bar{top:32px}@media screen and (max-width:782px){body.admin-bar #cf-admin-discovery-bar{top:46px}}",document.head.appendChild(k),document.body.insertBefore(l,document.body.firstChild),document.addEventListener("click",function(e){for(var t=e.target;t&&1===t.nodeType&&"A"!==t.tagName;)t=t.parentNode;if(t&&"A"===t.tagName){var o=t.getAttribute("target");if("_blank"!==o&&"_top"!==o&&"_parent"!==o){var n=t.getAttribute("rel")||"";if(!n||-1===n.split(/\s+/).indexOf("external")){var i=t.getAttribute("href");if(i){var a;try{a=new URL(i,location.href)}catch(e){return}"http:"!==a.protocol&&"https:"!==a.protocol||a.origin===location.origin&&(a.pathname===location.pathname&&a.search===location.search&&a.hash!==location.hash||a.searchParams.has("cookieflame")||(a.searchParams.set("cookieflame","discover"),t.setAttribute("href",a.toString())))}}}}},!0),setTimeout(x,5e3);var w=setInterval(x,3e3);y.addEventListener("click",function(){y.disabled=!0,y.textContent="Finishing...",y.style.opacity="0.7",clearInterval(w),x();var e=n,o=window.__ccConfig||{};try{localStorage.setItem("cookieflame_discovery_results",JSON.stringify(e))}catch(t){var i=encodeURIComponent(JSON.stringify(e)),a=o.cookieDomain?"; domain="+o.cookieDomain:"";document.cookie="cookieflame_discovery_results="+i+"; path=/; max-age=300"+a}fetch(t.finishUrl,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","X-WP-Nonce":t.nonce},body:"{}"}).then(function(e){if(403===e.status)throw new Error("Your session has expired. Please reload the page and try again.");return e.json()}).then(function(e){var i=[location.hostname,"."+location.hostname];o.cookieDomain&&i.push(o.cookieDomain);for(var a=0;a<i.length;a++)document.cookie="cc_consent=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i[a];if(document.cookie="cc_consent=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/",e.restore_dnt){var r=o.cookieDomain?"; domain="+o.cookieDomain:"";document.cookie="cookieflame_dnt=1; path=/; max-age=31536000"+r}try{sessionStorage.removeItem("cookieflame_discovery_services")}catch(e){}for(var c=_(),s=0,d={},m=Object.keys(n),g=0;g<m.length;g++){var p=n[m[g]];if(p.unknown)s++,d[m[g]]=!0;else{var f=p.cookies||[];s+=f.length;for(var u=0;u<f.length;u++)d[f[u]]=!0}}for(var h=c.confirmed.concat(c.existing),v=0;v<h.length;v++)d[h[v]]||s++;l.style.background="#00ba37",l.innerHTML="";var y=document.createElement("span");y.style.cssText="font-weight:600;flex:1;text-align:center;";var k=s+" cookie"+(1!==s?"s":"")+" discovered. Redirecting to Cookie Manager...";y.textContent=k,l.appendChild(y),setTimeout(function(){window.location.href=t.adminUrl},1500)}).catch(function(e){y.disabled=!1,y.textContent="Finish Discovery",y.style.opacity="1",l.style.background="#d63638",u.style.color="#fff",u.textContent="Error: "+e.message})})}function x(){for(var t=function(){var t=[],n=e.getResourceSnapshot();e.setResourceSnapshot(n);for(var a=window.cookieflameAdmin&&window.cookieflameAdmin.serviceDetectors||[],r=[],c=0;c<a.length;c++)for(var s=a[c].scriptDomains||[],d=0;d<s.length;d++)r.push(s[d]);for(var l=0;l<a.length;l++){var m=a[l],g=m.name||"",p=i[g];if(p)try{if(p()){var f=o(g.toLowerCase().replace(/\s+/g,""));if(0===f.length&&-1!==g.indexOf(" "))for(var u=g.toLowerCase().split(" "),h=0;h<u.length&&0===f.length;h++)u[h].length>3&&(f=o(u[h]));t.push({name:g,cookies:m.cookies||[],canonicalDomain:m.canonicalDomain||"",scriptUrls:f})}}catch(e){}}t.pagePlugins=n.pluginScripts;for(var v=["wp.com","wordpress.com","wordpress.org","s.w.org","gravatar.com","cdnjs.cloudflare.com","cdn.jsdelivr.net","unpkg.com","ajax.googleapis.com","fonts.googleapis.com","fonts.gstatic.com","code.jquery.com","stackpath.bootstrapcdn.com","maxcdn.bootstrapcdn.com","www.google.com/recaptcha","www.gstatic.com","apis.google.com","maps.googleapis.com","maps.google.com","challenges.cloudflare.com","static.cloudflareinsights.com","chromium.org","mozilla.org","lazyload","rocket-loader","elementor.com"],y=n.thirdPartyDomains,k=0;k<y.length;k++){for(var w=y[k],x=!1,b=0;b<v.length;b++)if(w===v[b]||-1!==w.indexOf("."+v[b])||-1!==v[b].indexOf(w)){x=!0;break}if(!x){for(var _=!1,C=0;C<r.length;C++)if(-1!==w.indexOf(r[C])||-1!==r[C].indexOf(w)){_=!0;break}_||t.push({name:w,cookies:[],unknown:!0})}}return e.setResourceSnapshot(null),t}(),a=0;a<t.length;a++)n[t[a].name]||(n[t[a].name]=t[a]);if(t.pagePlugins&&t.pagePlugins.length){for(var r=n.__pagePlugins||{},c=0;c<t.pagePlugins.length;c++)r[t.pagePlugins[c].slug]=t.pagePlugins[c].url;n.__pagePlugins=r}!function(e){try{sessionStorage.setItem("cookieflame_discovery_services",JSON.stringify(e))}catch(e){}}(n),function(){var e=document.getElementById("cf-admin-discovery-count");if(e){for(var t=_(),o=(t.confirmed.length,t.existing.length,0),i={},a=Object.keys(n),r=[],c=[],s=0;s<a.length;s++){var d=n[a[s]];if(d.unknown)o++,c.push(a[s]),i[a[s]]=!0;else{r.push(a[s]);var l=d.cookies||[];o+=l.length;for(var m=0;m<l.length;m++)i[l[m]]=!0}}for(var g=t.confirmed.concat(t.existing),p=0;p<g.length;p++)i[g[p]]||o++;if(0===o)e.textContent="no cookies yet",e.title="Browse your site to discover cookies";else{e.textContent=o+" cookie"+(1!==o?"s":"")+" discovered";var f="";r.length>0&&(f+="Services: "+r.sort().join(", ")),c.length>0&&(f&&(f+="\n"),f+="Unknown scripts: "+c.sort().join(", ")),t.confirmed.length>0&&(f&&(f+="\n"),f+="New browser cookies: "+t.confirmed.join(", ")),e.title=f}}}()}function b(e){for(var t=0;t<d.length;t++)if(e===d[t]||0===e.indexOf(d[t]))return!0;return!1}function _(){for(var e=document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean),t=[],o=[],n={},i=Object.keys(a).length>0,r=0;r<e.length;r++){var c=e[r];n[c]||(n[c]=!0,b(c)||(i&&a[c]?o.push(c):t.push(c)))}return{confirmed:t.sort(),existing:o.sort()}}function C(){var e=document.getElementById("cc-overlay");e&&e.classList.remove("cc-show");var t=document.getElementById("cc-banner");t&&t.classList.remove("cc-show");var o=document.getElementById("cc-ccpa-banner");o&&o.classList.remove("cc-show");var n=document.getElementById("cc-icon");n&&n.classList.remove("cc-show")}}}();
     15!function(){"use strict";var e=window.__ccInternal;if(e){var o=e.scriptLoaded,t=e.scriptLoadedUrls,n=e.hasCookie,i={"Google Analytics":function(){return o("googletagmanager.com/gtag")||o("google-analytics.com")||o("analytics.google.com")||n("_ga")},"Google Ads":function(){return o("googleads.g.doubleclick.net")||o("googleadservices.com")||o("googlesyndication.com")||n("_gcl_au")||n("_gcl_aw")},"Google Tag Manager":function(){return o("googletagmanager.com/gtm.js")||!!window.google_tag_manager},"Microsoft Ads":function(){return o("bat.bing.com")||!!window.uetq||n("_uetsid")||n("_uetvid")},"Meta / Facebook":function(){return o("connect.facebook.net")||!!window.fbq||n("_fbp")||n("_fbc")},"Twitter / X":function(){return o("ads-twitter.com")||o("static.ads-twitter.com")||!!window.twq||n("_twpid")},LinkedIn:function(){return o("snap.licdn.com")||o("ads.linkedin.com")||!!window._linkedin_data_partner_ids||n("li_sugr")||n("bcookie")||n("lidc")},Pinterest:function(){return o("s.pinimg.com/ct")||o("pintrk")||!!window.pintrk||n("_pin_unauth")},Reddit:function(){return o("alb.reddit.com")||o("events.reddit.com")||!!window.rdt||n("_rdt_uuid")},TikTok:function(){return o("analytics.tiktok.com")||!!window.ttq}};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",a):setTimeout(a,100)}function a(){var o=window.__ccDiscovery;if(o&&o.active&&(!document.body||!document.body.classList.contains("wp-admin"))){var n=function(){try{var e=sessionStorage.getItem("cookieflame_discovery_services");return e?JSON.parse(e):{}}catch(e){return{}}}(),a={};try{var r=localStorage.getItem("cookieflame_discovery_before");if(r)for(var c=JSON.parse(r),s=0;s<c.length;s++)a[c[s]]=!0}catch(e){}var d=["wordpress_","wp-settings","wp_lang","PHPSESSID","cc_consent","cc_anon_id","cookieflame_","woocommerce_","wp_woocommerce_"],l=document.createElement("div");l.id="cf-admin-discovery-bar",l.style.cssText="position:fixed;top:0;left:0;right:0;z-index:999999;background:#1d2327;color:#f0f0f1;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:14px;display:flex;align-items:center;justify-content:space-between;padding:10px 20px;box-shadow:0 2px 8px rgba(0,0,0,.3);gap:16px;flex-wrap:wrap;";var m=document.createElement("div");m.style.cssText="display:flex;align-items:center;gap:12px;flex:1;min-width:200px;";var g=document.createElement("span");g.style.cssText="width:10px;height:10px;border-radius:50%;background:#00ba37;display:inline-block;flex-shrink:0;animation:cf-admin-pulse 2s infinite;";var p=document.createElement("span");p.textContent="Cookie Discovery Active",p.style.fontWeight="600";var f=document.createElement("span");f.id="cf-admin-discovery-count",f.style.cssText="background:#2271b1;color:#fff;padding:2px 10px;border-radius:12px;font-size:13px;font-weight:600;cursor:help;",f.textContent="scanning...",m.appendChild(g),m.appendChild(p),m.appendChild(f);var u=document.createElement("span");u.style.cssText="color:#a7aaad;font-size:13px;display:inline-flex;flex-direction:column;gap:2px;";var h=document.createElement("span");h.textContent="All cookies are temporarily accepted. Browse your pages, then click Finish.";var v=document.createElement("span");v.textContent="Tip: Browse by clicking links on the site. Typed URLs may skip discovery.",v.style.cssText="font-size:11px;opacity:0.75;",u.appendChild(h),u.appendChild(v),C(),setTimeout(C,500),setTimeout(C,2e3);var y=document.createElement("button");y.textContent="Finish Discovery",y.style.cssText="background:#00ba37;color:#fff;border:none;padding:8px 20px;border-radius:4px;font-size:14px;font-weight:600;cursor:pointer;white-space:nowrap;",y.onmouseover=function(){y.style.background="#009929"},y.onmouseout=function(){y.style.background="#00ba37"},l.appendChild(m),l.appendChild(u),l.appendChild(y);var k=document.createElement("style");k.textContent="@keyframes cf-admin-pulse{0%,100%{opacity:1}50%{opacity:.4}}#cf-admin-discovery-bar~*{margin-top:0!important;}body.admin-bar #cf-admin-discovery-bar{top:32px}@media screen and (max-width:782px){body.admin-bar #cf-admin-discovery-bar{top:46px}}",document.head.appendChild(k),document.body.insertBefore(l,document.body.firstChild),document.addEventListener("click",function(e){for(var o=e.target;o&&1===o.nodeType&&"A"!==o.tagName;)o=o.parentNode;if(o&&"A"===o.tagName){var t=o.getAttribute("target");if("_blank"!==t&&"_top"!==t&&"_parent"!==t){var n=o.getAttribute("rel")||"";if(!n||-1===n.split(/\s+/).indexOf("external")){var i=o.getAttribute("href");if(i){var a;try{a=new URL(i,location.href)}catch(e){return}"http:"!==a.protocol&&"https:"!==a.protocol||a.origin===location.origin&&(a.pathname===location.pathname&&a.search===location.search&&a.hash!==location.hash||a.searchParams.has("cookieflame")||(a.searchParams.set("cookieflame","discover"),o.setAttribute("href",a.toString())))}}}}},!0),setTimeout(x,5e3);var w=setInterval(x,3e3);y.addEventListener("click",function(){y.disabled=!0,y.textContent="Finishing...",y.style.opacity="0.7",clearInterval(w),x();var e=n,t=window.__ccConfig||{};try{localStorage.setItem("cookieflame_discovery_results",JSON.stringify(e))}catch(o){var i=encodeURIComponent(JSON.stringify(e)),a=t.cookieDomain?"; domain="+t.cookieDomain:"";document.cookie="cookieflame_discovery_results="+i+"; path=/; max-age=300"+a}fetch(o.finishUrl,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","X-WP-Nonce":o.nonce},body:"{}"}).then(function(e){if(403===e.status)throw new Error("Your session has expired. Please reload the page and try again.");return e.json()}).then(function(e){var i=[location.hostname,"."+location.hostname];t.cookieDomain&&i.push(t.cookieDomain);for(var a=0;a<i.length;a++)document.cookie="cc_consent=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i[a];if(document.cookie="cc_consent=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/",e.restore_dnt){var r=t.cookieDomain?"; domain="+t.cookieDomain:"";document.cookie="cookieflame_dnt=1; path=/; max-age=31536000"+r}try{sessionStorage.removeItem("cookieflame_discovery_services")}catch(e){}for(var c=_(),s=0,d={},m=Object.keys(n),g=0;g<m.length;g++){var p=n[m[g]];if(p.unknown)s++,d[m[g]]=!0;else{var f=p.cookies||[];s+=f.length;for(var u=0;u<f.length;u++)d[f[u]]=!0}}for(var h=c.confirmed.concat(c.existing),v=0;v<h.length;v++)d[h[v]]||s++;l.style.background="#00ba37",l.innerHTML="";var y=document.createElement("span");y.style.cssText="font-weight:600;flex:1;text-align:center;";var k=s+" cookie"+(1!==s?"s":"")+" discovered. Redirecting to Cookie Manager...";y.textContent=k,l.appendChild(y),setTimeout(function(){window.location.href=o.adminUrl},1500)}).catch(function(e){y.disabled=!1,y.textContent="Finish Discovery",y.style.opacity="1",l.style.background="#d63638",u.style.color="#fff",u.textContent="Error: "+e.message})})}function x(){for(var o=function(){var o=[],n=e.getResourceSnapshot();e.setResourceSnapshot(n);for(var a=window.cookieflameAdmin&&window.cookieflameAdmin.serviceDetectors||[],r=[],c=0;c<a.length;c++)for(var s=a[c].scriptDomains||[],d=0;d<s.length;d++)r.push(s[d]);for(var l=0;l<a.length;l++){var m=a[l],g=m.name||"",p=i[g];if(p)try{if(p()){var f=t(g.toLowerCase().replace(/\s+/g,""));if(0===f.length&&-1!==g.indexOf(" "))for(var u=g.toLowerCase().split(" "),h=0;h<u.length&&0===f.length;h++)u[h].length>3&&(f=t(u[h]));o.push({name:g,cookies:m.cookies||[],canonicalDomain:m.canonicalDomain||"",scriptUrls:f})}}catch(e){}}o.pagePlugins=n.pluginScripts;for(var v=["wp.com","wordpress.com","wordpress.org","s.w.org","gravatar.com","cdnjs.cloudflare.com","cdn.jsdelivr.net","unpkg.com","ajax.googleapis.com","fonts.googleapis.com","fonts.gstatic.com","code.jquery.com","stackpath.bootstrapcdn.com","maxcdn.bootstrapcdn.com","www.google.com/recaptcha","www.gstatic.com","apis.google.com","maps.googleapis.com","maps.google.com","challenges.cloudflare.com","static.cloudflareinsights.com","chromium.org","mozilla.org","lazyload","rocket-loader","elementor.com"],y=n.thirdPartyDomains,k=0;k<y.length;k++){for(var w=y[k],x=!1,b=0;b<v.length;b++)if(w===v[b]||-1!==w.indexOf("."+v[b])||-1!==v[b].indexOf(w)){x=!0;break}if(!x){for(var _=!1,C=0;C<r.length;C++)if(-1!==w.indexOf(r[C])||-1!==r[C].indexOf(w)){_=!0;break}_||o.push({name:w,cookies:[],unknown:!0})}}return e.setResourceSnapshot(null),o}(),a=0;a<o.length;a++)n[o[a].name]||(n[o[a].name]=o[a]);if(o.pagePlugins&&o.pagePlugins.length){for(var r=n.__pagePlugins||{},c=0;c<o.pagePlugins.length;c++)r[o.pagePlugins[c].slug]=o.pagePlugins[c].url;n.__pagePlugins=r}!function(e){try{sessionStorage.setItem("cookieflame_discovery_services",JSON.stringify(e))}catch(e){}}(n),function(){var e=document.getElementById("cf-admin-discovery-count");if(e){for(var o=_(),t=(o.confirmed.length,o.existing.length,0),i={},a=Object.keys(n),r=[],c=[],s=0;s<a.length;s++){var d=n[a[s]];if(d.unknown)t++,c.push(a[s]),i[a[s]]=!0;else{r.push(a[s]);var l=d.cookies||[];t+=l.length;for(var m=0;m<l.length;m++)i[l[m]]=!0}}for(var g=o.confirmed.concat(o.existing),p=0;p<g.length;p++)i[g[p]]||t++;if(0===t)e.textContent="no cookies yet",e.title="Browse your site to discover cookies";else{e.textContent=t+" cookie"+(1!==t?"s":"")+" discovered";var f="";r.length>0&&(f+="Services: "+r.sort().join(", ")),c.length>0&&(f&&(f+="\n"),f+="Unknown scripts: "+c.sort().join(", ")),o.confirmed.length>0&&(f&&(f+="\n"),f+="New browser cookies: "+o.confirmed.join(", ")),e.title=f}}}()}function b(e){for(var o=0;o<d.length;o++)if(e===d[o]||0===e.indexOf(d[o]))return!0;return!1}function _(){for(var o=document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean),t=[],n=[],i={},r=Object.keys(a).length>0,c=e.visitorCookies?e.visitorCookies(o):{},s=window.cookieflameAdmin&&window.cookieflameAdmin.ignoredCookieNames||[],d=0;d<o.length;d++){var l=o[d];i[l]||(i[l]=!0,b(l)||c[l]||-1!==s.indexOf(l)||(r&&a[l]?n.push(l):t.push(l)))}return{confirmed:t.sort(),existing:n.sort()}}function C(){var e=document.getElementById("cc-overlay");e&&e.classList.remove("cc-show");var o=document.getElementById("cc-banner");o&&o.classList.remove("cc-show");var t=document.getElementById("cc-ccpa-banner");t&&t.classList.remove("cc-show");var n=document.getElementById("cc-icon");n&&n.classList.remove("cc-show")}}}();
  • cookieflame/tags/5.3.0/assets/js/cookieconsent.js

    r3768250 r3769546  
    11/**
    2  * CookieFlame v5.2.0
     2 * CookieFlame v5.3.0
    33 * ─────────────────────────────────────────────────────────────
    44 * Self-hosted cookie consent manager with Google Consent Mode v2,
    … …  
    4040  } catch (e) {}
    4141
    42   var PLUGIN_VERSION = "5.2.0";
     42  var PLUGIN_VERSION = "5.3.0";
    4343
    4444  /* ═══════════════════════════════════════════
    … …  
    14001400    hasCookiePrefix:     hasCookiePrefix,
    14011401    setResourceSnapshot: function (s) { _resourceSnapshot = s; },
     1402    // Discovery bar (v5.3.0): which of these cookies came with the browser,
     1403    // by the same check passive monitoring uses, so its count matches the
     1404    // Cookie Manager.
     1405    visitorCookies: function (names) {
     1406      var domains = getResourceSnapshot().thirdPartyDomains, out = {};
     1407      for (var i = 0; i < names.length; i++) {
     1408        if (isVisitorCookie(names[i], domains, cookieSetter(names[i]))) out[names[i]] = true;
     1409      }
     1410      return out;
     1411    },
    14021412    // Facade module bridge (v4.41.0+). The facade module reads this to
    14031413    // access core state it needs for overlay rendering, and registers
    … …  
    18981908    try {
    18991909      var url = new URL(String(setters[name]).replace(/:\d+(:\d+)?$/, ""));
     1910      if (/^(chrome-extension|moz-extension|safari-web-extension|safari-extension):$/.test(url.protocol)) {
     1911        out.set_by = "browser-extension";
     1912        return out;
     1913      }
    19001914      var host = url.hostname;
    19011915      if (host === location.hostname || host === "www." + location.hostname || "www." + host === location.hostname) {
    … …  
    19081922    } catch (e) {}
    19091923    return out;
     1924  }
     1925
     1926  /**
     1927   * Whether a cookie came with the visitor's browser rather than this site:
     1928   * written by a browser extension, or another consent tool's cookie when
     1929   * that tool isn't on the page (rules from window.__ccVisitorCookieRules,
     1930   * limited to tools whose WordPress plugin isn't active). Skipping these
     1931   * keeps the page's beacon allowance for real cookies; the server makes
     1932   * the same check and records them on the ignore list.
     1933   */
     1934  function isVisitorCookie(name, domains, setter) {
     1935    if (setter.set_by === "browser-extension") return true;
     1936    var rules = window.__ccVisitorCookieRules;
     1937    if (!rules || !rules.tools) return false;
     1938    if (rules.allow && rules.allow.indexOf(name) !== -1) return false;
     1939    if (setter.set_by) return false; // A page script wrote it, so the tool runs here.
     1940    for (var t = 0; t < rules.tools.length; t++) {
     1941      var tool = rules.tools[t], hit = (tool.n || []).indexOf(name) !== -1;
     1942      for (var p = 0; !hit && p < (tool.p || []).length; p++) {
     1943        if (name.indexOf(tool.p[p]) === 0) hit = true;
     1944      }
     1945      if (!hit) continue;
     1946      for (var d = 0; d < domains.length; d++) {
     1947        for (var k = 0; k < (tool.d || []).length; k++) {
     1948          var host = String(domains[d]).toLowerCase(), dom = tool.d[k];
     1949          if (host === dom || host.slice(-dom.length - 1) === "." + dom) return false;
     1950        }
     1951      }
     1952      return true;
     1953    }
     1954    return false;
    19101955  }
    19111956
    … …  
    19882033
    19892034      var setter = cookieSetter(name);
     2035      // Visitors' browsers don't report these. During Cookie Discovery they
     2036      // are reported anyway, so the server records them on the Permanently
     2037      // Ignored list with the reason and the admin sees what was set aside.
     2038      if (isVisitorCookie(name, scriptDomains, setter) && !(window.__ccDiscovery && window.__ccDiscovery.active)) {
     2039        beaconed[name] = true;
     2040        continue;
     2041      }
    19902042
    19912043      var payload = JSON.stringify({
  • cookieflame/tags/5.3.0/assets/js/cookieconsent.min.js

    r3768250 r3769546  
    11/**
    2  * CookieFlame v5.2.0
     2 * CookieFlame v5.3.0
    33 * ─────────────────────────────────────────────────────────────
    44 * Self-hosted cookie consent manager with Google Consent Mode v2,
    … …  
    1717 *   • Timezone-based geo heuristic with server-side fallback
    1818 */
    19 !function(){"use strict";var e={};try{for(var t=document.cookie.split(";"),n=0;n<t.length;n++){var o=t[n].trim().split("=")[0];o&&(e[o]=!0)}}catch(e){}var c="5.2.0",r={cookieName:"cc_consent",cookieExpiry:365,privacyUrl:"/privacy-policy",ccpaUrl:"/privacy-policy#do-not-sell",geoApi:"",logEndpoint:"",reconsentDays:365,cspSafe:!1,tcfEnabled:!0,cmpId:1,cmpVersion:1,vendorListVersion:2,storageMethod:"cookie",cookieDomain:"",shareSubdomains:!1,theme:"light",showPoweredBy:!1,iconPosition:"left",consentVersion:"",autoBlockPatterns:[{pattern:"googletagmanager.com/gtag",category:"analytics"},{pattern:"google-analytics.com",category:"analytics"},{pattern:"analytics.google.com",category:"analytics"},{pattern:"hotjar.com",category:"analytics"},{pattern:"mixpanel.com",category:"analytics"},{pattern:"segment.com/analytics",category:"analytics"},{pattern:"segment.io",category:"analytics"},{pattern:"clarity.ms",category:"analytics"},{pattern:"plausible.io",category:"analytics"},{pattern:"js.hs-analytics.net",category:"analytics"},{pattern:"connect.facebook.net",category:"marketing"},{pattern:"snap.licdn.com",category:"marketing"},{pattern:"ads.linkedin.com",category:"marketing"},{pattern:"googleads.g.doubleclick.net",category:"marketing"},{pattern:"googlesyndication.com",category:"marketing"},{pattern:"googleadservices.com",category:"marketing"},{pattern:"ads-twitter.com",category:"marketing"},{pattern:"analytics.tiktok.com",category:"marketing"},{pattern:"pintrk",category:"marketing"},{pattern:"s.pinimg.com/ct",category:"marketing"},{pattern:"widget.intercom.io",category:"preferences"},{pattern:"js.intercomcdn.com",category:"preferences"}],regionRules:{gdpr:["AT","BE","BG","CY","CZ","DE","DK","EE","ES","FI","FR","GR","HR","HU","IE","IT","LT","LU","LV","MT","NL","PL","PT","RO","SE","SI","SK","IS","LI","NO","GB","GG","JE","IM","CH","BR","ZA"],ccpa:["US"]},text:{title:"We value your privacy",description:"We use cookies to improve your experience, analyse traffic and personalise content. You can choose which categories to allow.",acceptAllButton:"Accept All",declineAllButton:"Decline All",saveButton:"Save Preferences",showDetailsButton:"Manage preferences",hideDetailsButton:"Hide preferences",alwaysOnLabel:"(always on)",ccpaTitle:"Your Privacy Choices",ccpaDescription:"We use cookies and similar technologies. Under California law, you have the right to opt out of the sale or sharing of your personal information.",ccpaAcceptButton:"OK",ccpaDnsmpiLink:"Do Not Sell My Personal Information"},cookieTypes:[{id:"necessary",name:"Necessary",description:"Required for the site to function. Cannot be disabled.",required:!0,consentMode:["security_storage","functionality_storage"],tcfPurposes:[1],onAccept:function(){}},{id:"preferences",name:"Preferences",description:"Remember your settings and personalisation choices.",required:!1,defaultValue:!1,consentMode:["functionality_storage","personalization_storage"],tcfPurposes:[2,3],onAccept:function(){},onReject:function(){}},{id:"analytics",name:"Analytics",description:"Help us understand how visitors use the site.",required:!1,defaultValue:!1,consentMode:["analytics_storage"],tcfPurposes:[4,5,6],onAccept:function(){},onReject:function(){}},{id:"marketing",name:"Marketing",description:"Used to deliver relevant ads and track campaigns.",required:!1,defaultValue:!1,consentMode:["ad_storage","ad_user_data","ad_personalization"],tcfPurposes:[7,8,9,10],onAccept:function(){},onReject:function(){}}]};if(window.__ccConfig&&"object"==typeof window.__ccConfig){var a=window.__ccConfig;["cookieName","cookieExpiry","privacyUrl","ccpaUrl","geoApi","logEndpoint","reconsentDays","cspSafe","tcfEnabled","cmpId","cmpVersion","vendorListVersion","storageMethod","cookieDomain","shareSubdomains","theme","showPoweredBy","iconPosition","consentVersion"].forEach(function(e){void 0!==a[e]&&(r[e]=a[e])}),a.text&&"object"==typeof a.text&&Object.keys(a.text).forEach(function(e){r.text[e]=a.text[e]}),Array.isArray(a.cookieTypes)&&(r.cookieTypes=a.cookieTypes),Array.isArray(a.autoBlockPatterns)&&(r.autoBlockPatterns=a.autoBlockPatterns),a.regionRules&&(r.regionRules=a.regionRules)}window.__ccConsentVersion&&(r.consentVersion=window.__ccConsentVersion);var i=!!window.__ccGPC;window.__ccBlockRules&&Array.isArray(window.__ccBlockRules)&&window.__ccBlockRules.length&&(r.autoBlockPatterns=window.__ccBlockRules);var s={cc_consent:{vendor:"CookieFlame",category:"necessary",purpose:"Stores your consent preferences",expiry:"1 year"},cc_anon_id:{vendor:"CookieFlame",category:"necessary",purpose:"Anonymous ID for consent logging",expiry:"1 year"},cookieflame_dnt:{vendor:"CookieFlame",category:"necessary",purpose:"Do-not-track opt-out flag",expiry:"1 year"},PHPSESSID:{vendor:"PHP",category:"necessary",purpose:"Server-side session identifier",expiry:"Session"},wp_settings_:{vendor:"WordPress",category:"necessary",purpose:"Admin interface preferences",prefix:!0,expiry:"1 year"},"wp-settings-":{vendor:"WordPress",category:"necessary",purpose:"Admin interface preferences",prefix:!0,expiry:"1 year"},"wp-settings-time-":{vendor:"WordPress",category:"necessary",purpose:"Admin settings timestamp",prefix:!0,expiry:"1 year"},wordpress_logged_in_:{vendor:"WordPress",category:"necessary",purpose:"Login session",prefix:!0,expiry:"Session"},wordpress_sec_:{vendor:"WordPress",category:"necessary",purpose:"Secure login session",prefix:!0,expiry:"Session"},wordpress_test_cookie:{vendor:"WordPress",category:"necessary",purpose:"Checks cookie support",expiry:"Session"},wp_lang:{vendor:"WordPress",category:"necessary",purpose:"Language preference",expiry:"Session"},woocommerce_cart_hash:{vendor:"WooCommerce",category:"necessary",purpose:"Shopping cart contents",expiry:"Session"},woocommerce_items_in_cart:{vendor:"WooCommerce",category:"necessary",purpose:"Cart item count",expiry:"Session"},wp_woocommerce_session_:{vendor:"WooCommerce",category:"necessary",purpose:"Shopping session",prefix:!0,expiry:"2 days"},_ga:{vendor:"Google Analytics",category:"analytics",purpose:"Distinguishes visitors",expiry:"2 years"},_ga_:{vendor:"Google Analytics",category:"analytics",purpose:"Stores session state",prefix:!0,expiry:"2 years"},_gid:{vendor:"Google Analytics",category:"analytics",purpose:"Distinguishes visitors (24h)",expiry:"24 hours"},_gat:{vendor:"Google Analytics",category:"analytics",purpose:"Throttles request rate",expiry:"1 minute"},wp_wk_ga_untrack_:{vendor:"Google Analytics",category:"analytics",purpose:"GA opt-out flag",prefix:!0,expiry:"Persistent"},_gcl_au:{vendor:"Google Ads",category:"marketing",purpose:"Conversion linker",expiry:"90 days"},_gcl_aw:{vendor:"Google Ads",category:"marketing",purpose:"Stores ad click info",expiry:"90 days"},_gac_:{vendor:"Google Ads",category:"marketing",purpose:"Campaign information",prefix:!0,expiry:"90 days"},IDE:{vendor:"Google DoubleClick",category:"marketing",purpose:"Ad serving and frequency",expiry:"1 year"},test_cookie:{vendor:"Google DoubleClick",category:"marketing",purpose:"Checks cookie support",expiry:"15 minutes"},NID:{vendor:"Google",category:"preferences",purpose:"Stores preferences",expiry:"6 months"}},d=window.__ccCustomCookies||[];function u(e){for(var t=0;t<d.length;t++){var n=d[t];if("exact"===n.match&&e===n.name)return n;if("prefix"===n.match&&0===e.indexOf(n.name))return n;if("contains"===n.match&&-1!==e.indexOf(n.name))return n}var o=s[e];return o||(Object.keys(s).forEach(function(t){!o&&s[t].prefix&&0===e.indexOf(t)&&(o=s[t])}),o||null)}var l=null,p="none",f=null,m=null,g=!1,y=0;function v(e){return"string"!=typeof e&&(e=String(e)),e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function h(e){if("localStorage"===r.storageMethod)try{return localStorage.getItem(e)}catch(e){return null}var t=document.cookie.match(new RegExp("(?:^|;\\s*)"+e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"=([^;]*)"));if(!t)return null;try{return decodeURIComponent(t[1])}catch(e){return null}}function w(e,t,n){if("localStorage"!==r.storageMethod){var o=new Date;o.setTime(o.getTime()+864e5*n);var c=e+"="+encodeURIComponent(t)+";expires="+o.toUTCString()+";path=/;SameSite=Lax;Secure";r.cookieDomain&&(c+=";domain="+r.cookieDomain),document.cookie=c}else try{localStorage.setItem(e,t)}catch(e){}}function b(){if(m)return m;var e=h("cc_anon_id");return e?(m=e,e):(w("cc_anon_id",m="cc_"+function(){if("undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID)try{return crypto.randomUUID()}catch(e){}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}(),730),m)}function k(){var e={timestamp:l?l.ts:null,complianceMode:p,country:f,gpc:i,consentVersion:r.consentVersion};return r.cookieTypes.forEach(function(t){e[t.id]=l?!!l[t.id]:!!t.required}),e}function _(e,t){window.dispatchEvent(new CustomEvent(e,{detail:t||k()}))}function x(){window.dataLayer=window.dataLayer||[],window.dataLayer.push(arguments)}function E(e){var t={};r.cookieTypes.forEach(function(n){n.consentMode&&n.consentMode.forEach(function(o){t[o]=e[n.id]?"granted":"denied"})}),i&&(t.ad_storage="denied",t.ad_user_data="denied",t.ad_personalization="denied"),x("consent","update",t)}var A=new Map,S=!1;function L(e){var t={};return r.cookieTypes.forEach(function(n){n.tcfPurposes&&n.tcfPurposes.forEach(function(o){t[o]=!!e[n.id]})}),{tcString:"",tcfPolicyVersion:4,cmpId:r.cmpId,cmpVersion:r.cmpVersion,gdprApplies:"gdpr"===p,eventStatus:"useractioncomplete",cmpStatus:"loaded",listenerId:void 0,isServiceSpecific:!0,useNonStandardTexts:!1,publisherCC:"GB",purposeOneTreatment:!1,purpose:{consents:t,legitimateInterests:{}},vendor:{consents:{},legitimateInterests:{}},specialFeatureOptins:{},publisher:{consents:{},legitimateInterests:{},customPurpose:{consents:{},legitimateInterests:{}},restrictions:{}}}}function C(e,t,n,o){if("function"==typeof n)if("addEventListener"===e){var c=++y;if(A.set(c,n),l){var a=L(l);a.listenerId=c,n(a,!0)}}else"removeEventListener"===e?n({listenerId:o},A.delete(o)):"getTCData"===e?n(l?L(l):null,!!l):"ping"===e?n({gdprApplies:"gdpr"===p,cmpLoaded:S,cmpStatus:S?"loaded":"stub",displayStatus:"hidden",apiVersion:"2.2",cmpVersion:r.cmpVersion,cmpId:r.cmpId,gvlVersion:r.vendorListVersion,tcfPolicyVersion:4},!0):n(null,!1)}function B(e){A.forEach(function(t,n){var o=L(e);o.listenerId=n;try{t(o,!0)}catch(e){}})}function I(e){if(!e)return null;for(var t=e.toLowerCase(),n=0;n<r.autoBlockPatterns.length;n++){var o=r.autoBlockPatterns[n];if(o.pattern instanceof RegExp){if(o.pattern.test(t))return o.category}else if(-1!==t.indexOf(o.pattern.toLowerCase()))return o.category}return null}function T(e){document.querySelectorAll('script[type="text/plain"][data-cc]').forEach(function(t){var n=(t.getAttribute("data-cc")||"").toLowerCase();if(e[n]&&!t.getAttribute("data-cc-activated")){t.setAttribute("data-cc-activated","1");var o=document.createElement("script");o.isWPRocket=!0,Array.from(t.attributes).forEach(function(e){"type"!==e.name&&"data-cc"!==e.name&&"data-cc-activated"!==e.name&&"data-cc-auto"!==e.name&&o.setAttribute(e.name,e.value)}),!t.src&&t.textContent&&(o.textContent=t.textContent),t.parentNode.insertBefore(o,t.nextSibling),t.parentNode.removeChild(t)}}),["img","iframe","link"].forEach(function(t){document.querySelectorAll(t+"[data-cc][data-cc-src]").forEach(function(n){var o=(n.getAttribute("data-cc")||"").toLowerCase();e[o]&&!n.getAttribute("data-cc-activated")&&(n.setAttribute("data-cc-activated","1"),"link"===t?n.href=n.getAttribute("data-cc-src"):n.src=n.getAttribute("data-cc-src"))})})}var O=[{pattern:"youtube.com",category:"marketing"},{pattern:"youtube-nocookie.com",category:"marketing"},{pattern:"youtu.be",category:"marketing"},{pattern:"player.vimeo.com",category:"marketing"},{pattern:"vimeo.com",category:"marketing"},{pattern:"dailymotion.com",category:"marketing"},{pattern:"open.spotify.com",category:"preferences"},{pattern:"soundcloud.com",category:"preferences"},{pattern:"google.com/maps",category:"preferences"},{pattern:"maps.google.com",category:"preferences"},{pattern:"facebook.com/plugins",category:"marketing"},{pattern:"instagram.com",category:"marketing"},{pattern:"tiktok.com/embed",category:"marketing"},{pattern:"twitter.com",category:"marketing"},{pattern:"x.com",category:"marketing"}];function P(){return!(!document.body||!(document.body.classList.contains("elementor-editor-active")||document.body.classList.contains("fl-builder-edit")||document.body.classList.contains("et-fb-root-ancestor")))}var D=[],M=0,R=null,q=!1;function V(){if(R&&D.length){var e=D;D=[];try{R(e)}catch(e){}}}function N(){0!==D.length&&(2!==M?1!==M&&3!==M&&(M=1,function(){var e=window.__ccConfig&&window.__ccConfig.facadesUrl||null;if(!e)return void(M=3);var t=document.createElement("script");t.src=e,t.async=!0,t.onload=function(){M=2,V()},t.onerror=function(){M=3},(document.head||document.documentElement).appendChild(t)}()):V())}function U(e){var t=e.src||e.getAttribute("data-src")||"";if(t&&!e.getAttribute("data-cc-facade")&&!P()){var n=function(e){if(!e)return null;for(var t=e.toLowerCase(),n=0;n<O.length;n++)if(-1!==t.indexOf(O[n].pattern))return O[n].category;return null}(t);if(n){var o=l||$();o&&o[n]||(e.setAttribute("data-cc-facade","1"),e.setAttribute("data-cc-iframe-cat",n),e.setAttribute("data-cc-original-src",t),e.removeAttribute("src"),e.style.display="none",D.push({type:"iframe",node:e,category:n,src:t}),N())}}}function G(e){if(!e.getAttribute("data-cc-facade")&&!P()){var t=e.getAttribute("data-settings");if(t){var n;try{n=JSON.parse(t)}catch(e){return}var o=n.youtube_url||n.vimeo_url||"";if(o){var c=o.toLowerCase(),r=null;if((-1!==c.indexOf("youtube")||-1!==c.indexOf("youtu.be")||-1!==c.indexOf("vimeo")||-1!==c.indexOf("dailymotion"))&&(r="marketing"),r&&(!l||!l[r])){if(!l){var a=$();if(a&&a[r])return}e.setAttribute("data-cc-facade","1"),q=!0,D.push({type:"elementor",node:e,category:r,url:o}),N()}}}}}function j(){for(var e=document.querySelectorAll("iframe[src]"),t=0;t<e.length;t++)U(e[t]);for(var n=document.querySelectorAll(".elementor-widget-video"),o=0;o<n.length;o++)G(n[o]);for(var c=document.querySelectorAll(".wp-block-embed__wrapper iframe[src]"),r=0;r<c.length;r++)U(c[r])}function J(){new MutationObserver(function(e){e.forEach(function(e){e.addedNodes.forEach(function(e){if(1===e.nodeType){if("IFRAME"===e.tagName&&U(e),e.querySelectorAll){for(var t=e.querySelectorAll("iframe[src]"),n=0;n<t.length;n++)U(t[n]);for(var o=e.querySelectorAll(".elementor-widget-video"),c=0;c<o.length;c++)G(o[c])}e.classList&&e.classList.contains("elementor-widget-video")&&G(e)}})})}).observe(document.documentElement,{childList:!0,subtree:!0})}function W(){for(var e=document.querySelectorAll("iframe[src]"),t=0;t<e.length;t++){var n=e[t];if(!n.getAttribute("data-cc-facade")){var o=n.src||"";if(o)for(var c=0;c<O.length;c++)if(-1!==o.indexOf(O[c].pattern)){n.src=o;break}}}}function H(e,t){r.cookieTypes.forEach(function(n){var o=!!e[n.id];if((t?!!t[n.id]:null)!==o||null===t)if(o&&"function"==typeof n.onAccept)try{n.onAccept()}catch(e){console.warn("[CookieConsent] onAccept error for "+n.id,e)}else if(!o&&"function"==typeof n.onReject)try{n.onReject()}catch(e){console.warn("[CookieConsent] onReject error for "+n.id,e)}})}function F(e,t){var n=l;if(l=e,w(r.cookieName,JSON.stringify(e),r.cookieExpiry),E(e),q&&t)for(var o=document.querySelectorAll(".cc-iframe-facade[data-cc-elementor-widget]"),a=0;a<o.length;a++){var s=o[a].getAttribute("data-cc-iframe-cat");if(s&&e[s])return void window.location.reload()}z(e,n),T(e),function(e){for(var t=document.querySelectorAll(".cc-iframe-facade"),n=0;n<t.length;n++)if(!t[n].getAttribute("data-cc-elementor-widget")&&e[t[n].getAttribute("data-cc-iframe-cat")]){var o=t[n].nextElementSibling;if(o&&"IFRAME"===o.tagName&&o.getAttribute("data-cc-facade")){var c=o.getAttribute("data-cc-original-src");c&&(o.src=c,o.style.display="",o.removeAttribute("data-cc-facade"))}t[n].parentNode.removeChild(t[n])}}(e),t&&"auto_grant_non_eu"!==t&&"ccpa_auto_grant"!==t&&setTimeout(W,300),H(e,n),Z(e),r.tcfEnabled&&B(e),t&&function(e,t){if(r.logEndpoint){var n={};r.cookieTypes.forEach(function(e){n[e.id]=!!t[e.id]});var o={anonymousId:b(),timestamp:(new Date).toISOString(),action:e,consent:n,complianceMode:p,country:f,url:location.href,userAgent:navigator.userAgent,tcfEnabled:r.tcfEnabled,gpc:i,consentVersion:r.consentVersion,version:c};try{var a=JSON.stringify(o);navigator.sendBeacon?navigator.sendBeacon(r.logEndpoint,new Blob([a],{type:"application/json"})):fetch(r.logEndpoint,{method:"POST",body:a,headers:{"Content-Type":"application/json"},keepalive:!0})}catch(e){}}}(t,e)}function Z(e){r.cookieTypes.forEach(function(t){if(!t.required){var n=document.getElementById("cc-cb-"+t.id);n&&(n.checked=!!e[t.id])}});var t=document.getElementById("cc-cats"),n=document.getElementById("cc-details"),o=document.getElementById("cc-pref-buttons");t&&t.classList.remove("cc-open"),n&&(n.textContent=r.text.showDetailsButton),o&&o.classList.remove("cc-open")}function $(){var e=h(r.cookieName);if(!e)return null;try{var t=JSON.parse(e);return!t||"object"!=typeof t||Array.isArray(t)||(!t.ts||isNaN(new Date(t.ts).getTime())||(Date.now()-new Date(t.ts).getTime())/864e5>r.reconsentDays||r.consentVersion&&t.cv!==r.consentVersion)?null:t}catch(e){return null}}function Y(){var e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f};return r.cookieTypes.forEach(function(t){e[t.id]=!0}),e}function K(){var e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f};return r.cookieTypes.forEach(function(t){e[t.id]=!!t.required}),e}function z(e,t){if(t){var n=[];if(r.cookieTypes.forEach(function(o){o.required||t[o.id]&&!e[o.id]&&n.push(o.id)}),n.length)document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean).forEach(function(e){var t=u(e);if(t&&-1!==n.indexOf(t.category)){var o=location.hostname,c="."+o;document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/",document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT",document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+c,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+o,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; Secure",r.cookieDomain&&(document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+r.cookieDomain,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+r.cookieDomain+"; Secure");var a=o.split(".");if(a.length>2){var i="."+a.slice(1).join(".");document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i+"; Secure"}}})}}function Q(e){return(f=e?e.toUpperCase():null)?r.regionRules.gdpr&&-1!==r.regionRules.gdpr.indexOf(f)?"gdpr":r.regionRules.ccpa&&-1!==r.regionRules.ccpa.indexOf(f)?"ccpa":"none":"gdpr"}function X(e){if(window.__ccGeo)return e(Q(window.__ccGeo));r.geoApi?fetch(r.geoApi).then(function(e){return e.json()}).then(function(t){var n=t.country_code||t.country||"";n?e(Q(n)):ee(e)}).catch(function(){ee(e)}):ee(e)}function ee(e){var t="";try{t=Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch(e){}return 0===t.indexOf("Europe/")||"Atlantic/Reykjavik"===t?(f=null,e("gdpr")):0===t.indexOf("US/")||0===t.indexOf("America/")&&["New_York","Chicago","Denver","Los_Angeles","Phoenix","Anchorage","Boise","Detroit","Indianapolis","Louisville","Menominee","Nome","Adak","Juneau","Sitka","Yakutat","Metlakatla","Kentucky","Indiana","North_Dakota"].some(function(e){return-1!==t.indexOf(e)})||"Pacific/Honolulu"===t?(f="US",e("ccpa")):(f=null,void e("gdpr"))}function te(){"dark"===r.theme&&document.documentElement.classList.add("cc-dark"),"right"===r.iconPosition&&document.documentElement.classList.add("cc-icon-right")}function ne(){var e=r.text,t=document.createElement("div");t.id="cc-overlay",document.body.appendChild(t);var n=document.createElement("div");n.id="cc-banner",n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-label","Cookie consent"),n.setAttribute("tabindex","-1");var o=r.cookieTypes.map(function(t){var n=t.required?' <span class="cc-always-on">'+v(e.alwaysOnLabel)+"</span>":"";return'<div class="cc-cat"><div><div class="cc-cat-label">'+v(t.name)+n+'</div><div class="cc-cat-desc">'+v(t.description||"")+'</div></div><label class="cc-toggle"><input type="checkbox" id="cc-cb-'+v(t.id)+'" aria-label="'+v(t.name)+'"'+(t.required||t.defaultValue?" checked":"")+(t.required?" disabled":"")+'><span class="cc-slider"></span></label></div>'}).join("");n.innerHTML='<div class="cc-inner"><p class="cc-title">'+v(e.title)+'</p><p class="cc-text">'+v(e.description)+' <a href="'+v(r.privacyUrl)+'">Privacy Policy</a></p><div class="cc-buttons"><button class="cc-btn cc-btn-accept" id="cc-accept">'+v(e.acceptAllButton)+'</button><button class="cc-btn cc-btn-details" id="cc-details" aria-expanded="false">'+v(e.showDetailsButton)+'</button></div><div class="cc-categories" id="cc-cats">'+o+'</div><div class="cc-pref-buttons" id="cc-pref-buttons"><button class="cc-btn cc-btn-save" id="cc-save">'+v(e.saveButton)+'</button><button class="cc-btn cc-btn-decline" id="cc-decline">'+v(e.declineAllButton)+"</button></div></div>"+(r.showPoweredBy?'<div class="cc-powered"><a href="https://www.cookieflame.com" target="_blank" rel="noopener">Powered by CookieFlame</a></div>':""),document.body.appendChild(n);var c=document.createElement("button");c.id="cc-icon",c.setAttribute("aria-label","Cookie settings"),c.innerHTML='<svg width="24" height="24" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M19.1 1.16c-.06-.42-.38-.77-.8-.83A11.8 11.8 0 0 0 13.28 1.2L7.5 4.3A11.8 11.8 0 0 0 3.95 7.98L1.09 13.9A11.8 11.8 0 0 0 .38 18.96l1.13 6.47a11.8 11.8 0 0 0 3.53 4.99l4.71 4.56a11.8 11.8 0 0 0 4.59 2.24l6.48.91a11.8 11.8 0 0 0 5.52-.89l5.78-3.09a11.8 11.8 0 0 0 3.55-3.69l2.87-5.9a11.8 11.8 0 0 0 .71-5.08c-.07-.41-.42-.73-.83-.79a14.8 14.8 0 0 1-8.16-7.61c-.14-.59-.63-1.05-1.23-1.14A14.8 14.8 0 0 1 19.1 1.16ZM15.25 10.25a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm-5 15a2.5 2.5 0 1 1 5 0 2.5 2.5 0 0 1-5 0Zm17.5-5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Z" fill="currentColor"/></svg>',document.body.appendChild(c),document.getElementById("cc-accept").addEventListener("click",se),document.getElementById("cc-decline").addEventListener("click",de),document.getElementById("cc-save").addEventListener("click",ue),document.getElementById("cc-details").addEventListener("click",le),c.addEventListener("click",function(){re("gdpr")})}var oe=null;function ce(e){if("Tab"===e.key){var t=document.getElementById("cc-banner")||document.getElementById("cc-ccpa-banner");if(t&&t.classList.contains("cc-show")){for(var n=[],o=t.querySelectorAll('button, a[href], input:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])'),c=0;c<o.length;c++){for(var r=o[c],a=!1,i=r.parentElement;i&&i!==t;){if(i.style&&"none"===window.getComputedStyle(i).display){a=!0;break}i=i.parentElement}a||n.push(r)}if(0!==n.length){var s=n[0],d=n[n.length-1];e.shiftKey?document.activeElement===s&&(e.preventDefault(),d.focus()):document.activeElement===d&&(e.preventDefault(),s.focus())}}}}function re(e){"ccpa"!==e?(oe=document.activeElement,requestAnimationFrame(function(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");if(e&&e.classList.add("cc-show"),t){t.classList.add("cc-show");var o=t.querySelector(".cc-btn-accept");o&&o.focus(),document.addEventListener("keydown",ce)}n&&n.classList.remove("cc-show")})):requestAnimationFrame(function(){var e=document.getElementById("cc-ccpa-banner");e&&(e.classList.add("cc-show"),oe=document.activeElement,e.focus(),document.addEventListener("keydown",ce))})}function ae(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");if(e&&e.classList.remove("cc-show"),t&&t.classList.remove("cc-show"),n&&"gdpr"===p&&n.classList.add("cc-show"),document.removeEventListener("keydown",ce),oe&&"function"==typeof oe.focus)try{oe.focus()}catch(e){}oe=null}function ie(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");e&&e.classList.remove("cc-show"),t&&t.classList.remove("cc-show"),n&&n.classList.remove("cc-show"),document.removeEventListener("keydown",ce)}function se(){F(Y(),"accept_all"),ae(),_("cc:accept-all"),_("cc:update")}function de(){F(K(),"decline_all"),ae(),_("cc:decline-all"),_("cc:update")}function ue(){var e;F((e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f},r.cookieTypes.forEach(function(t){if(t.required)e[t.id]=!0;else{var n=document.getElementById("cc-cb-"+t.id);e[t.id]=!!n&&n.checked}}),e),"save_preferences"),ae(),_("cc:save"),_("cc:update")}function le(){var e=document.getElementById("cc-cats"),t=document.getElementById("cc-details"),n=document.getElementById("cc-pref-buttons"),o=e.classList.toggle("cc-open");t.textContent=o?r.text.hideDetailsButton:r.text.showDetailsButton,t.setAttribute("aria-expanded",o?"true":"false"),n&&(o?n.classList.add("cc-open"):n.classList.remove("cc-open"))}function pe(){var e=document.getElementById("cc-declaration");if(e){for(var t=k(),n={},o=document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean),c=0;c<o.length;c++){var a=u(o[c]);a&&(n[o[c]]={name:o[c],vendor:a.vendor,category:a.category,purpose:a.purpose,expiry:a.expiry||"",active:!0})}for(var i=0;i<d.length;i++){var s=d[i];s.name&&s.vendor&&s.category&&"unclassified"!==s.category&&!n[s.name]&&(n[s.name]={name:s.name,vendor:s.vendor,category:s.category,purpose:s.purpose||"",expiry:s.expiry||"",active:!1})}for(var l=0;l<o.length;l++)n[o[l]]||(n[o[l]]={name:o[l],vendor:"",category:"unclassified",purpose:"",expiry:"",active:!0});var p=["necessary","preferences","analytics","marketing"],f={};r.cookieTypes.forEach(function(e){f[e.id]=e.name});var m={};p.forEach(function(e){m[e]=[]}),Object.keys(n).forEach(function(e){var t=n[e],o=t.category;m[o]||(m[o]=[]),m[o].push(t)}),p.forEach(function(e){m[e]&&m[e].sort(function(e,t){return e.name.localeCompare(t.name)})});var g="<div>";if(t.timestamp){var y=new Date(t.timestamp);g+="<p>"+v("Your consent was last updated on "+y.toLocaleDateString(void 0,{year:"numeric",month:"long",day:"numeric"})+" at "+y.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"})+".")+"</p>"}else g+="<p>"+v("You have not yet made a cookie preference choice on this site.")+"</p>";for(var h=0;h<p.length;h++){var w=p[h],b=m[w];if(b&&b.length){var _,x=f[w]||w,E=r.cookieTypes.filter(function(e){return e.id===w})[0],A=E&&E.description||"";_=!!E&&E.required?"Always active":t[w]?"Active":"Inactive",g+="<div>",g+="<p><b>"+v(x)+"</b> ("+v(_)+")</p>",A&&(g+="<p>"+v(A)+"</p>"),g+="<table><thead><tr>",g+="<th>Name</th><th>Provider</th><th>Purpose</th><th>Expiry</th>",g+="</tr></thead><tbody>";for(var S=0;S<b.length;S++){var L=b[S];g+="<tr>",g+="<td><code>"+v(L.name)+"</code></td>",g+="<td>"+v(L.vendor||"Unknown")+"</td>",g+="<td>"+v(L.purpose||"Not specified")+"</td>",g+="<td>"+v(L.expiry||"—")+"</td>",g+="</tr>"}g+="</tbody></table></div>"}}g+="</div>",e.innerHTML=g}}window.__ccInternal={getResourceSnapshot:ye,scriptLoaded:function(e){for(var t=ge||ye(),n=0;n<t.allUrls.length;n++)if(-1!==t.allUrls[n].indexOf(e))return!0;return!1},scriptLoadedUrls:function(e){for(var t=ge||ye(),n=[],o=0;o<t.allUrls.length;o++)-1!==t.allUrls[o].indexOf(e)&&n.push(t.allUrls[o]);return n},hasCookie:function(e){return document.cookie.split(";").some(function(t){return t.trim().split("=")[0]===e})},hasCookiePrefix:function(e){return document.cookie.split(";").some(function(t){return 0===t.trim().split("=")[0].indexOf(e)})},setResourceSnapshot:function(e){ge=e},facadeBridge:{getConfig:function(){return r},getConsentState:function(){return l},loadConsent:$,saveConsent:function(e,t){F(e,t)},hideAllBanners:ie,registerRenderer:function(e){R=e,M=2,V()}}},window.CookieConsent={getState:k,getConfig:function(){return r},getComplianceMode:function(){return p},getCountry:function(){return f},show:function(){re("gdpr")},acceptAll:se,declineAll:de,getGPC:function(){return i},getConsentVersion:function(){return r.consentVersion},on:function(e,t){window.addEventListener(e,t)},off:function(e,t){window.removeEventListener(e,t)},VERSION:c},window.addEventListener("cc:ready",pe),window.addEventListener("cc:update",pe),function(){if(!window.__ccDefaultsSet){var e={wait_for_update:500};r.cookieTypes.forEach(function(t){t.consentMode&&t.consentMode.forEach(function(n){e[n]=t.required?"granted":"denied"})}),x("consent","default",e)}}(),function(){if(r.tcfEnabled){var e=window.__tcfapi&&window.__tcfapi.queue?window.__tcfapi.queue:[];window.__tcfapi=C,e.forEach(function(e){C.apply(null,e)}),S=!0,window===window.top&&window.addEventListener("message",function(e){var t;try{t="string"==typeof e.data?JSON.parse(e.data):e.data}catch(e){return}if(t&&t.__tcfapiCall){var n=t.__tcfapiCall;C(n.command,n.version,function(t,o){var c={__tcfapiReturn:{returnValue:t,success:o,callId:n.callId}};e.source&&e.source.postMessage("string"==typeof e.data?JSON.stringify(c):c,"*")},n.parameter)}})}}();var fe=$();function me(){var e=window.__ccDiscovery&&window.__ccDiscovery.active;if(!e&&(window.__ccDNT||document.cookie.split(";").some(function(e){return 0===e.trim().indexOf("cookieflame_dnt=")})))return E(l=K()),z(l,Y()),J(),j(),p="dnt",te(),ne(),ie(),Z(l),_("cc:ready"),void pe();if(e){E(l=Y()),T(l),J(),H(l,null),r.tcfEnabled&&B(l),window.dataLayer=window.dataLayer||[];for(var t=["consent_accepted_analytics","consent_accepted_marketing","consent_accepted_preferences","consent_accepted_necessary","consent_update","consent_given","cookie_consent_given","cookie_consent_update","consent_accepted"],n=0;n<t.length;n++)window.dataLayer.push({event:t[n]});return p="discovery",te(),ne(),ie(),_("cc:ready"),_("cc:update"),void pe()}var o=$();if(o)if(i&&(o.marketing=!1),l=o,E(o),T(o),J(),j(),H(o,null),r.tcfEnabled&&B(o),te(),ne(),ie(),Z(o),o.cc){if("gdpr"===(p=Q(o.cc))){var c=document.getElementById("cc-icon");c&&c.classList.add("cc-show")}_("cc:ready"),pe()}else X(function(e){if(p=e,"gdpr"===e){var t=document.getElementById("cc-icon");t&&t.classList.add("cc-show")}_("cc:ready"),pe()});else X(function(e){if(p=e,"none"===e)return J(),F(Y(),"auto_grant_non_eu"),_("cc:ready"),_("cc:update"),void pe();if("ccpa"===e){if(J(),i){var t=Y();t.marketing=!1,F(t,"ccpa_gpc_optout")}else F(Y(),"ccpa_auto_grant");return te(),n=r.text,(o=document.createElement("div")).id="cc-ccpa-banner",o.setAttribute("role","dialog"),o.setAttribute("aria-modal","true"),o.setAttribute("aria-label","Privacy notice"),o.setAttribute("tabindex","-1"),o.innerHTML='<div class="cc-ccpa-inner"><div class="cc-ccpa-text"><p>'+v(n.ccpaDescription)+'</p></div><div class="cc-ccpa-actions"><a href="'+v(r.ccpaUrl)+'" class="cc-ccpa-link">'+v(n.ccpaDnsmpiLink)+'</a><button class="cc-btn cc-btn-accept" id="cc-ccpa-ok">'+v(n.ccpaAcceptButton)+"</button></div></div>",document.body.appendChild(o),document.getElementById("cc-ccpa-ok").addEventListener("click",function(){F(Y(),"ccpa_acknowledge"),o.classList.remove("cc-show"),_("cc:update")}),re("ccpa"),_("cc:ready"),_("cc:update"),void pe()}var n,o;te(),J(),j(),ne(),re("gdpr"),_("cc:ready"),pe()})}fe&&(l=fe,E(fe)),function(){if(!g&&r.autoBlockPatterns&&r.autoBlockPatterns.length){g=!0;var e=document.createElement.bind(document),t=Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,"src");t&&t.set&&(document.createElement=function(){var n=e.apply(document,arguments);if("script"!==(arguments[0]||"").toLowerCase())return n;function o(e,t){if(t&&"string"==typeof t&&!e.getAttribute("data-cc")&&!e.getAttribute("data-cc-auto")&&-1===t.indexOf("cookieconsent")){var n=I(t);!n||l&&l[n]||(e.type="text/plain",e.setAttribute("data-cc",n),e.setAttribute("data-cc-auto","1"))}}Object.defineProperty(n,"src",{configurable:!0,enumerable:!0,get:function(){return t.get.call(this)},set:function(e){return o(this,e),t.set.call(this,e)}});var c=n.setAttribute.bind(n);return n.setAttribute=function(e,t){return"src"===e.toLowerCase()&&o(n,t),c(e,t)},n}),new MutationObserver(function(e){e.forEach(function(e){e.addedNodes.forEach(function(e){if(1===e.nodeType){var t=[];"SCRIPT"===e.tagName&&t.push(e),e.querySelectorAll&&e.querySelectorAll("script[src]").forEach(function(e){t.push(e)}),t.forEach(function(e){if(!(e.getAttribute("data-cc-auto")||e.getAttribute("data-cc")||e.src&&-1!==e.src.indexOf("cookieconsent"))){var t=I(e.src);t&&(l&&l[t]||(e.type="text/plain",e.setAttribute("data-cc",t),e.setAttribute("data-cc-auto","1")))}})}})})}).observe(document.documentElement,{childList:!0,subtree:!0})}}(),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",me):me();var ge=null;function ye(){for(var e=[],t=[],n=[],o={},c={},r=document.querySelectorAll("script[src]"),a=0;a<r.length;a++)r[a].src&&e.push(r[a].src);for(var i=document.querySelectorAll("iframe[src]"),s=0;s<i.length;s++)i[s].src&&e.push(i[s].src);if(window.performance&&performance.getEntriesByType)for(var d=performance.getEntriesByType("resource"),u=0;u<d.length;u++){var l=d[u].name||"";if(l){-1===e.indexOf(l)&&e.push(l);var p=d[u].initiatorType||"";if("script"===p||"iframe"===p||"xmlhttprequest"===p||"fetch"===p||-1!==l.indexOf(".js"))try{var f=new URL(l),m=f.hostname;if(m===location.hostname||m==="www."+location.hostname||"www."+m===location.hostname){var g=f.pathname.match(/\/wp-content\/plugins\/([^\/]+)\//);g&&!c[g[1]]&&(c[g[1]]=!0,n.push({slug:g[1],url:l}))}else o[m]||(o[m]=!0,t.push(m))}catch(e){}}}return{allUrls:e,thirdPartyDomains:t,pluginScripts:n}}var ve={};function he(e){var t={set_by:"",set_by_plugin:""},n=window.__ccCookieSetters;if(!n||!Object.prototype.hasOwnProperty.call(n,e)||!n[e])return t;try{var o=new URL(String(n[e]).replace(/:\d+(:\d+)?$/,"")),c=o.hostname;if(c===location.hostname||c==="www."+location.hostname||"www."+c===location.hostname){t.set_by="first-party";var r=o.pathname.match(/\/wp-content\/(?:plugins|themes)\/([^\/]+)\//);r&&(t.set_by_plugin=r[1].toLowerCase())}else t.set_by=c.toLowerCase()}catch(e){}return t}function we(){var t=window.__ccClassified||[],n=window.__ccUnclassified||[],o=window.__ccBeaconUrl;if(o){var c,r={};for(c=0;c<t.length;c++)r[t[c]]=!0;for(c=0;c<n.length;c++)r[n[c]]=!0;var a=Object.keys(ve);for(c=0;c<a.length;c++)r[a[c]]=!0;var i=Object.keys(s);for(c=0;c<i.length;c++)r[i[c]]=!0;var d={},u=[],l=document.cookie.split(";");for(c=0;c<l.length;c++){var p=l[c].indexOf("="),f=(p>-1?l[c].substring(0,p):l[c]).trim(),m=p>-1?l[c].substring(p+1).trim():"";f&&(u.push(f),d[f]=m)}var g=[];for(c=0;c<u.length;c++)if(!r[u[c]]){for(var y=!1,v=Object.keys(s),h=0;h<v.length;h++)if(s[v[h]].prefix&&0===u[c].indexOf(v[h])){y=!0;break}y||g.push(u[c])}if(0!==g.length){for(var w=ye(),b=w.thirdPartyDomains,k=[],_=0;_<w.pluginScripts.length;_++)k.push(w.pluginScripts[_].slug);for(var x=location.pathname,E=0;E<g.length;E++){var A=g[E],S=!!e[A]&&!r[A],L=be(d[A]||""),C=he(A),B=JSON.stringify({cookie_name:A,script_domains:b,plugin_scripts:k,page_path:x,pre_existing:S,value_shape:L,set_by:C.set_by,set_by_plugin:C.set_by_plugin});ve[A]=!0,navigator.sendBeacon&&navigator.sendBeacon(o,new Blob([B],{type:"application/json"}))}}}}function be(e){if(!e||0===e.length)return"empty";if("true"===e||"false"===e||"1"===e||"0"===e)return"boolean";if(/^\d+$/.test(e))return e.length>12?"long-numeric":"numeric";if(/^\d+\.\d+$/.test(e))return"decimal";if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return"uuid";if(/^[A-Za-z0-9+\/]{20,}={0,2}$/.test(e))return"base64";if(/^(GA\d+\.\d+\.\d+\.\d+|G-[A-Z0-9]+)/.test(e))return"ga-id";try{if("object"==typeof JSON.parse(decodeURIComponent(e)))return"json"}catch(e){}return/^[a-f0-9]{24,}$/i.test(e)?"hex-id":/^\d{10,13}$/.test(e)?"timestamp":e.length<=20?"short-string":e.length<=100?"medium-string":"long-string"}setTimeout(we,3e3),setTimeout(we,15e3),setTimeout(we,45e3),window.addEventListener("cc:update",function(){setTimeout(we,3e3)}),window.addEventListener("pagehide",we)}();
     19!function(){"use strict";var e={};try{for(var t=document.cookie.split(";"),n=0;n<t.length;n++){var o=t[n].trim().split("=")[0];o&&(e[o]=!0)}}catch(e){}var c="5.3.0",r={cookieName:"cc_consent",cookieExpiry:365,privacyUrl:"/privacy-policy",ccpaUrl:"/privacy-policy#do-not-sell",geoApi:"",logEndpoint:"",reconsentDays:365,cspSafe:!1,tcfEnabled:!0,cmpId:1,cmpVersion:1,vendorListVersion:2,storageMethod:"cookie",cookieDomain:"",shareSubdomains:!1,theme:"light",showPoweredBy:!1,iconPosition:"left",consentVersion:"",autoBlockPatterns:[{pattern:"googletagmanager.com/gtag",category:"analytics"},{pattern:"google-analytics.com",category:"analytics"},{pattern:"analytics.google.com",category:"analytics"},{pattern:"hotjar.com",category:"analytics"},{pattern:"mixpanel.com",category:"analytics"},{pattern:"segment.com/analytics",category:"analytics"},{pattern:"segment.io",category:"analytics"},{pattern:"clarity.ms",category:"analytics"},{pattern:"plausible.io",category:"analytics"},{pattern:"js.hs-analytics.net",category:"analytics"},{pattern:"connect.facebook.net",category:"marketing"},{pattern:"snap.licdn.com",category:"marketing"},{pattern:"ads.linkedin.com",category:"marketing"},{pattern:"googleads.g.doubleclick.net",category:"marketing"},{pattern:"googlesyndication.com",category:"marketing"},{pattern:"googleadservices.com",category:"marketing"},{pattern:"ads-twitter.com",category:"marketing"},{pattern:"analytics.tiktok.com",category:"marketing"},{pattern:"pintrk",category:"marketing"},{pattern:"s.pinimg.com/ct",category:"marketing"},{pattern:"widget.intercom.io",category:"preferences"},{pattern:"js.intercomcdn.com",category:"preferences"}],regionRules:{gdpr:["AT","BE","BG","CY","CZ","DE","DK","EE","ES","FI","FR","GR","HR","HU","IE","IT","LT","LU","LV","MT","NL","PL","PT","RO","SE","SI","SK","IS","LI","NO","GB","GG","JE","IM","CH","BR","ZA"],ccpa:["US"]},text:{title:"We value your privacy",description:"We use cookies to improve your experience, analyse traffic and personalise content. You can choose which categories to allow.",acceptAllButton:"Accept All",declineAllButton:"Decline All",saveButton:"Save Preferences",showDetailsButton:"Manage preferences",hideDetailsButton:"Hide preferences",alwaysOnLabel:"(always on)",ccpaTitle:"Your Privacy Choices",ccpaDescription:"We use cookies and similar technologies. Under California law, you have the right to opt out of the sale or sharing of your personal information.",ccpaAcceptButton:"OK",ccpaDnsmpiLink:"Do Not Sell My Personal Information"},cookieTypes:[{id:"necessary",name:"Necessary",description:"Required for the site to function. Cannot be disabled.",required:!0,consentMode:["security_storage","functionality_storage"],tcfPurposes:[1],onAccept:function(){}},{id:"preferences",name:"Preferences",description:"Remember your settings and personalisation choices.",required:!1,defaultValue:!1,consentMode:["functionality_storage","personalization_storage"],tcfPurposes:[2,3],onAccept:function(){},onReject:function(){}},{id:"analytics",name:"Analytics",description:"Help us understand how visitors use the site.",required:!1,defaultValue:!1,consentMode:["analytics_storage"],tcfPurposes:[4,5,6],onAccept:function(){},onReject:function(){}},{id:"marketing",name:"Marketing",description:"Used to deliver relevant ads and track campaigns.",required:!1,defaultValue:!1,consentMode:["ad_storage","ad_user_data","ad_personalization"],tcfPurposes:[7,8,9,10],onAccept:function(){},onReject:function(){}}]};if(window.__ccConfig&&"object"==typeof window.__ccConfig){var a=window.__ccConfig;["cookieName","cookieExpiry","privacyUrl","ccpaUrl","geoApi","logEndpoint","reconsentDays","cspSafe","tcfEnabled","cmpId","cmpVersion","vendorListVersion","storageMethod","cookieDomain","shareSubdomains","theme","showPoweredBy","iconPosition","consentVersion"].forEach(function(e){void 0!==a[e]&&(r[e]=a[e])}),a.text&&"object"==typeof a.text&&Object.keys(a.text).forEach(function(e){r.text[e]=a.text[e]}),Array.isArray(a.cookieTypes)&&(r.cookieTypes=a.cookieTypes),Array.isArray(a.autoBlockPatterns)&&(r.autoBlockPatterns=a.autoBlockPatterns),a.regionRules&&(r.regionRules=a.regionRules)}window.__ccConsentVersion&&(r.consentVersion=window.__ccConsentVersion);var i=!!window.__ccGPC;window.__ccBlockRules&&Array.isArray(window.__ccBlockRules)&&window.__ccBlockRules.length&&(r.autoBlockPatterns=window.__ccBlockRules);var s={cc_consent:{vendor:"CookieFlame",category:"necessary",purpose:"Stores your consent preferences",expiry:"1 year"},cc_anon_id:{vendor:"CookieFlame",category:"necessary",purpose:"Anonymous ID for consent logging",expiry:"1 year"},cookieflame_dnt:{vendor:"CookieFlame",category:"necessary",purpose:"Do-not-track opt-out flag",expiry:"1 year"},PHPSESSID:{vendor:"PHP",category:"necessary",purpose:"Server-side session identifier",expiry:"Session"},wp_settings_:{vendor:"WordPress",category:"necessary",purpose:"Admin interface preferences",prefix:!0,expiry:"1 year"},"wp-settings-":{vendor:"WordPress",category:"necessary",purpose:"Admin interface preferences",prefix:!0,expiry:"1 year"},"wp-settings-time-":{vendor:"WordPress",category:"necessary",purpose:"Admin settings timestamp",prefix:!0,expiry:"1 year"},wordpress_logged_in_:{vendor:"WordPress",category:"necessary",purpose:"Login session",prefix:!0,expiry:"Session"},wordpress_sec_:{vendor:"WordPress",category:"necessary",purpose:"Secure login session",prefix:!0,expiry:"Session"},wordpress_test_cookie:{vendor:"WordPress",category:"necessary",purpose:"Checks cookie support",expiry:"Session"},wp_lang:{vendor:"WordPress",category:"necessary",purpose:"Language preference",expiry:"Session"},woocommerce_cart_hash:{vendor:"WooCommerce",category:"necessary",purpose:"Shopping cart contents",expiry:"Session"},woocommerce_items_in_cart:{vendor:"WooCommerce",category:"necessary",purpose:"Cart item count",expiry:"Session"},wp_woocommerce_session_:{vendor:"WooCommerce",category:"necessary",purpose:"Shopping session",prefix:!0,expiry:"2 days"},_ga:{vendor:"Google Analytics",category:"analytics",purpose:"Distinguishes visitors",expiry:"2 years"},_ga_:{vendor:"Google Analytics",category:"analytics",purpose:"Stores session state",prefix:!0,expiry:"2 years"},_gid:{vendor:"Google Analytics",category:"analytics",purpose:"Distinguishes visitors (24h)",expiry:"24 hours"},_gat:{vendor:"Google Analytics",category:"analytics",purpose:"Throttles request rate",expiry:"1 minute"},wp_wk_ga_untrack_:{vendor:"Google Analytics",category:"analytics",purpose:"GA opt-out flag",prefix:!0,expiry:"Persistent"},_gcl_au:{vendor:"Google Ads",category:"marketing",purpose:"Conversion linker",expiry:"90 days"},_gcl_aw:{vendor:"Google Ads",category:"marketing",purpose:"Stores ad click info",expiry:"90 days"},_gac_:{vendor:"Google Ads",category:"marketing",purpose:"Campaign information",prefix:!0,expiry:"90 days"},IDE:{vendor:"Google DoubleClick",category:"marketing",purpose:"Ad serving and frequency",expiry:"1 year"},test_cookie:{vendor:"Google DoubleClick",category:"marketing",purpose:"Checks cookie support",expiry:"15 minutes"},NID:{vendor:"Google",category:"preferences",purpose:"Stores preferences",expiry:"6 months"}},d=window.__ccCustomCookies||[];function u(e){for(var t=0;t<d.length;t++){var n=d[t];if("exact"===n.match&&e===n.name)return n;if("prefix"===n.match&&0===e.indexOf(n.name))return n;if("contains"===n.match&&-1!==e.indexOf(n.name))return n}var o=s[e];return o||(Object.keys(s).forEach(function(t){!o&&s[t].prefix&&0===e.indexOf(t)&&(o=s[t])}),o||null)}var l=null,p="none",f=null,m=null,g=!1,y=0;function v(e){return"string"!=typeof e&&(e=String(e)),e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function h(e){if("localStorage"===r.storageMethod)try{return localStorage.getItem(e)}catch(e){return null}var t=document.cookie.match(new RegExp("(?:^|;\\s*)"+e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"=([^;]*)"));if(!t)return null;try{return decodeURIComponent(t[1])}catch(e){return null}}function w(e,t,n){if("localStorage"!==r.storageMethod){var o=new Date;o.setTime(o.getTime()+864e5*n);var c=e+"="+encodeURIComponent(t)+";expires="+o.toUTCString()+";path=/;SameSite=Lax;Secure";r.cookieDomain&&(c+=";domain="+r.cookieDomain),document.cookie=c}else try{localStorage.setItem(e,t)}catch(e){}}function b(){if(m)return m;var e=h("cc_anon_id");return e?(m=e,e):(w("cc_anon_id",m="cc_"+function(){if("undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID)try{return crypto.randomUUID()}catch(e){}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}(),730),m)}function k(){var e={timestamp:l?l.ts:null,complianceMode:p,country:f,gpc:i,consentVersion:r.consentVersion};return r.cookieTypes.forEach(function(t){e[t.id]=l?!!l[t.id]:!!t.required}),e}function _(e,t){window.dispatchEvent(new CustomEvent(e,{detail:t||k()}))}function x(){window.dataLayer=window.dataLayer||[],window.dataLayer.push(arguments)}function E(e){var t={};r.cookieTypes.forEach(function(n){n.consentMode&&n.consentMode.forEach(function(o){t[o]=e[n.id]?"granted":"denied"})}),i&&(t.ad_storage="denied",t.ad_user_data="denied",t.ad_personalization="denied"),x("consent","update",t)}var A=new Map,S=!1;function L(e){var t={};return r.cookieTypes.forEach(function(n){n.tcfPurposes&&n.tcfPurposes.forEach(function(o){t[o]=!!e[n.id]})}),{tcString:"",tcfPolicyVersion:4,cmpId:r.cmpId,cmpVersion:r.cmpVersion,gdprApplies:"gdpr"===p,eventStatus:"useractioncomplete",cmpStatus:"loaded",listenerId:void 0,isServiceSpecific:!0,useNonStandardTexts:!1,publisherCC:"GB",purposeOneTreatment:!1,purpose:{consents:t,legitimateInterests:{}},vendor:{consents:{},legitimateInterests:{}},specialFeatureOptins:{},publisher:{consents:{},legitimateInterests:{},customPurpose:{consents:{},legitimateInterests:{}},restrictions:{}}}}function C(e,t,n,o){if("function"==typeof n)if("addEventListener"===e){var c=++y;if(A.set(c,n),l){var a=L(l);a.listenerId=c,n(a,!0)}}else"removeEventListener"===e?n({listenerId:o},A.delete(o)):"getTCData"===e?n(l?L(l):null,!!l):"ping"===e?n({gdprApplies:"gdpr"===p,cmpLoaded:S,cmpStatus:S?"loaded":"stub",displayStatus:"hidden",apiVersion:"2.2",cmpVersion:r.cmpVersion,cmpId:r.cmpId,gvlVersion:r.vendorListVersion,tcfPolicyVersion:4},!0):n(null,!1)}function B(e){A.forEach(function(t,n){var o=L(e);o.listenerId=n;try{t(o,!0)}catch(e){}})}function I(e){if(!e)return null;for(var t=e.toLowerCase(),n=0;n<r.autoBlockPatterns.length;n++){var o=r.autoBlockPatterns[n];if(o.pattern instanceof RegExp){if(o.pattern.test(t))return o.category}else if(-1!==t.indexOf(o.pattern.toLowerCase()))return o.category}return null}function T(e){document.querySelectorAll('script[type="text/plain"][data-cc]').forEach(function(t){var n=(t.getAttribute("data-cc")||"").toLowerCase();if(e[n]&&!t.getAttribute("data-cc-activated")){t.setAttribute("data-cc-activated","1");var o=document.createElement("script");o.isWPRocket=!0,Array.from(t.attributes).forEach(function(e){"type"!==e.name&&"data-cc"!==e.name&&"data-cc-activated"!==e.name&&"data-cc-auto"!==e.name&&o.setAttribute(e.name,e.value)}),!t.src&&t.textContent&&(o.textContent=t.textContent),t.parentNode.insertBefore(o,t.nextSibling),t.parentNode.removeChild(t)}}),["img","iframe","link"].forEach(function(t){document.querySelectorAll(t+"[data-cc][data-cc-src]").forEach(function(n){var o=(n.getAttribute("data-cc")||"").toLowerCase();e[o]&&!n.getAttribute("data-cc-activated")&&(n.setAttribute("data-cc-activated","1"),"link"===t?n.href=n.getAttribute("data-cc-src"):n.src=n.getAttribute("data-cc-src"))})})}var O=[{pattern:"youtube.com",category:"marketing"},{pattern:"youtube-nocookie.com",category:"marketing"},{pattern:"youtu.be",category:"marketing"},{pattern:"player.vimeo.com",category:"marketing"},{pattern:"vimeo.com",category:"marketing"},{pattern:"dailymotion.com",category:"marketing"},{pattern:"open.spotify.com",category:"preferences"},{pattern:"soundcloud.com",category:"preferences"},{pattern:"google.com/maps",category:"preferences"},{pattern:"maps.google.com",category:"preferences"},{pattern:"facebook.com/plugins",category:"marketing"},{pattern:"instagram.com",category:"marketing"},{pattern:"tiktok.com/embed",category:"marketing"},{pattern:"twitter.com",category:"marketing"},{pattern:"x.com",category:"marketing"}];function D(){return!(!document.body||!(document.body.classList.contains("elementor-editor-active")||document.body.classList.contains("fl-builder-edit")||document.body.classList.contains("et-fb-root-ancestor")))}var P=[],M=0,R=null,q=!1;function V(){if(R&&P.length){var e=P;P=[];try{R(e)}catch(e){}}}function N(){0!==P.length&&(2!==M?1!==M&&3!==M&&(M=1,function(){var e=window.__ccConfig&&window.__ccConfig.facadesUrl||null;if(!e)return void(M=3);var t=document.createElement("script");t.src=e,t.async=!0,t.onload=function(){M=2,V()},t.onerror=function(){M=3},(document.head||document.documentElement).appendChild(t)}()):V())}function U(e){var t=e.src||e.getAttribute("data-src")||"";if(t&&!e.getAttribute("data-cc-facade")&&!D()){var n=function(e){if(!e)return null;for(var t=e.toLowerCase(),n=0;n<O.length;n++)if(-1!==t.indexOf(O[n].pattern))return O[n].category;return null}(t);if(n){var o=l||Z();o&&o[n]||(e.setAttribute("data-cc-facade","1"),e.setAttribute("data-cc-iframe-cat",n),e.setAttribute("data-cc-original-src",t),e.removeAttribute("src"),e.style.display="none",P.push({type:"iframe",node:e,category:n,src:t}),N())}}}function G(e){if(!e.getAttribute("data-cc-facade")&&!D()){var t=e.getAttribute("data-settings");if(t){var n;try{n=JSON.parse(t)}catch(e){return}var o=n.youtube_url||n.vimeo_url||"";if(o){var c=o.toLowerCase(),r=null;if((-1!==c.indexOf("youtube")||-1!==c.indexOf("youtu.be")||-1!==c.indexOf("vimeo")||-1!==c.indexOf("dailymotion"))&&(r="marketing"),r&&(!l||!l[r])){if(!l){var a=Z();if(a&&a[r])return}e.setAttribute("data-cc-facade","1"),q=!0,P.push({type:"elementor",node:e,category:r,url:o}),N()}}}}}function j(){for(var e=document.querySelectorAll("iframe[src]"),t=0;t<e.length;t++)U(e[t]);for(var n=document.querySelectorAll(".elementor-widget-video"),o=0;o<n.length;o++)G(n[o]);for(var c=document.querySelectorAll(".wp-block-embed__wrapper iframe[src]"),r=0;r<c.length;r++)U(c[r])}function J(){new MutationObserver(function(e){e.forEach(function(e){e.addedNodes.forEach(function(e){if(1===e.nodeType){if("IFRAME"===e.tagName&&U(e),e.querySelectorAll){for(var t=e.querySelectorAll("iframe[src]"),n=0;n<t.length;n++)U(t[n]);for(var o=e.querySelectorAll(".elementor-widget-video"),c=0;c<o.length;c++)G(o[c])}e.classList&&e.classList.contains("elementor-widget-video")&&G(e)}})})}).observe(document.documentElement,{childList:!0,subtree:!0})}function W(){for(var e=document.querySelectorAll("iframe[src]"),t=0;t<e.length;t++){var n=e[t];if(!n.getAttribute("data-cc-facade")){var o=n.src||"";if(o)for(var c=0;c<O.length;c++)if(-1!==o.indexOf(O[c].pattern)){n.src=o;break}}}}function H(e,t){r.cookieTypes.forEach(function(n){var o=!!e[n.id];if((t?!!t[n.id]:null)!==o||null===t)if(o&&"function"==typeof n.onAccept)try{n.onAccept()}catch(e){console.warn("[CookieConsent] onAccept error for "+n.id,e)}else if(!o&&"function"==typeof n.onReject)try{n.onReject()}catch(e){console.warn("[CookieConsent] onReject error for "+n.id,e)}})}function F(e,t){var n=l;if(l=e,w(r.cookieName,JSON.stringify(e),r.cookieExpiry),E(e),q&&t)for(var o=document.querySelectorAll(".cc-iframe-facade[data-cc-elementor-widget]"),a=0;a<o.length;a++){var s=o[a].getAttribute("data-cc-iframe-cat");if(s&&e[s])return void window.location.reload()}K(e,n),T(e),function(e){for(var t=document.querySelectorAll(".cc-iframe-facade"),n=0;n<t.length;n++)if(!t[n].getAttribute("data-cc-elementor-widget")&&e[t[n].getAttribute("data-cc-iframe-cat")]){var o=t[n].nextElementSibling;if(o&&"IFRAME"===o.tagName&&o.getAttribute("data-cc-facade")){var c=o.getAttribute("data-cc-original-src");c&&(o.src=c,o.style.display="",o.removeAttribute("data-cc-facade"))}t[n].parentNode.removeChild(t[n])}}(e),t&&"auto_grant_non_eu"!==t&&"ccpa_auto_grant"!==t&&setTimeout(W,300),H(e,n),$(e),r.tcfEnabled&&B(e),t&&function(e,t){if(r.logEndpoint){var n={};r.cookieTypes.forEach(function(e){n[e.id]=!!t[e.id]});var o={anonymousId:b(),timestamp:(new Date).toISOString(),action:e,consent:n,complianceMode:p,country:f,url:location.href,userAgent:navigator.userAgent,tcfEnabled:r.tcfEnabled,gpc:i,consentVersion:r.consentVersion,version:c};try{var a=JSON.stringify(o);navigator.sendBeacon?navigator.sendBeacon(r.logEndpoint,new Blob([a],{type:"application/json"})):fetch(r.logEndpoint,{method:"POST",body:a,headers:{"Content-Type":"application/json"},keepalive:!0})}catch(e){}}}(t,e)}function $(e){r.cookieTypes.forEach(function(t){if(!t.required){var n=document.getElementById("cc-cb-"+t.id);n&&(n.checked=!!e[t.id])}});var t=document.getElementById("cc-cats"),n=document.getElementById("cc-details"),o=document.getElementById("cc-pref-buttons");t&&t.classList.remove("cc-open"),n&&(n.textContent=r.text.showDetailsButton),o&&o.classList.remove("cc-open")}function Z(){var e=h(r.cookieName);if(!e)return null;try{var t=JSON.parse(e);return!t||"object"!=typeof t||Array.isArray(t)||(!t.ts||isNaN(new Date(t.ts).getTime())||(Date.now()-new Date(t.ts).getTime())/864e5>r.reconsentDays||r.consentVersion&&t.cv!==r.consentVersion)?null:t}catch(e){return null}}function Y(){var e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f};return r.cookieTypes.forEach(function(t){e[t.id]=!0}),e}function z(){var e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f};return r.cookieTypes.forEach(function(t){e[t.id]=!!t.required}),e}function K(e,t){if(t){var n=[];if(r.cookieTypes.forEach(function(o){o.required||t[o.id]&&!e[o.id]&&n.push(o.id)}),n.length)document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean).forEach(function(e){var t=u(e);if(t&&-1!==n.indexOf(t.category)){var o=location.hostname,c="."+o;document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/",document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT",document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+c,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+o,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; Secure",r.cookieDomain&&(document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+r.cookieDomain,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+r.cookieDomain+"; Secure");var a=o.split(".");if(a.length>2){var i="."+a.slice(1).join(".");document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i+"; Secure"}}})}}function Q(e){return(f=e?e.toUpperCase():null)?r.regionRules.gdpr&&-1!==r.regionRules.gdpr.indexOf(f)?"gdpr":r.regionRules.ccpa&&-1!==r.regionRules.ccpa.indexOf(f)?"ccpa":"none":"gdpr"}function X(e){if(window.__ccGeo)return e(Q(window.__ccGeo));r.geoApi?fetch(r.geoApi).then(function(e){return e.json()}).then(function(t){var n=t.country_code||t.country||"";n?e(Q(n)):ee(e)}).catch(function(){ee(e)}):ee(e)}function ee(e){var t="";try{t=Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch(e){}return 0===t.indexOf("Europe/")||"Atlantic/Reykjavik"===t?(f=null,e("gdpr")):0===t.indexOf("US/")||0===t.indexOf("America/")&&["New_York","Chicago","Denver","Los_Angeles","Phoenix","Anchorage","Boise","Detroit","Indianapolis","Louisville","Menominee","Nome","Adak","Juneau","Sitka","Yakutat","Metlakatla","Kentucky","Indiana","North_Dakota"].some(function(e){return-1!==t.indexOf(e)})||"Pacific/Honolulu"===t?(f="US",e("ccpa")):(f=null,void e("gdpr"))}function te(){"dark"===r.theme&&document.documentElement.classList.add("cc-dark"),"right"===r.iconPosition&&document.documentElement.classList.add("cc-icon-right")}function ne(){var e=r.text,t=document.createElement("div");t.id="cc-overlay",document.body.appendChild(t);var n=document.createElement("div");n.id="cc-banner",n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-label","Cookie consent"),n.setAttribute("tabindex","-1");var o=r.cookieTypes.map(function(t){var n=t.required?' <span class="cc-always-on">'+v(e.alwaysOnLabel)+"</span>":"";return'<div class="cc-cat"><div><div class="cc-cat-label">'+v(t.name)+n+'</div><div class="cc-cat-desc">'+v(t.description||"")+'</div></div><label class="cc-toggle"><input type="checkbox" id="cc-cb-'+v(t.id)+'" aria-label="'+v(t.name)+'"'+(t.required||t.defaultValue?" checked":"")+(t.required?" disabled":"")+'><span class="cc-slider"></span></label></div>'}).join("");n.innerHTML='<div class="cc-inner"><p class="cc-title">'+v(e.title)+'</p><p class="cc-text">'+v(e.description)+' <a href="'+v(r.privacyUrl)+'">Privacy Policy</a></p><div class="cc-buttons"><button class="cc-btn cc-btn-accept" id="cc-accept">'+v(e.acceptAllButton)+'</button><button class="cc-btn cc-btn-details" id="cc-details" aria-expanded="false">'+v(e.showDetailsButton)+'</button></div><div class="cc-categories" id="cc-cats">'+o+'</div><div class="cc-pref-buttons" id="cc-pref-buttons"><button class="cc-btn cc-btn-save" id="cc-save">'+v(e.saveButton)+'</button><button class="cc-btn cc-btn-decline" id="cc-decline">'+v(e.declineAllButton)+"</button></div></div>"+(r.showPoweredBy?'<div class="cc-powered"><a href="https://www.cookieflame.com" target="_blank" rel="noopener">Powered by CookieFlame</a></div>':""),document.body.appendChild(n);var c=document.createElement("button");c.id="cc-icon",c.setAttribute("aria-label","Cookie settings"),c.innerHTML='<svg width="24" height="24" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M19.1 1.16c-.06-.42-.38-.77-.8-.83A11.8 11.8 0 0 0 13.28 1.2L7.5 4.3A11.8 11.8 0 0 0 3.95 7.98L1.09 13.9A11.8 11.8 0 0 0 .38 18.96l1.13 6.47a11.8 11.8 0 0 0 3.53 4.99l4.71 4.56a11.8 11.8 0 0 0 4.59 2.24l6.48.91a11.8 11.8 0 0 0 5.52-.89l5.78-3.09a11.8 11.8 0 0 0 3.55-3.69l2.87-5.9a11.8 11.8 0 0 0 .71-5.08c-.07-.41-.42-.73-.83-.79a14.8 14.8 0 0 1-8.16-7.61c-.14-.59-.63-1.05-1.23-1.14A14.8 14.8 0 0 1 19.1 1.16ZM15.25 10.25a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm-5 15a2.5 2.5 0 1 1 5 0 2.5 2.5 0 0 1-5 0Zm17.5-5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Z" fill="currentColor"/></svg>',document.body.appendChild(c),document.getElementById("cc-accept").addEventListener("click",se),document.getElementById("cc-decline").addEventListener("click",de),document.getElementById("cc-save").addEventListener("click",ue),document.getElementById("cc-details").addEventListener("click",le),c.addEventListener("click",function(){re("gdpr")})}var oe=null;function ce(e){if("Tab"===e.key){var t=document.getElementById("cc-banner")||document.getElementById("cc-ccpa-banner");if(t&&t.classList.contains("cc-show")){for(var n=[],o=t.querySelectorAll('button, a[href], input:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])'),c=0;c<o.length;c++){for(var r=o[c],a=!1,i=r.parentElement;i&&i!==t;){if(i.style&&"none"===window.getComputedStyle(i).display){a=!0;break}i=i.parentElement}a||n.push(r)}if(0!==n.length){var s=n[0],d=n[n.length-1];e.shiftKey?document.activeElement===s&&(e.preventDefault(),d.focus()):document.activeElement===d&&(e.preventDefault(),s.focus())}}}}function re(e){"ccpa"!==e?(oe=document.activeElement,requestAnimationFrame(function(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");if(e&&e.classList.add("cc-show"),t){t.classList.add("cc-show");var o=t.querySelector(".cc-btn-accept");o&&o.focus(),document.addEventListener("keydown",ce)}n&&n.classList.remove("cc-show")})):requestAnimationFrame(function(){var e=document.getElementById("cc-ccpa-banner");e&&(e.classList.add("cc-show"),oe=document.activeElement,e.focus(),document.addEventListener("keydown",ce))})}function ae(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");if(e&&e.classList.remove("cc-show"),t&&t.classList.remove("cc-show"),n&&"gdpr"===p&&n.classList.add("cc-show"),document.removeEventListener("keydown",ce),oe&&"function"==typeof oe.focus)try{oe.focus()}catch(e){}oe=null}function ie(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");e&&e.classList.remove("cc-show"),t&&t.classList.remove("cc-show"),n&&n.classList.remove("cc-show"),document.removeEventListener("keydown",ce)}function se(){F(Y(),"accept_all"),ae(),_("cc:accept-all"),_("cc:update")}function de(){F(z(),"decline_all"),ae(),_("cc:decline-all"),_("cc:update")}function ue(){var e;F((e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f},r.cookieTypes.forEach(function(t){if(t.required)e[t.id]=!0;else{var n=document.getElementById("cc-cb-"+t.id);e[t.id]=!!n&&n.checked}}),e),"save_preferences"),ae(),_("cc:save"),_("cc:update")}function le(){var e=document.getElementById("cc-cats"),t=document.getElementById("cc-details"),n=document.getElementById("cc-pref-buttons"),o=e.classList.toggle("cc-open");t.textContent=o?r.text.hideDetailsButton:r.text.showDetailsButton,t.setAttribute("aria-expanded",o?"true":"false"),n&&(o?n.classList.add("cc-open"):n.classList.remove("cc-open"))}function pe(){var e=document.getElementById("cc-declaration");if(e){for(var t=k(),n={},o=document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean),c=0;c<o.length;c++){var a=u(o[c]);a&&(n[o[c]]={name:o[c],vendor:a.vendor,category:a.category,purpose:a.purpose,expiry:a.expiry||"",active:!0})}for(var i=0;i<d.length;i++){var s=d[i];s.name&&s.vendor&&s.category&&"unclassified"!==s.category&&!n[s.name]&&(n[s.name]={name:s.name,vendor:s.vendor,category:s.category,purpose:s.purpose||"",expiry:s.expiry||"",active:!1})}for(var l=0;l<o.length;l++)n[o[l]]||(n[o[l]]={name:o[l],vendor:"",category:"unclassified",purpose:"",expiry:"",active:!0});var p=["necessary","preferences","analytics","marketing"],f={};r.cookieTypes.forEach(function(e){f[e.id]=e.name});var m={};p.forEach(function(e){m[e]=[]}),Object.keys(n).forEach(function(e){var t=n[e],o=t.category;m[o]||(m[o]=[]),m[o].push(t)}),p.forEach(function(e){m[e]&&m[e].sort(function(e,t){return e.name.localeCompare(t.name)})});var g="<div>";if(t.timestamp){var y=new Date(t.timestamp);g+="<p>"+v("Your consent was last updated on "+y.toLocaleDateString(void 0,{year:"numeric",month:"long",day:"numeric"})+" at "+y.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"})+".")+"</p>"}else g+="<p>"+v("You have not yet made a cookie preference choice on this site.")+"</p>";for(var h=0;h<p.length;h++){var w=p[h],b=m[w];if(b&&b.length){var _,x=f[w]||w,E=r.cookieTypes.filter(function(e){return e.id===w})[0],A=E&&E.description||"";_=!!E&&E.required?"Always active":t[w]?"Active":"Inactive",g+="<div>",g+="<p><b>"+v(x)+"</b> ("+v(_)+")</p>",A&&(g+="<p>"+v(A)+"</p>"),g+="<table><thead><tr>",g+="<th>Name</th><th>Provider</th><th>Purpose</th><th>Expiry</th>",g+="</tr></thead><tbody>";for(var S=0;S<b.length;S++){var L=b[S];g+="<tr>",g+="<td><code>"+v(L.name)+"</code></td>",g+="<td>"+v(L.vendor||"Unknown")+"</td>",g+="<td>"+v(L.purpose||"Not specified")+"</td>",g+="<td>"+v(L.expiry||"—")+"</td>",g+="</tr>"}g+="</tbody></table></div>"}}g+="</div>",e.innerHTML=g}}window.__ccInternal={getResourceSnapshot:ye,scriptLoaded:function(e){for(var t=ge||ye(),n=0;n<t.allUrls.length;n++)if(-1!==t.allUrls[n].indexOf(e))return!0;return!1},scriptLoadedUrls:function(e){for(var t=ge||ye(),n=[],o=0;o<t.allUrls.length;o++)-1!==t.allUrls[o].indexOf(e)&&n.push(t.allUrls[o]);return n},hasCookie:function(e){return document.cookie.split(";").some(function(t){return t.trim().split("=")[0]===e})},hasCookiePrefix:function(e){return document.cookie.split(";").some(function(t){return 0===t.trim().split("=")[0].indexOf(e)})},setResourceSnapshot:function(e){ge=e},visitorCookies:function(e){for(var t=ye().thirdPartyDomains,n={},o=0;o<e.length;o++)we(e[o],t,he(e[o]))&&(n[e[o]]=!0);return n},facadeBridge:{getConfig:function(){return r},getConsentState:function(){return l},loadConsent:Z,saveConsent:function(e,t){F(e,t)},hideAllBanners:ie,registerRenderer:function(e){R=e,M=2,V()}}},window.CookieConsent={getState:k,getConfig:function(){return r},getComplianceMode:function(){return p},getCountry:function(){return f},show:function(){re("gdpr")},acceptAll:se,declineAll:de,getGPC:function(){return i},getConsentVersion:function(){return r.consentVersion},on:function(e,t){window.addEventListener(e,t)},off:function(e,t){window.removeEventListener(e,t)},VERSION:c},window.addEventListener("cc:ready",pe),window.addEventListener("cc:update",pe),function(){if(!window.__ccDefaultsSet){var e={wait_for_update:500};r.cookieTypes.forEach(function(t){t.consentMode&&t.consentMode.forEach(function(n){e[n]=t.required?"granted":"denied"})}),x("consent","default",e)}}(),function(){if(r.tcfEnabled){var e=window.__tcfapi&&window.__tcfapi.queue?window.__tcfapi.queue:[];window.__tcfapi=C,e.forEach(function(e){C.apply(null,e)}),S=!0,window===window.top&&window.addEventListener("message",function(e){var t;try{t="string"==typeof e.data?JSON.parse(e.data):e.data}catch(e){return}if(t&&t.__tcfapiCall){var n=t.__tcfapiCall;C(n.command,n.version,function(t,o){var c={__tcfapiReturn:{returnValue:t,success:o,callId:n.callId}};e.source&&e.source.postMessage("string"==typeof e.data?JSON.stringify(c):c,"*")},n.parameter)}})}}();var fe=Z();function me(){var e=window.__ccDiscovery&&window.__ccDiscovery.active;if(!e&&(window.__ccDNT||document.cookie.split(";").some(function(e){return 0===e.trim().indexOf("cookieflame_dnt=")})))return E(l=z()),K(l,Y()),J(),j(),p="dnt",te(),ne(),ie(),$(l),_("cc:ready"),void pe();if(e){E(l=Y()),T(l),J(),H(l,null),r.tcfEnabled&&B(l),window.dataLayer=window.dataLayer||[];for(var t=["consent_accepted_analytics","consent_accepted_marketing","consent_accepted_preferences","consent_accepted_necessary","consent_update","consent_given","cookie_consent_given","cookie_consent_update","consent_accepted"],n=0;n<t.length;n++)window.dataLayer.push({event:t[n]});return p="discovery",te(),ne(),ie(),_("cc:ready"),_("cc:update"),void pe()}var o=Z();if(o)if(i&&(o.marketing=!1),l=o,E(o),T(o),J(),j(),H(o,null),r.tcfEnabled&&B(o),te(),ne(),ie(),$(o),o.cc){if("gdpr"===(p=Q(o.cc))){var c=document.getElementById("cc-icon");c&&c.classList.add("cc-show")}_("cc:ready"),pe()}else X(function(e){if(p=e,"gdpr"===e){var t=document.getElementById("cc-icon");t&&t.classList.add("cc-show")}_("cc:ready"),pe()});else X(function(e){if(p=e,"none"===e)return J(),F(Y(),"auto_grant_non_eu"),_("cc:ready"),_("cc:update"),void pe();if("ccpa"===e){if(J(),i){var t=Y();t.marketing=!1,F(t,"ccpa_gpc_optout")}else F(Y(),"ccpa_auto_grant");return te(),n=r.text,(o=document.createElement("div")).id="cc-ccpa-banner",o.setAttribute("role","dialog"),o.setAttribute("aria-modal","true"),o.setAttribute("aria-label","Privacy notice"),o.setAttribute("tabindex","-1"),o.innerHTML='<div class="cc-ccpa-inner"><div class="cc-ccpa-text"><p>'+v(n.ccpaDescription)+'</p></div><div class="cc-ccpa-actions"><a href="'+v(r.ccpaUrl)+'" class="cc-ccpa-link">'+v(n.ccpaDnsmpiLink)+'</a><button class="cc-btn cc-btn-accept" id="cc-ccpa-ok">'+v(n.ccpaAcceptButton)+"</button></div></div>",document.body.appendChild(o),document.getElementById("cc-ccpa-ok").addEventListener("click",function(){F(Y(),"ccpa_acknowledge"),o.classList.remove("cc-show"),_("cc:update")}),re("ccpa"),_("cc:ready"),_("cc:update"),void pe()}var n,o;te(),J(),j(),ne(),re("gdpr"),_("cc:ready"),pe()})}fe&&(l=fe,E(fe)),function(){if(!g&&r.autoBlockPatterns&&r.autoBlockPatterns.length){g=!0;var e=document.createElement.bind(document),t=Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,"src");t&&t.set&&(document.createElement=function(){var n=e.apply(document,arguments);if("script"!==(arguments[0]||"").toLowerCase())return n;function o(e,t){if(t&&"string"==typeof t&&!e.getAttribute("data-cc")&&!e.getAttribute("data-cc-auto")&&-1===t.indexOf("cookieconsent")){var n=I(t);!n||l&&l[n]||(e.type="text/plain",e.setAttribute("data-cc",n),e.setAttribute("data-cc-auto","1"))}}Object.defineProperty(n,"src",{configurable:!0,enumerable:!0,get:function(){return t.get.call(this)},set:function(e){return o(this,e),t.set.call(this,e)}});var c=n.setAttribute.bind(n);return n.setAttribute=function(e,t){return"src"===e.toLowerCase()&&o(n,t),c(e,t)},n}),new MutationObserver(function(e){e.forEach(function(e){e.addedNodes.forEach(function(e){if(1===e.nodeType){var t=[];"SCRIPT"===e.tagName&&t.push(e),e.querySelectorAll&&e.querySelectorAll("script[src]").forEach(function(e){t.push(e)}),t.forEach(function(e){if(!(e.getAttribute("data-cc-auto")||e.getAttribute("data-cc")||e.src&&-1!==e.src.indexOf("cookieconsent"))){var t=I(e.src);t&&(l&&l[t]||(e.type="text/plain",e.setAttribute("data-cc",t),e.setAttribute("data-cc-auto","1")))}})}})})}).observe(document.documentElement,{childList:!0,subtree:!0})}}(),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",me):me();var ge=null;function ye(){for(var e=[],t=[],n=[],o={},c={},r=document.querySelectorAll("script[src]"),a=0;a<r.length;a++)r[a].src&&e.push(r[a].src);for(var i=document.querySelectorAll("iframe[src]"),s=0;s<i.length;s++)i[s].src&&e.push(i[s].src);if(window.performance&&performance.getEntriesByType)for(var d=performance.getEntriesByType("resource"),u=0;u<d.length;u++){var l=d[u].name||"";if(l){-1===e.indexOf(l)&&e.push(l);var p=d[u].initiatorType||"";if("script"===p||"iframe"===p||"xmlhttprequest"===p||"fetch"===p||-1!==l.indexOf(".js"))try{var f=new URL(l),m=f.hostname;if(m===location.hostname||m==="www."+location.hostname||"www."+m===location.hostname){var g=f.pathname.match(/\/wp-content\/plugins\/([^\/]+)\//);g&&!c[g[1]]&&(c[g[1]]=!0,n.push({slug:g[1],url:l}))}else o[m]||(o[m]=!0,t.push(m))}catch(e){}}}return{allUrls:e,thirdPartyDomains:t,pluginScripts:n}}var ve={};function he(e){var t={set_by:"",set_by_plugin:""},n=window.__ccCookieSetters;if(!n||!Object.prototype.hasOwnProperty.call(n,e)||!n[e])return t;try{var o=new URL(String(n[e]).replace(/:\d+(:\d+)?$/,""));if(/^(chrome-extension|moz-extension|safari-web-extension|safari-extension):$/.test(o.protocol))return t.set_by="browser-extension",t;var c=o.hostname;if(c===location.hostname||c==="www."+location.hostname||"www."+c===location.hostname){t.set_by="first-party";var r=o.pathname.match(/\/wp-content\/(?:plugins|themes)\/([^\/]+)\//);r&&(t.set_by_plugin=r[1].toLowerCase())}else t.set_by=c.toLowerCase()}catch(e){}return t}function we(e,t,n){if("browser-extension"===n.set_by)return!0;var o=window.__ccVisitorCookieRules;if(!o||!o.tools)return!1;if(o.allow&&-1!==o.allow.indexOf(e))return!1;if(n.set_by)return!1;for(var c=0;c<o.tools.length;c++){for(var r=o.tools[c],a=-1!==(r.n||[]).indexOf(e),i=0;!a&&i<(r.p||[]).length;i++)0===e.indexOf(r.p[i])&&(a=!0);if(a){for(var s=0;s<t.length;s++)for(var d=0;d<(r.d||[]).length;d++){var u=String(t[s]).toLowerCase(),l=r.d[d];if(u===l||u.slice(-l.length-1)==="."+l)return!1}return!0}}return!1}function be(){var t=window.__ccClassified||[],n=window.__ccUnclassified||[],o=window.__ccBeaconUrl;if(o){var c,r={};for(c=0;c<t.length;c++)r[t[c]]=!0;for(c=0;c<n.length;c++)r[n[c]]=!0;var a=Object.keys(ve);for(c=0;c<a.length;c++)r[a[c]]=!0;var i=Object.keys(s);for(c=0;c<i.length;c++)r[i[c]]=!0;var d={},u=[],l=document.cookie.split(";");for(c=0;c<l.length;c++){var p=l[c].indexOf("="),f=(p>-1?l[c].substring(0,p):l[c]).trim(),m=p>-1?l[c].substring(p+1).trim():"";f&&(u.push(f),d[f]=m)}var g=[];for(c=0;c<u.length;c++)if(!r[u[c]]){for(var y=!1,v=Object.keys(s),h=0;h<v.length;h++)if(s[v[h]].prefix&&0===u[c].indexOf(v[h])){y=!0;break}y||g.push(u[c])}if(0!==g.length){for(var w=ye(),b=w.thirdPartyDomains,k=[],_=0;_<w.pluginScripts.length;_++)k.push(w.pluginScripts[_].slug);for(var x=location.pathname,E=0;E<g.length;E++){var A=g[E],S=!!e[A]&&!r[A],L=ke(d[A]||""),C=he(A);if(!we(A,b,C)||window.__ccDiscovery&&window.__ccDiscovery.active){var B=JSON.stringify({cookie_name:A,script_domains:b,plugin_scripts:k,page_path:x,pre_existing:S,value_shape:L,set_by:C.set_by,set_by_plugin:C.set_by_plugin});ve[A]=!0,navigator.sendBeacon&&navigator.sendBeacon(o,new Blob([B],{type:"application/json"}))}else ve[A]=!0}}}}function ke(e){if(!e||0===e.length)return"empty";if("true"===e||"false"===e||"1"===e||"0"===e)return"boolean";if(/^\d+$/.test(e))return e.length>12?"long-numeric":"numeric";if(/^\d+\.\d+$/.test(e))return"decimal";if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return"uuid";if(/^[A-Za-z0-9+\/]{20,}={0,2}$/.test(e))return"base64";if(/^(GA\d+\.\d+\.\d+\.\d+|G-[A-Z0-9]+)/.test(e))return"ga-id";try{if("object"==typeof JSON.parse(decodeURIComponent(e)))return"json"}catch(e){}return/^[a-f0-9]{24,}$/i.test(e)?"hex-id":/^\d{10,13}$/.test(e)?"timestamp":e.length<=20?"short-string":e.length<=100?"medium-string":"long-string"}setTimeout(be,3e3),setTimeout(be,15e3),setTimeout(be,45e3),window.addEventListener("cc:update",function(){setTimeout(be,3e3)}),window.addEventListener("pagehide",be)}();
  • cookieflame/tags/5.3.0/cookieflame.php

    r3768250 r3769546  
    44 * Plugin URI:        https://cookieflame.com
    55 * Description:       A lightweight, self-hosted cookie consent manager with Google Consent Mode v2, IAB TCF v2.2, automatic script blocking, multi-region compliance (GDPR/CCPA/LGPD/POPIA), passive cookie monitoring, and server-side geo detection via Cloudflare.
    6  * Version:           5.2.0
     6 * Version:           5.3.0
    77 * Requires at least: 6.0
    88 * Requires PHP:      8.0
    … …  
    1919}
    2020
    21 define( 'COOKIEFLAME_VERSION', '5.2.0' );
     21define( 'COOKIEFLAME_VERSION', '5.3.0' );
    2222define( 'COOKIEFLAME_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    2323define( 'COOKIEFLAME_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    … …  
    219219// cookieconsent-discovery.js via the cookieflameAdmin global.
    220220require_once COOKIEFLAME_PLUGIN_DIR . 'includes/service-detectors.php';
     221require_once COOKIEFLAME_PLUGIN_DIR . 'includes/consent-tools.php';
    221222
    222223/* ═══════════════════════════════════════════════════════════
    … …  
    11401141    foreach ( $ignored as $ig ) {
    11411142        if ( ( $ig['name'] ?? '' ) === $cookie_name ) {
    1142             if ( ! $discovery_active ) {
     1143            // Cookies ignored automatically as visitor-side cookies stay ignored
     1144            // during discovery unless their source now checks out (the admin's
     1145            // own browser may carry the same extension).
     1146            $still_visitor = ! empty( $ig['auto'] ) && '' !== cookieflame_visitor_cookie_reason( $cookie_name, $script_domains, $set_by );
     1147            if ( ! $discovery_active || $still_visitor ) {
    11431148                return new WP_REST_Response( array( 'received' => true, 'status' => 'ignored' ), 200 );
    11441149            }
    … …  
    11501155            break;
    11511156        }
     1157    }
     1158
     1159    // Another consent tool's cookie that doesn't run here, or a cookie a
     1160    // browser extension wrote: it came with the visitor's browser, not from
     1161    // this site. Record it on the ignore list with the reason.
     1162    $visitor_reason = cookieflame_visitor_cookie_reason( $cookie_name, $script_domains, $set_by );
     1163    if ( '' !== $visitor_reason ) {
     1164        global $wpdb;
     1165        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- MySQL named lock; a lock must not be cached.
     1166        $locked = (int) $wpdb->get_var( "SELECT GET_LOCK( 'cookieflame_beacon', 5 )" );
     1167        wp_cache_delete( 'alloptions', 'options' );
     1168        cookieflame_ignore_visitor_cookie( $cookie_name, $visitor_reason );
     1169        if ( $locked ) {
     1170            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Releases the MySQL named lock taken above.
     1171            $wpdb->query( "SELECT RELEASE_LOCK( 'cookieflame_beacon' )" );
     1172        }
     1173        return new WP_REST_Response( array( 'received' => true, 'status' => 'visitor_cookie' ), 200 );
    11521174    }
    11531175
    … …  
    13641386function cookieflame_sanitize_set_by( $value ) {
    13651387    $value = strtolower( trim( is_scalar( $value ) ? (string) $value : '' ) );
    1366     if ( 'first-party' === $value ) {
     1388    if ( 'first-party' === $value || 'browser-extension' === $value ) {
    13671389        return $value;
    13681390    }
    … …  
    18791901
    18801902    if ( $action === 'restore' ) {
     1903        // Restoring a cookie that was ignored automatically as a visitor-side
     1904        // cookie is the admin overruling that check, so remember it.
     1905        foreach ( $ignored as $ig ) {
     1906            if ( ( $ig['name'] ?? '' ) === $name && ! empty( $ig['auto'] ) ) {
     1907                $allow   = (array) get_option( 'cookieflame_visitor_cookie_allow', array() );
     1908                $allow[] = $name;
     1909                update_option( 'cookieflame_visitor_cookie_allow', array_values( array_unique( $allow ) ) );
     1910            }
     1911        }
    18811912        $ignored = array_values( array_filter( $ignored, function( $ig ) use ( $name ) {
    18821913            return ( $ig['name'] ?? '' ) !== $name;
    … …  
    22162247        'ignoredCookies'      => get_option( 'cookieflame_ignored_cookies', array() ),
    22172248        'ignoredCookiesUrl'   => rest_url( 'cookieflame/v1/ignored-cookies' ),
     2249        'visitorCookieRules'  => cookieflame_visitor_cookie_rules(),
     2250        'siteScriptDomains'   => cookieflame_site_script_domains(),
    22182251        'serviceDetectors'    => cookieflame_get_service_detectors(),
    22192252    );
    … …  
    24702503        . "set:function(v){try{var n=String(v).split('=')[0].trim();"
    24712504        . "if(n&&!Object.prototype.hasOwnProperty.call(w,n)){"
    2472         . "var u=String((new Error()).stack||'').match(/https?:\\/\\/[^\\s)'\"]+/g)||[];"
     2505        . "var u=String((new Error()).stack||'').match(/(?:https?|chrome-extension|moz-extension|safari-web-extension|safari-extension):\\/\\/[^\\s)'\"]+/g)||[];"
    24732506        . "w[n]=u.length>1?u[1]:'';}}catch(e){}"
    24742507        . "d.set.call(this,v);}});"
    … …  
    26432676        // active discovery only; no REST URLs or nonces are exposed.
    26442677        $parts[] = 'window.cookieflameAdmin=Object.assign(window.cookieflameAdmin||{},' . wp_json_encode( array(
    2645             'serviceDetectors' => cookieflame_get_service_detectors(),
     2678            'serviceDetectors'   => cookieflame_get_service_detectors(),
     2679            'ignoredCookieNames' => array_values( array_filter( array_column( (array) get_option( 'cookieflame_ignored_cookies', array() ), 'name' ) ) ),
    26462680        ) ) . ');';
    26472681    }
    … …  
    26802714    if ( ! cookieflame_monitoring_is_off() ) {
    26812715        $parts[] = 'window.__ccBeaconUrl=' . wp_json_encode( rest_url( 'cookieflame/v1/beacon' ) ) . ';';
     2716        // Other consent tools' cookies to skip (see includes/consent-tools.php).
     2717        $parts[] = 'window.__ccVisitorCookieRules=' . wp_json_encode( cookieflame_visitor_cookie_rules() ) . ';';
    26822718    }
    26832719
    … …  
    26862722        implode( '', $parts ),
    26872723        array(
     2724            'id'                    => 'cookieflame-config',
    26882725            'data-cfasync'          => 'false',
    26892726            'data-no-optimize'      => '1',
    … …  
    30143051    }
    30153052
    3016     // 2. Duplicate consent plugins
     3053    // 2. Duplicate consent plugins. CookieFlame's own page settings name
     3054    // other consent tools (the cookies it skips, cookie names it has seen),
     3055    // so they are left out of this check.
     3056    $consent_html   = preg_replace( '#<script\b[^>]*\bid=(["\'])cookieflame-config\1[^>]*>[\s\S]*?</script>#i', '', $html );
    30173057    $consent_checks = array(
    30183058        array( 'cookiebot.com',      'Cookiebot' ),
    … …  
    30263066        array( 'usercentrics.eu',    'Usercentrics' ),
    30273067    );
     3068    // The check reads the page's code, so the other tool may come from a
     3069    // plugin, a page builder's custom code, the theme or a scripts plugin:
     3070    // say what was found and where, not just "deactivate the plugin".
     3071    $consent_page = substr( (string) wp_parse_url( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ?? '/' ) ), PHP_URL_PATH ), 0, 120 );
    30283072    foreach ( $consent_checks as $check ) {
    3029         if ( stripos( $html, $check[0] ) !== false ) {
     3073        if ( stripos( (string) $consent_html, $check[0] ) !== false ) {
    30303074            $conflicts[] = array(
    30313075                'type'        => 'duplicate_consent',
    30323076                'severity'    => 'critical',
    3033                 'title'       => $check[1] . ' consent plugin detected',
    3034                 'description' => 'Running two cookie consent plugins simultaneously causes unpredictable behaviour. Visitors may see two consent banners, scripts may be blocked or allowed inconsistently, and consent signals can conflict.',
    3035                 'action'      => 'Deactivate ' . $check[1] . ' if you intend to use CookieFlame as your consent solution.',
     3077                'title'       => $check[1] . ' found on your pages',
     3078                'description' => 'Your pages load ' . $check[1] . '\'s code as well as CookieFlame. Two consent tools on one site can show visitors two banners, block or allow scripts unpredictably, and mix up the choices visitors make. Found on ' . ( in_array( $consent_page, array( '', '/' ), true ) ? 'your home page' : $consent_page ) . '. In that page\'s source, search for "' . $check[0] . '".',
     3079                'action'      => 'Remove ' . $check[1] . '\'s code if CookieFlame is your consent tool. It isn\'t always a plugin: also check your page builder\'s custom code (for example Elementor > Custom Code), your theme\'s header, and any header and footer scripts plugin. Then visit a page on your site again to update this check.',
    30363080            );
    30373081        }
    … …  
    46224666                            </th>
    46234667                            <th style="width:14%">
    4624                                 <?php esc_html_e( 'Source', 'cookieflame' ); ?>
    4625                                 <button type="button" class="cf-admin-help-btn" data-help-title="<?php esc_attr_e( 'Source', 'cookieflame' ); ?>" data-help="<?php esc_attr_e( 'How this cookie was identified. Known: recognised from our built-in database. Auto-Identified: classified automatically by the CookieFlame service. Edited: you changed the classification from the default. Reported: discovered by visitors browsing your site. Unclassified: not yet identified.', 'cookieflame' ); ?>">?</button>
     4668                                <?php esc_html_e( 'Status', 'cookieflame' ); ?>
     4669                                <?php
     4670                                $cookieflame_status_help = array(
     4671                                    array( '', __( 'Where each cookie stands, and what you can do with it.', 'cookieflame' ) ),
     4672                                    array( __( 'Not identified yet', 'cookieflame' ), __( 'CookieFlame doesn\'t know this cookie yet, so your banner doesn\'t list it. Red rows were reported by visitors or found by your scan. Yellow rows were only seen in your own browser.', 'cookieflame' ) ),
     4673                                    array( __( 'Known, Auto-Identified, Classified', 'cookieflame' ), __( 'In CookieFlame\'s built-in list, named by the CookieFlame service, or sorted by Cookie Discovery or by you in this table.', 'cookieflame' ) ),
     4674                                    array( __( 'Added by you, Changed by you', 'cookieflame' ), __( 'Your own settings. Re-identify only ever suggests changes to these.', 'cookieflame' ) ),
     4675                                    array( __( 'Lock as correct (Pro)', 'cookieflame' ), __( 'Once you\'ve checked a cookie, CookieFlame won\'t change it, even when you re-identify.', 'cookieflame' ) ),
     4676                                    array( __( 'Auto-Identify, Re-identify (Pro)', 'cookieflame' ), __( 'Ask the CookieFlame service what the cookie is.', 'cookieflame' ) ),
     4677                                    array( __( 'Remove', 'cookieflame' ), __( 'Asks why first. If your site doesn\'t use it any more, it\'s taken off your list and comes back if it\'s seen again. If it isn\'t from your site, it\'s hidden for good on the Permanently Ignored list.', 'cookieflame' ) ),
     4678                                );
     4679                                $cookieflame_status_html = '';
     4680                                foreach ( $cookieflame_status_help as $cookieflame_help_item ) {
     4681                                    $cookieflame_status_html .= '<p>' . ( $cookieflame_help_item[0] ? '<strong>' . esc_html( $cookieflame_help_item[0] ) . ':</strong> ' : '' ) . esc_html( $cookieflame_help_item[1] ) . '</p>';
     4682                                }
     4683                                ?>
     4684                                <button type="button" class="cf-admin-help-btn" data-help-title="<?php esc_attr_e( 'Status', 'cookieflame' ); ?>" data-help-html="<?php echo esc_attr( '<div class="cf-admin-help-list">' . $cookieflame_status_html . '</div>' ); ?>">?</button>
    46264685                            </th>
    46274686                        </tr>
  • cookieflame/tags/5.3.0/readme.txt

    r3768250 r3769546  
    55Tested up to: 7.1
    66Requires PHP: 8.0
    7 Stable tag: 5.2.0
     7Stable tag: 5.3.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    195195== Changelog ==
    196196
     197= 5.3.0 =
     198* Cookie monitoring now ignores cookies that come with a visitor's browser rather than from your site. Some browser extensions write the "already accepted" cookies of other consent tools (such as Complianz, CookieYes, Cookiebot, OneTrust or Didomi) onto every site they visit, and these were being reported as your site's cookies. A consent tool's cookie is now ignored when that tool doesn't run on your site: its plugin isn't active, its script isn't on the page, and no script on the page wrote the cookie.
     199* Cookies written by a browser extension's own code are ignored too.
     200* Cookies ignored this way go on the Permanently Ignored list with the reason, and the list now shows why each cookie was ignored. Restoring one keeps it monitored from then on.
     201* The Cookie Manager and the Cookie Discovery count leave these cookies out too, including ones your own browser extensions add. Cookies that came with your browser during Cookie Discovery go on the Permanently Ignored list with the reason. Cookies on the Permanently Ignored list no longer show in the Cookie Manager just because they are in your own browser.
     202* On update, other consent tools' cookies that were already reported are moved to the Permanently Ignored list. Cookies you classified yourself are left alone.
     203* Fixed: the "consent plugin detected" conflict warning could be set off by CookieFlame's own list of cookie names rather than by another consent plugin on the page.
     204* Cookie Manager: the last column is now "Status". Each cookie shows one label for where it stands (Known, Auto-Identified, Classified, Added by you, Changed by you, or Not identified yet), and cookies that aren't identified yet say where they came from: found by your scan, reported by visitors, or seen in your own browser.
     205* Cookie Manager: Remove and Ignore permanently are now one "Remove…" link that asks why. "My site doesn't use it any more" takes the cookie off your list, and it comes back if it's seen again. "It's not from my site" puts it on the Permanently Ignored list for good.
     206* Cookie Manager: with CookieFlame Pro, the padlock is now a "Lock as correct" button. A locked cookie keeps its vendor, category and purpose, even when you re-identify.
     207* Cookie Manager: the small help buttons beside each link are gone. Each link explains itself when you hover over it, and the Status column's help button explains every label and action.
     208* Improved: when another consent tool's code is on your pages, the warning now says so in plain words ("Cookiebot found on your pages"), names the page and the text to look for, and lists where such code usually lives (plugins, page builder custom code such as Elementor > Custom Code, the theme's header, or a header and footer scripts plugin). It used to say a plugin was installed even when the code came from somewhere else.
     209
    197210= 5.2.0 =
    198211* Cookie monitoring now records which script first wrote each new cookie: a third-party script's domain, or the WordPress plugin or theme that set it. Previously only the full list of third-party scripts on the page was known. Cookie values are never read or sent.
    … …  
    229242== Upgrade Notice ==
    230243
     244= 5.3.0 =
     245Cookie monitoring now ignores other consent tools' cookies that visitors' browser extensions add, so they no longer appear as your site's cookies. The Cookie Manager's last column is tidier and easier to understand.
     246
    231247= 5.2.0 =
    232248Better cookie detection: CookieFlame now records which script set each new cookie and keeps checking after consent is given.
  • cookieflame/tags/5.3.0/uninstall.php

    r3766806 r3769546  
    3131delete_option( 'cookieflame_dismissed_cookies' );
    3232delete_option( 'cookieflame_ignored_cookies' );
     33delete_option( 'cookieflame_visitor_cookie_allow' );
     34delete_option( 'cookieflame_visitor_cleanup' );
    3335delete_option( 'cookieflame_discovery_backup' );
    3436delete_option( 'cookieflame_maxmind_last_update' );
  • cookieflame/trunk/assets/css/admin.css

    r3766806 r3769546  
    394394}
    395395
    396 /* Source column */
     396/* Status column: one label, then the actions, one per line */
    397397.cf-admin-source-cell {
    398398  font-size: 12px;
    … …  
    403403  margin-top: 1px;
    404404}
     405.cf-admin-status-line + .cf-admin-status-line {
     406  margin-top: 3px;
     407}
     408.cf-admin-status-where {
     409  color: #646970;
     410  line-height: 1.4;
     411}
    405412.cf-admin-source-link {
    406   font-size: 11px;
    407   color: #646970;
     413  font-size: 12px;
     414  color: #2271b1;
    408415  text-decoration: none;
    409416  cursor: pointer;
    410417  display: inline-block;
    411   margin-top: 2px;
    412418  transition: color .15s;
    413419}
    414420.cf-admin-source-link:hover {
    415   color: #2271b1;
     421  color: #135e96;
    416422  text-decoration: underline;
     423}
     424.cf-admin-source-link-main {
     425  font-weight: 600;
    417426}
    418427
    … …  
    474483  align-items: center;
    475484  gap: 4px;
    476   font-size: 11px;
    477   font-weight: 600;
     485  font-size: 12px;
     486  font-weight: 600;
     487  line-height: 20px;
     488  padding: 0 8px;
     489  border-radius: 3px;
    478490}
    479491.cf-admin-badge-known,
    480492.cf-admin-badge-ai {
    481   color: #00a32a;
     493  color: #00701f;
     494  background: #e6f4e9;
    482495}
    483496.cf-admin-badge-edited {
    484   color: #2271b1;
     497  color: #135e96;
     498  background: #e9f1fa;
    485499}
    486500.cf-admin-badge-locked {
    487   color: #996800;
     501  color: #8a5b00;
     502  background: #fcf3e1;
    488503}
    489504.cf-admin-badge-unclassified {
    490   color: #d63638;
     505  color: #8a5b00;
     506  background: #fff;
     507  box-shadow: inset 0 0 0 1px #dba617;
    491508}
    492509
    … …  
    859876  -webkit-user-select: text;
    860877}
     878.cf-admin-help-list p {
     879  margin: 0 0 8px;
     880  line-height: 1.5;
     881}
     882.cf-admin-help-list p:last-child {
     883  margin-bottom: 0;
     884}
    861885@keyframes cf-admin-fade-in {
    862886  from { opacity: 0; }
    … …  
    19421966}
    19431967
    1944 /* Lock toggle (unstyled button) */
    1945 .cf-admin-lock-toggle {
    1946   background: none;
    1947   border: none;
    1948   cursor: pointer;
     1968/* Lock as correct: a labelled button that fills in when locked */
     1969.cf-admin-source-cell .button.cf-admin-lock-toggle {
     1970  display: inline-flex;
     1971  align-items: center;
     1972  gap: 3px;
     1973  font-size: 12px;
     1974  min-height: 24px;
     1975  line-height: 22px;
     1976  padding: 0 8px 0 6px;
     1977}
     1978.cf-admin-lock-toggle .dashicons {
    19491979  font-size: 14px;
    1950   padding: 0 2px;
    1951   vertical-align: middle;
     1980  width: 14px;
     1981  height: 14px;
     1982}
     1983.cf-admin-source-cell .button.cf-admin-lock-toggle.is-locked {
     1984  background: #fcf3e1;
     1985  border-color: #dba617;
     1986  color: #8a5b00;
    19521987}
    19531988
    … …  
    19992034  font-size: 12px;
    20002035  padding: 8px 12px;
     2036}
     2037
     2038/* Remove…: asks why, in a bar under the row */
     2039.cf-admin-inline-bar-remove {
     2040  background: #f6f7f7;
     2041  border-left: 3px solid #8c8f94;
     2042  font-size: 13px;
     2043  padding: 10px 14px;
     2044}
     2045.cf-admin-remove-choices {
     2046  display: flex;
     2047  flex-wrap: wrap;
     2048  align-items: stretch;
     2049  gap: 8px;
     2050  margin-top: 8px;
     2051}
     2052.cf-admin-remove-choices .button {
     2053  display: flex;
     2054  flex-direction: column;
     2055  align-items: flex-start;
     2056  gap: 2px;
     2057  max-width: 380px;
     2058  height: auto;
     2059  padding: 6px 12px;
     2060  line-height: 1.4;
     2061  text-align: left;
     2062  white-space: normal;
     2063}
     2064.cf-admin-remove-choices .button span,
     2065.cf-admin-remove-hint {
     2066  font-size: 12px;
     2067  color: #646970;
     2068}
     2069.cf-admin-remove-hint {
     2070  display: block;
     2071  margin-top: 2px;
     2072}
     2073.cf-admin-remove-cancel {
     2074  align-self: center;
     2075  margin-left: 4px;
    20012076}
    20022077
    … …  
    20532128}
    20542129
    2055 /* Not-active indicator label */
    2056 .cf-admin-not-active-label {
    2057   font-size: 11px;
    2058   color: #646970;
    2059   display: inline-block;
    2060   margin-top: 3px;
    2061 }
    2062 
    20632130/* Origin column cell */
    20642131.cf-admin-origin-cell {
    20652132  font-size: 12px;
    2066 }
    2067 
    2068 /* Ignore link — muted by default */
    2069 .cf-admin-ignore-cookie.cf-admin-source-link {
    2070   color: #888;
    20712133}
    20722134
  • cookieflame/trunk/assets/js/admin.js

    r3766806 r3769546  
    148148   * Queue a DELETE for a single cookie via the REST endpoint.
    149149   */
    150   function queueDelete(cookieName, opts, onDone) {
     150  function queueDelete(cookieName, opts, onDone, onFail) {
    151151    // opts is optional: queueDelete(name, fn) or queueDelete(name, {dismissed:true}, fn)
    152     if (typeof opts === "function") { onDone = opts; opts = {}; }
     152    if (typeof opts === "function") { onFail = onDone; onDone = opts; opts = {}; }
    153153    opts = opts || {};
    154154    fetch(cookieflameAdmin.cookieUrl + "/" + encodeURIComponent(cookieName), {
    … …  
    164164    .catch(function (err) {
    165165      cookieflameNotice("Failed to remove \"" + cookieName + "\": " + err.message, "error");
     166      if (onFail) onFail();
    166167    });
    167168  }
    … …  
    463464  }
    464465
     466  /**
     467   * Whether a cookie in the admin's own browser is another consent tool's
     468   * cookie when that tool doesn't run on this site (its plugin isn't active
     469   * and its script hasn't been seen on the site's pages). Browser extensions
     470   * that get around consent banners write these. Same rules as the visitor
     471   * check in cookieconsent.js, without the page context.
     472   */
     473  function isVisitorOnlyCookie(name) {
     474    var rules = cookieflameAdmin && cookieflameAdmin.visitorCookieRules;
     475    if (!rules || !rules.tools) return false;
     476    if (rules.allow && rules.allow.indexOf(name) !== -1) return false;
     477    var domains = cookieflameAdmin.siteScriptDomains || [];
     478    for (var t = 0; t < rules.tools.length; t++) {
     479      var tool = rules.tools[t], hit = (tool.n || []).indexOf(name) !== -1;
     480      for (var p = 0; !hit && p < (tool.p || []).length; p++) {
     481        if (name.indexOf(tool.p[p]) === 0) hit = true;
     482      }
     483      if (!hit) continue;
     484      for (var d = 0; d < domains.length; d++) {
     485        for (var k = 0; k < (tool.d || []).length; k++) {
     486          var host = String(domains[d]).toLowerCase(), dom = tool.d[k];
     487          if (host === dom || host.slice(-dom.length - 1) === "." + dom) return false;
     488        }
     489      }
     490      return true;
     491    }
     492    return false;
     493  }
     494
    465495  /* ═══════════════════════════════════════════
    466496     RENDER SCAN TABLE
    … …  
    650680    var CAT_ORDER  = { necessary: 0, preferences: 1, analytics: 2, marketing: 3, unclassified: 4 };
    651681
     682    var ignoredSet = {};
     683    var ignoredList = (cookieflameAdmin && cookieflameAdmin.ignoredCookies) || [];
     684    for (var il = 0; il < ignoredList.length; il++) {
     685      if (ignoredList[il] && ignoredList[il].name) ignoredSet[ignoredList[il].name] = true;
     686    }
     687
    652688    // Pre-process: build row data objects for sorting
    653689    var rowData = [];
    … …  
    660696      var custom = findCustom(name);
    661697      var builtin = identifyBuiltIn(name);
     698
     699      // Cookies you haven't classified stay out of the table when they are on
     700      // the Permanently Ignored list, or when only your own browser carries
     701      // them and they belong to a consent tool that doesn't run on this site.
     702      if (!custom && ignoredSet[name]) continue;
     703      if (!custom && !beaconData[name] && !cookieOriginMap[name] && isVisitorOnlyCookie(name)) continue;
    662704
    663705      // Determine source (same logic as before)
    … …  
    794836        divider.innerHTML = '<td colspan="7">' +
    795837          '<span class="cf-admin-cat-dot" style="background:' + (CAT_COLORS[lastCat] || "#a7aaad") + '"></span>' +
    796           '<span class="cf-admin-cat-label">' + esc(lastCat.charAt(0).toUpperCase() + lastCat.slice(1)) + '</span>' +
     838          '<span class="cf-admin-cat-label">' + esc(lastCat === "unclassified" ? "Not identified yet" : lastCat.charAt(0).toUpperCase() + lastCat.slice(1)) + '</span>' +
    797839          '<span class="cf-admin-cat-count">(' + catCount + ')</span>' +
    798840          '</td>';
    … …  
    800842      }
    801843
    802       // Source column content
    803       var removeTip = 'Removes this cookie from the Cookie Manager. It will reappear as a new unclassified cookie if a visitor still has it in their browser. Use this if you classified it incorrectly and want to start again.';
    804       var ignoreTip = 'Permanently hides this cookie from the Cookie Manager. Visitor sessions will silently skip it and it will not trigger email notifications. Use this for cookies that are not from your site, such as those set by browser extensions. If you later reinstall the service that set it, Start Cookie Discovery will detect it again and automatically remove it from the ignore list. You can also restore ignored cookies manually from the Ignored Cookies panel at the bottom of this page.';
    805       var removeHtml = '<br><a href="#" class="cf-admin-delete-cookie cf-admin-source-link" data-name="' + esc(rd.name) + '">Remove</a>' +
    806         ' <button type="button" class="cf-admin-help-btn cf-admin-help-btn-sm" ' +
    807         'data-help-title="Remove cookie" data-help="' + esc(removeTip) + '">?</button>' +
    808         '<br><a href="#" class="cf-admin-ignore-cookie cf-admin-source-link" data-name="' + esc(rd.name) + '">Ignore permanently</a>' +
    809         ' <button type="button" class="cf-admin-help-btn cf-admin-help-btn-sm" ' +
    810         'data-help-title="Ignore permanently" data-help="' + esc(ignoreTip) + '">?</button>';
    811 
    812       var sourceHtml;
    813 
    814       // Helper: lock badge + toggle for classified sources
    815       function _lockBadgeHtml(src, lock, name) {
    816         var badge, lockIcon, lockTip, aiLabel, reIdLabel;
    817         // Determine badge
    818         if (lock === "locked") {
    819           badge = '<span class="cf-admin-badge-locked">&#128274; Locked</span>';
    820         } else if (lock === "edited") {
    821           badge = '<span class="cf-admin-badge-edited">&#9998; Customised</span>';
    822         } else {
    823           // Managed: show original source label
    824           if (src === "ai") { aiLabel = "Auto-Identified"; }
    825           else if (src === "discovered") { aiLabel = "Classified"; }
    826           else if (src === "manual") { aiLabel = "Manual"; }
    827           else if (src === "custom") { aiLabel = "Edited"; }
    828           else { aiLabel = "Classified"; }
    829           badge = '<span class="cf-admin-badge-ai">&#10003; ' + aiLabel + '</span>';
    830         }
    831         // Lock toggle button
    832         lockIcon = lock === "locked" ? "&#128274;" : "&#128275;";
    833         lockTip = lock === "locked"
    834           ? "This cookie is locked. Auto-Identify All will skip it. Click to unlock."
    835           : "Click to lock this cookie. Locking prevents Auto-Identify All from changing your classification.";
    836         badge += ' <button type="button" class="cf-admin-lock-toggle" data-name="' + esc(name) + '" title="' + esc(lockTip) + '" style="opacity:' + (lock === "locked" ? '1' : '0.4') + ';">' + lockIcon + '</button>';
    837         // Re-identify vs Auto-Identify label
    838         reIdLabel = (src === "ai" || lock === "edited") ? "Re-identify" : "Auto-Identify";
    839         if (cookieflameAdmin.isPremium) {
    840           var greyStyle = lock === "locked" ? ' style="color:#999;cursor:default;"' : '';
    841           badge += '<br><a href="#" class="cf-admin-identify-link cf-admin-ai-row-btn cf-admin-source-link" data-name="' + esc(name) + '"' + greyStyle + '>' + reIdLabel + '</a>';
    842         }
    843         return badge;
    844       }
    845 
    846       if (rd.source === "custom") {
    847         sourceHtml = _lockBadgeHtml("custom", rd.lock, rd.name);
    848         sourceHtml += '<br><a href="#" class="cf-admin-revert-btn cf-admin-source-link" data-name="' + esc(rd.name) + '">Revert to default</a>';
    849         sourceHtml += removeHtml;
    850       } else if (rd.source === "ai") {
    851         sourceHtml = _lockBadgeHtml("ai", rd.lock, rd.name);
    852         sourceHtml += removeHtml;
    853       } else if (rd.source === "manual") {
    854         sourceHtml = _lockBadgeHtml("manual", rd.lock, rd.name);
    855         sourceHtml += removeHtml;
    856       } else if (rd.source === "discovered") {
    857         sourceHtml = _lockBadgeHtml("discovered", rd.lock, rd.name);
    858         sourceHtml += removeHtml;
    859       } else if (rd.source === "builtin") {
    860         sourceHtml = '<span class="cf-admin-badge-known">&#10003; Known</span>';
    861         sourceHtml += '<br><a href="#" class="cf-admin-identify-link cf-admin-ai-row-btn cf-admin-source-link" data-name="' + esc(rd.name) + '">Auto-Identify</a>' + proRowPill;
    862         sourceHtml += removeHtml;
    863       } else if (rd.isBeacon) {
    864         sourceHtml = '<span class="cf-admin-badge-unclassified">&#9888; Reported</span>';
    865         sourceHtml += '<br><a href="#" class="cf-admin-ai-row-btn cf-admin-source-link" data-name="' + esc(rd.name) + '">Auto-Identify</a>' + proRowPill;
    866         sourceHtml += removeHtml;
    867       } else {
    868         sourceHtml = '<span class="cf-admin-badge-unclassified">&#9888; Unclassified</span>';
    869         sourceHtml += '<br><a href="#" class="cf-admin-ai-row-btn cf-admin-source-link" data-name="' + esc(rd.name) + '">Auto-Identify</a>' + proRowPill;
    870         sourceHtml += removeHtml;
    871       }
    872 
    873       // "Not active" label in Source column
    874       if (!rd.inBrowser) {
    875         var notActiveLabel = rd.isBeacon ? "Reported by visitors" : "Detected, not active";
    876         var notActiveTip = rd.isBeacon
    877           ? "This cookie was found by visitors browsing your site. It is not set in your admin browser because marketing and analytics scripts do not run in the admin area. Classify it so CookieFlame manages it correctly when visitors encounter it."
    878           : "This cookie was found during a discovery scan on your site but is not set in your browser right now. This is normal. You are viewing the Cookie Manager in the admin area where marketing and analytics scripts do not run. The cookie will be set for visitors on the frontend when they interact with the service or consent to its category. You should still classify it so CookieFlame manages it correctly when visitors encounter it.";
    879         sourceHtml += '<br><span class="cf-admin-not-active-label">' + notActiveLabel +
    880           ' <button type="button" class="cf-admin-help-btn cf-admin-help-btn-sm" ' +
    881           'data-help-title="' + esc(notActiveLabel) + '" data-help="' + esc(notActiveTip) + '">?</button></span>';
    882       }
    883 
    884       // Residual cookie badge (deactivated/removed plugin)
     844      // Status column: one label, then the actions (5.3.0).
    885845      var residual = residualMap[rd.name];
    886       if (residual) {
    887         sourceHtml += '<br><span class="cf-admin-badge-residual" data-residual-name="' + esc(rd.name) + '">&#128337; Residual</span>' +
    888           '<span class="cf-admin-residual-from">from ' + esc(residual.plugin) + '</span>';
    889       }
     846      var sourceHtml = statusCellHtml(rd, beaconData[rd.name], residual, proRowPill);
    890847
    891848      // Recommended label below category dropdown
    … …  
    978935    }
    979936
    980     // Bind remove buttons for manually added cookies
    981     // Bind per-row delete buttons (inline confirmation, no confirm() dialog)
    982     var delBtns = tbody.querySelectorAll(".cf-admin-delete-cookie");
    983     for (var rb = 0; rb < delBtns.length; rb++) {
    984       delBtns[rb].addEventListener("click", function (e) {
    985         e.preventDefault();
    986         var link = this;
    987         var n = link.getAttribute("data-name");
    988         var tr = link.closest("tr");
    989 
    990         // If already showing confirmation, ignore
    991         if (link.getAttribute("data-confirming")) return;
    992         link.setAttribute("data-confirming", "1");
    993 
    994         // Replace link text with inline confirmation
    995         var origHtml = link.innerHTML;
    996         link.style.display = "none";
    997         var confirmSpan = document.createElement("span");
    998         confirmSpan.className = "cf-admin-meta-hint";
    999         confirmSpan.innerHTML = 'Remove <strong>' + esc(n) + '</strong>? ' +
    1000           '<a href="#" class="cf-admin-confirm-yes cf-admin-link-danger">Yes, remove</a>' +
    1001           ' / <a href="#" class="cf-admin-confirm-no cf-admin-link-cancel">Cancel</a>';
    1002         link.parentNode.insertBefore(confirmSpan, link.nextSibling);
    1003 
    1004         confirmSpan.querySelector(".cf-admin-confirm-yes").addEventListener("click", function (ev) {
    1005           ev.preventDefault();
    1006           var source = tr ? (tr.getAttribute("data-source") || "") : "";
    1007           // Track as dismissed for builtin cookies (hides from table)
    1008           if (source === "builtin" && dismissedCookies.indexOf(n) === -1) {
    1009             dismissedCookies.push(n);
    1010           }
    1011           // Use DELETE endpoint
    1012           var delOpts = source === "builtin" ? { dismissed: true } : {};
    1013           queueDelete(n, delOpts, function () {
    1014             // Remove residual explanation bar if present
    1015             var resBar = tbody.querySelector('.cf-admin-residual-bar[data-residual-for="' + n + '"]');
    1016             if (resBar) resBar.remove();
    1017             if (tr) tr.remove();
    1018             cookieflameNotice("\"" + n + "\" removed.", "success");
    1019           });
    1020         });
    1021 
    1022         confirmSpan.querySelector(".cf-admin-confirm-no").addEventListener("click", function (ev) {
    1023           ev.preventDefault();
    1024           confirmSpan.remove();
    1025           link.style.display = "";
    1026           link.removeAttribute("data-confirming");
    1027         });
    1028       });
     937    // Bind each row's Remove… link and Lock as correct button
     938    var statusCells = tbody.querySelectorAll("td.cf-admin-source-cell");
     939    for (var cellIdx = 0; cellIdx < statusCells.length; cellIdx++) {
     940      bindStatusCell(statusCells[cellIdx], statusCells[cellIdx].closest("tr"));
    1029941    }
    1030942
    … …  
    1035947        e.preventDefault();
    1036948        identifySingleWithAi(this);
    1037       });
    1038     }
    1039 
    1040     // Bind per-row "Ignore permanently" buttons
    1041     var ignoreBtns = tbody.querySelectorAll(".cf-admin-ignore-cookie");
    1042     for (var ig = 0; ig < ignoreBtns.length; ig++) {
    1043       ignoreBtns[ig].addEventListener("click", function (e) {
    1044         e.preventDefault();
    1045         var link = this;
    1046         var n = link.getAttribute("data-name");
    1047         var tr = link.closest("tr");
    1048 
    1049         if (link.getAttribute("data-confirming")) return;
    1050         link.setAttribute("data-confirming", "1");
    1051 
    1052         // Check beacon context for confidence-aware message.
    1053         var bc = getBeaconContext(n);
    1054         var isLikelyExtension = bc && bc.pre_existing;
    1055         var confirmMsg;
    1056         if (isLikelyExtension) {
    1057           confirmMsg = 'This appears to be a browser extension cookie. ' +
    1058             'It will be permanently hidden from notifications and your cookie declaration.';
    1059         } else {
    1060           confirmMsg = 'This cookie and the script that sets it will continue to fire without visitor consent. ' +
    1061             'It will not appear in your cookie declaration or notifications.';
    1062         }
    1063 
    1064         link.style.display = "none";
    1065         var confirmSpan = document.createElement("span");
    1066         confirmSpan.className = "cf-admin-meta-hint";
    1067         confirmSpan.innerHTML = '<span class="cf-admin-link-warning">' + esc(confirmMsg) + '</span><br>' +
    1068           '<a href="#" class="cf-admin-ignore-yes cf-admin-link-danger">Yes, ignore permanently</a>' +
    1069           ' / <a href="#" class="cf-admin-ignore-no cf-admin-link-cancel">Cancel</a>';
    1070         link.parentNode.insertBefore(confirmSpan, link.nextSibling);
    1071 
    1072         confirmSpan.querySelector(".cf-admin-ignore-yes").addEventListener("click", function (ev) {
    1073           ev.preventDefault();
    1074 
    1075           // AJAX call to add to ignored list.
    1076           fetch(cookieflameAdmin.ignoredCookiesUrl, {
    1077             method: "POST",
    1078             headers: { "Content-Type": "application/json", "X-WP-Nonce": cookieflameAdmin.nonce },
    1079             credentials: "same-origin",
    1080             body: JSON.stringify({ action: "ignore", name: n })
    1081           })
    1082             .then(function (r) { return r.json(); })
    1083             .then(function (data) {
    1084               if (data.success) {
    1085                 // Update local state.
    1086                 cookieflameAdmin.ignoredCookies = data.ignored || [];
    1087                 // Remove from in-memory data.
    1088                 savedCustom = savedCustom.filter(function (c) { return c.name !== n; });
    1089                 if (deletedCookies.indexOf(n) === -1) deletedCookies.push(n);
    1090                 if (dismissedCookies.indexOf(n) === -1) dismissedCookies.push(n);
    1091                 // Remove row.
    1092                 var resBarIg = tbody.querySelector('.cf-admin-residual-bar[data-residual-for="' + n + '"]');
    1093                 if (resBarIg) resBarIg.remove();
    1094                 if (tr) tr.remove();
    1095                 // Refresh ignored section.
    1096                 renderIgnoredSection();
    1097                 cookieflameNotice("\"" + n + "\" permanently ignored.", "success");
    1098               }
    1099             })
    1100             .catch(function () {
    1101               cookieflameNotice("Could not ignore cookie. Please try again.", "error");
    1102               confirmSpan.remove();
    1103               link.style.display = "";
    1104               link.removeAttribute("data-confirming");
    1105             });
    1106         });
    1107 
    1108         confirmSpan.querySelector(".cf-admin-ignore-no").addEventListener("click", function (ev) {
    1109           ev.preventDefault();
    1110           confirmSpan.remove();
    1111           link.style.display = "";
    1112           link.removeAttribute("data-confirming");
    1113         });
    1114       });
    1115     }
    1116 
    1117     // Bind lock toggle buttons (padlock icon on classified cookies)
    1118     var lockBtns = tbody.querySelectorAll(".cf-admin-lock-toggle");
    1119     for (var lb = 0; lb < lockBtns.length; lb++) {
    1120       lockBtns[lb].addEventListener("click", function (e) {
    1121         e.preventDefault();
    1122         var cookieName = this.getAttribute("data-name");
    1123         var tr = this.closest("tr");
    1124         if (!tr) return;
    1125         var curLock = tr.getAttribute("data-lock") || "";
    1126         var newLock = curLock === "locked" ? "" : "locked";
    1127         tr.setAttribute("data-lock", newLock);
    1128 
    1129         // Update button icon, tooltip and opacity
    1130         this.innerHTML = newLock === "locked" ? "&#128274;" : "&#128275;";
    1131         this.title = newLock === "locked"
    1132           ? "This cookie is locked. Auto-Identify All will skip it. Click to unlock."
    1133           : "Click to lock this cookie. Locking prevents Auto-Identify All from changing your classification.";
    1134         this.style.opacity = newLock === "locked" ? "1" : "0.4";
    1135 
    1136         // Update badge
    1137         var sourceCell = tr.querySelector(".cf-admin-source-cell");
    1138         if (sourceCell) {
    1139           var badge = sourceCell.querySelector("[class^='cf-admin-badge']");
    1140           if (badge) {
    1141             if (newLock === "locked") {
    1142               badge.className = "cf-admin-badge-locked";
    1143               badge.innerHTML = "&#128274; Locked";
    1144             } else {
    1145               var src = tr.getAttribute("data-source") || "";
    1146               var label = src === "ai" ? "Auto-Identified" : src === "discovered" ? "Classified" : src === "manual" ? "Manual" : "Edited";
    1147               badge.className = "cf-admin-badge-ai";
    1148               badge.innerHTML = "&#10003; " + label;
    1149             }
    1150           }
    1151           // Update Re-identify link grey state
    1152           var reIdLink = sourceCell.querySelector(".cf-admin-identify-link");
    1153           if (reIdLink) {
    1154             reIdLink.style.color = newLock === "locked" ? "#999" : "";
    1155             reIdLink.style.cursor = newLock === "locked" ? "default" : "";
    1156           }
    1157         }
    1158 
    1159         queueSave(cookieName, { lock: newLock });
    1160949      });
    1161950    }
    … …  
    1169958        pluginCount + " CookieFlame, " +
    1170959        thirdPartyCount + " third-party, " +
    1171         unknownCount + " unclassified).";
     960        unknownCount + " not identified yet).";
    1172961    }
    1173962
    … …  
    1184973    // Render the permanently ignored cookies section.
    1185974    renderIgnoredSection();
     975  }
     976
     977  /* ═══════════════════════════════════════════
     978     STATUS COLUMN (5.3.0)
     979     One label for where the cookie stands, then its actions: Lock as
     980     correct (Pro), Auto-Identify or Re-identify, Revert to default,
     981     and one Remove… link that asks why before it removes or ignores
     982     the cookie. The explanations live in each link's hover text and
     983     the Status column's help.
     984  ═══════════════════════════════════════════ */
     985
     986  var LOCK_TIP_OFF = "Press this once you've checked the vendor, category and purpose are right. CookieFlame won't change them after that, even when you re-identify.";
     987  var LOCK_TIP_ON  = "You've checked this cookie. CookieFlame won't change its vendor, category or purpose, even when you re-identify. Click to unlock.";
     988
     989  function statusLabelHtml(source, lock) {
     990    if (source === "unknown")  return '<span class="cf-admin-badge-unclassified">Not identified yet</span>';
     991    if (source === "builtin")  return '<span class="cf-admin-badge-known">Known</span>';
     992    if (source === "manual")   return '<span class="cf-admin-badge-edited">Added by you</span>';
     993    if (source === "custom" || lock === "edited") return '<span class="cf-admin-badge-edited">Changed by you</span>';
     994    if (source === "discovered") return '<span class="cf-admin-badge-known">Classified</span>';
     995    return '<span class="cf-admin-badge-ai">Auto-Identified</span>';
     996  }
     997
     998  function lockButtonInner(locked) {
     999    return '<span class="dashicons dashicons-' + (locked ? "lock" : "unlock") + '" aria-hidden="true"></span>' +
     1000      (locked ? "Locked by you" : "Lock as correct");
     1001  }
     1002
     1003  // Where a cookie nobody has identified yet came from. Pro 1.5.1+ marks
     1004  // the cookies its clean scan added with found_by "clean_scan".
     1005  function whereFoundText(rd, beacon) {
     1006    if (beacon && beacon.found_by === "clean_scan") return "Found by your scan";
     1007    if (beacon) return "Reported by visitors";
     1008    if (rd.inBrowser) return "Seen in your browser";
     1009    return "";
     1010  }
     1011
     1012  function statusCellHtml(rd, beacon, residual, proPill) {
     1013    var name = esc(rd.name);
     1014    var classified = ["ai", "manual", "discovered", "custom"].indexOf(rd.source) !== -1;
     1015    var line = function (html, cls, hidden) {
     1016      return '<div class="cf-admin-status-line' + (cls ? " " + cls : "") + '"' + (hidden ? ' style="display:none;"' : "") + '>' + html + '</div>';
     1017    };
     1018
     1019    var html = line(statusLabelHtml(rd.source, rd.lock));
     1020    var where = rd.source === "unknown" ? whereFoundText(rd, beacon) : "";
     1021    if (where) html += line(esc(where), "cf-admin-status-where");
     1022
     1023    // Locking only matters to Pro, which is what re-identifies cookies.
     1024    if (classified && cookieflameAdmin.isPremium) {
     1025      var locked = rd.lock === "locked";
     1026      html += line('<button type="button" class="button button-small cf-admin-lock-toggle' + (locked ? " is-locked" : "") + '" data-name="' + name + '"' +
     1027        ' aria-pressed="' + (locked ? "true" : "false") + '" title="' + esc(locked ? LOCK_TIP_ON : LOCK_TIP_OFF) + '">' + lockButtonInner(locked) + '</button>');
     1028    }
     1029
     1030    if (rd.source === "unknown") {
     1031      html += line('<a href="#" class="cf-admin-ai-row-btn cf-admin-source-link cf-admin-source-link-main" data-name="' + name + '" title="Ask CookieFlame\'s service what this cookie is and fill in its details.">Auto-Identify</a>' + proPill);
     1032    } else if (rd.source === "builtin") {
     1033      html += line('<a href="#" class="cf-admin-identify-link cf-admin-ai-row-btn cf-admin-source-link" data-name="' + name + '" title="Ask CookieFlame\'s service what this cookie is.">Auto-Identify</a>' + proPill);
     1034    } else if (cookieflameAdmin.isPremium) {
     1035      var reLabel = (rd.source === "ai" || rd.lock === "edited") ? "Re-identify" : "Auto-Identify";
     1036      html += line('<a href="#" class="cf-admin-identify-link cf-admin-ai-row-btn cf-admin-source-link" data-name="' + name + '" title="Ask CookieFlame\'s service about this cookie again. Your own changes are only ever suggested, never replaced.">' + reLabel + '</a>',
     1037        "cf-admin-status-reidentify", rd.lock === "locked");
     1038    }
     1039
     1040    if (rd.source === "custom") {
     1041      html += line('<a href="#" class="cf-admin-revert-btn cf-admin-source-link" data-name="' + name + '" title="Go back to the vendor, category and purpose from CookieFlame\'s built-in list.">Revert to default</a>');
     1042    }
     1043    html += line('<a href="#" class="cf-admin-remove-ask cf-admin-source-link" data-name="' + name + '" title="Asks whether your site no longer uses it, or it isn\'t from your site.">Remove&hellip;</a>');
     1044
     1045    if (residual) {
     1046      html += line('<span class="cf-admin-badge-residual" data-residual-name="' + name + '">&#128337; Residual</span>' +
     1047        '<span class="cf-admin-residual-from">from ' + esc(residual.plugin) + '</span>');
     1048    }
     1049    return html;
     1050  }
     1051
     1052  function setLockState(tr, lock) {
     1053    var locked = lock === "locked";
     1054    tr.setAttribute("data-lock", lock);
     1055    var btn = tr.querySelector(".cf-admin-lock-toggle");
     1056    if (btn) {
     1057      btn.classList.toggle("is-locked", locked);
     1058      btn.setAttribute("aria-pressed", locked ? "true" : "false");
     1059      btn.title = locked ? LOCK_TIP_ON : LOCK_TIP_OFF;
     1060      btn.innerHTML = lockButtonInner(locked);
     1061    }
     1062    // A locked cookie isn't re-identified, so the link goes while it's locked.
     1063    var reId = tr.querySelector(".cf-admin-status-reidentify");
     1064    if (reId) reId.style.display = locked ? "none" : "";
     1065  }
     1066
     1067  function bindStatusCell(cell, tr) {
     1068    if (!cell || !tr) return;
     1069    var removeLink = cell.querySelector(".cf-admin-remove-ask");
     1070    if (removeLink) {
     1071      removeLink.addEventListener("click", function (e) {
     1072        e.preventDefault();
     1073        openRemoveBar(tr);
     1074      });
     1075    }
     1076    var lockBtn = cell.querySelector(".cf-admin-lock-toggle");
     1077    if (lockBtn) {
     1078      lockBtn.addEventListener("click", function (e) {
     1079        e.preventDefault();
     1080        var newLock = (tr.getAttribute("data-lock") || "") === "locked" ? "" : "locked";
     1081        setLockState(tr, newLock);
     1082        queueSave(tr.getAttribute("data-cookie-name"), { lock: newLock });
     1083      });
     1084    }
     1085  }
     1086
     1087  /**
     1088   * Remove… asks why, in a bar under the row. "My site doesn't use it any
     1089   * more" removes the cookie, so it comes back if it's seen again. "It's
     1090   * not from my site" puts it on the Permanently Ignored list.
     1091   */
     1092  function openRemoveBar(tr) {
     1093    var n = tr.getAttribute("data-cookie-name");
     1094    var tbody = tr.parentNode;
     1095    if (!n || !tbody) return;
     1096    var key = CSS.escape(n);
     1097    var open = tbody.querySelector('.cf-admin-remove-bar[data-remove-for="' + key + '"]');
     1098    if (open) { open.remove(); return; }
     1099
     1100    var bc = getBeaconContext(n);
     1101    var extHint = (bc && bc.pre_existing)
     1102      ? '<em class="cf-admin-remove-hint">This one was already in a visitor\'s browser before your page loaded, which usually means an extension.</em>'
     1103      : "";
     1104
     1105    var bar = document.createElement("tr");
     1106    bar.className = "cf-admin-remove-bar";
     1107    bar.setAttribute("data-remove-for", n);
     1108    bar.innerHTML = '<td colspan="7" class="cf-admin-inline-bar-remove">' +
     1109      '<strong>Why are you removing <code>' + esc(n) + '</code>?</strong>' +
     1110      '<div class="cf-admin-remove-choices">' +
     1111        '<button type="button" class="button cf-admin-remove-unused"><strong>My site doesn\'t use it any more</strong>' +
     1112          '<span>Taken off your list. If your site sets it again, it comes back to be identified.</span></button>' +
     1113        '<button type="button" class="button cf-admin-remove-notmine"><strong>It\'s not from my site</strong>' +
     1114          '<span>From a visitor\'s browser extension or another website. Hidden for good, and CookieFlame stops managing it. You can bring it back from Permanently Ignored at the bottom of this page.</span>' +
     1115          extHint + '</button>' +
     1116        '<a href="#" class="cf-admin-remove-cancel cf-admin-link-cancel">Cancel</a>' +
     1117      '</div></td>';
     1118    tbody.insertBefore(bar, tr.nextSibling);
     1119
     1120    var buttons = bar.querySelectorAll("button");
     1121    var busy = function (on) {
     1122      for (var i = 0; i < buttons.length; i++) buttons[i].disabled = on;
     1123    };
     1124    var gone = function (msg) {
     1125      var extras = tbody.querySelectorAll('[data-remove-for="' + key + '"], [data-residual-for="' + key + '"], [data-suggestion-for="' + key + '"]');
     1126      for (var i = 0; i < extras.length; i++) extras[i].remove();
     1127      tr.remove();
     1128      cookieflameNotice(msg, "success");
     1129    };
     1130
     1131    bar.querySelector(".cf-admin-remove-unused").addEventListener("click", function () {
     1132      busy(true);
     1133      var source = tr.getAttribute("data-source") || "";
     1134      // A Known cookie still in your browser would show again at once, so it's hidden until seen again.
     1135      if (source === "builtin" && dismissedCookies.indexOf(n) === -1) dismissedCookies.push(n);
     1136      queueDelete(n, source === "builtin" ? { dismissed: true } : {}, function () {
     1137        gone("\"" + esc(n) + "\" removed.");
     1138      }, function () { busy(false); });
     1139    });
     1140
     1141    bar.querySelector(".cf-admin-remove-notmine").addEventListener("click", function () {
     1142      busy(true);
     1143      fetch(cookieflameAdmin.ignoredCookiesUrl, {
     1144        method: "POST",
     1145        headers: { "Content-Type": "application/json", "X-WP-Nonce": cookieflameAdmin.nonce },
     1146        credentials: "same-origin",
     1147        body: JSON.stringify({ action: "ignore", name: n })
     1148      })
     1149        .then(function (r) { return r.json(); })
     1150        .then(function (data) {
     1151          if (!data || !data.success) throw new Error("not ignored");
     1152          cookieflameAdmin.ignoredCookies = data.ignored || [];
     1153          savedCustom = savedCustom.filter(function (c) { return c.name !== n; });
     1154          if (deletedCookies.indexOf(n) === -1) deletedCookies.push(n);
     1155          if (dismissedCookies.indexOf(n) === -1) dismissedCookies.push(n);
     1156          gone("\"" + esc(n) + "\" moved to Permanently Ignored.");
     1157          renderIgnoredSection();
     1158        })
     1159        .catch(function () {
     1160          busy(false);
     1161          cookieflameNotice("Could not ignore cookie. Please try again.", "error");
     1162        });
     1163    });
     1164
     1165    bar.querySelector(".cf-admin-remove-cancel").addEventListener("click", function (e) {
     1166      e.preventDefault();
     1167      bar.remove();
     1168    });
    11861169  }
    11871170
    … …  
    15831566          }
    15841567
     1568          // It's identified now, so where it came from no longer matters.
     1569          var where = sourceCell.querySelector(".cf-admin-status-where");
     1570          if (where) where.remove();
     1571
    15851572          // Update AI link to "Re-identify" and remove lock toggle if present.
    15861573          var aiLink = sourceCell.querySelector(".cf-admin-identify-link, .cf-admin-ai-row-btn");
    … …  
    16151602    if (aiBtn) aiBtn.addEventListener("click", function (e) { e.preventDefault(); identifySingleWithAi(this); });
    16161603
    1617     // Delete (Remove) link — inline confirmation flow
    1618     var delBtn = cell.querySelector(".cf-admin-delete-cookie");
    1619     if (delBtn) {
    1620       delBtn.addEventListener("click", function (e) {
    1621         e.preventDefault();
    1622         var link = this;
    1623         var n = link.getAttribute("data-name");
    1624         if (link.getAttribute("data-confirming")) return;
    1625         link.setAttribute("data-confirming", "1");
    1626         link.style.display = "none";
    1627         var confirmSpan = document.createElement("span");
    1628         confirmSpan.className = "cf-admin-meta-hint";
    1629         confirmSpan.innerHTML = 'Remove <strong>' + esc(n) + '</strong>? ' +
    1630           '<a href="#" class="cf-admin-confirm-yes cf-admin-link-danger">Yes, remove</a>' +
    1631           ' / <a href="#" class="cf-admin-confirm-no cf-admin-link-cancel">Cancel</a>';
    1632         link.parentNode.insertBefore(confirmSpan, link.nextSibling);
    1633         confirmSpan.querySelector(".cf-admin-confirm-yes").addEventListener("click", function (ev) {
    1634           ev.preventDefault();
    1635           var source = tr ? (tr.getAttribute("data-source") || "") : "";
    1636           if (source === "builtin" && dismissedCookies.indexOf(n) === -1) dismissedCookies.push(n);
    1637           var delOpts = source === "builtin" ? { dismissed: true } : {};
    1638           queueDelete(n, delOpts, function () {
    1639             var tbody = tr ? tr.parentNode : null;
    1640             if (tbody) {
    1641               var resBar = tbody.querySelector('.cf-admin-residual-bar[data-residual-for="' + n + '"]');
    1642               if (resBar) resBar.remove();
    1643             }
    1644             if (tr) tr.remove();
    1645             cookieflameNotice("\"" + n + "\" removed.", "success");
    1646           });
    1647         });
    1648         confirmSpan.querySelector(".cf-admin-confirm-no").addEventListener("click", function (ev) {
    1649           ev.preventDefault();
    1650           confirmSpan.remove();
    1651           link.style.display = "";
    1652           link.removeAttribute("data-confirming");
    1653         });
    1654       });
    1655     }
    1656 
    1657     // Ignore link — inline confirmation flow
    1658     var ignoreBtn = cell.querySelector(".cf-admin-ignore-cookie");
    1659     if (ignoreBtn) {
    1660       ignoreBtn.addEventListener("click", function (e) {
    1661         e.preventDefault();
    1662         var link = this;
    1663         var n = link.getAttribute("data-name");
    1664         if (link.getAttribute("data-confirming")) return;
    1665         link.setAttribute("data-confirming", "1");
    1666         link.style.display = "none";
    1667         var confirmSpan = document.createElement("span");
    1668         confirmSpan.className = "cf-admin-meta-hint";
    1669         confirmSpan.innerHTML = 'Permanently ignore <strong>' + esc(n) + '</strong>? ' +
    1670           '<a href="#" class="cf-admin-ignore-yes cf-admin-link-warning">Yes, ignore permanently</a>' +
    1671           ' / <a href="#" class="cf-admin-ignore-no cf-admin-link-cancel">Cancel</a>';
    1672         link.parentNode.insertBefore(confirmSpan, link.nextSibling);
    1673         confirmSpan.querySelector(".cf-admin-ignore-yes").addEventListener("click", function (ev) {
    1674           ev.preventDefault();
    1675           fetch(cookieflameAdmin.ignoredCookiesUrl, {
    1676             method: "POST",
    1677             headers: { "Content-Type": "application/json", "X-WP-Nonce": cookieflameAdmin.nonce },
    1678             credentials: "same-origin",
    1679             body: JSON.stringify({ action: "ignore", name: n })
    1680           })
    1681             .then(function (r) { return r.json(); })
    1682             .then(function (data) {
    1683               if (data.success) {
    1684                 cookieflameAdmin.ignoredCookies = data.ignored || [];
    1685                 savedCustom = savedCustom.filter(function (c) { return c.name !== n; });
    1686                 if (deletedCookies.indexOf(n) === -1) deletedCookies.push(n);
    1687                 if (dismissedCookies.indexOf(n) === -1) dismissedCookies.push(n);
    1688                 var tbody = tr ? tr.parentNode : null;
    1689                 if (tbody) {
    1690                   var resBar = tbody.querySelector('.cf-admin-residual-bar[data-residual-for="' + n + '"]');
    1691                   if (resBar) resBar.remove();
    1692                 }
    1693                 if (tr) tr.remove();
    1694                 renderIgnoredSection();
    1695                 cookieflameNotice("\"" + n + "\" permanently ignored.", "success");
    1696               }
    1697             })
    1698             .catch(function () {
    1699               cookieflameNotice("Could not ignore cookie. Please try again.", "error");
    1700               confirmSpan.remove();
    1701               link.style.display = "";
    1702               link.removeAttribute("data-confirming");
    1703             });
    1704         });
    1705         confirmSpan.querySelector(".cf-admin-ignore-no").addEventListener("click", function (ev) {
    1706           ev.preventDefault();
    1707           confirmSpan.remove();
    1708           link.style.display = "";
    1709           link.removeAttribute("data-confirming");
    1710         });
    1711       });
    1712     }
    1713 
    1714     // Lock toggle
    1715     var lockBtn = cell.querySelector(".cf-admin-lock-toggle");
    1716     if (lockBtn) {
    1717       lockBtn.addEventListener("click", function (e) {
    1718         e.preventDefault();
    1719         var n = this.getAttribute("data-name");
    1720         var currentLock = tr.getAttribute("data-lock") || "";
    1721         var newLock = currentLock === "locked" ? "" : "locked";
    1722         tr.setAttribute("data-lock", newLock);
    1723         this.innerHTML = newLock === "locked" ? "&#128274;" : "&#128275;";
    1724         this.title = newLock === "locked"
    1725           ? "This cookie is locked. Auto-Identify All will skip it. Click to unlock."
    1726           : "Click to lock this cookie. Locking prevents Auto-Identify All from changing your classification.";
    1727         this.style.opacity = newLock === "locked" ? "1" : "0.4";
    1728         var badge = cell.querySelector("[class^='cf-admin-badge']");
    1729         if (badge) {
    1730           if (newLock === "locked") {
    1731             badge.className = "cf-admin-badge-locked";
    1732             badge.innerHTML = "&#128274; Locked";
    1733           } else {
    1734             var src = tr.getAttribute("data-source") || "";
    1735             var label = src === "ai" ? "Auto-Identified" : src === "discovered" ? "Classified" : src === "manual" ? "Manual" : "Edited";
    1736             badge.className = "cf-admin-badge-ai";
    1737             badge.innerHTML = "&#10003; " + label;
    1738           }
    1739         }
    1740         var reIdLink = cell.querySelector(".cf-admin-identify-link");
    1741         if (reIdLink) {
    1742           reIdLink.style.color = newLock === "locked" ? "#999" : "";
    1743           reIdLink.style.cursor = newLock === "locked" ? "default" : "";
    1744         }
    1745         queueSave(n, { lock: newLock });
    1746       });
    1747     }
     1604    // Remove… link and Lock as correct button
     1605    bindStatusCell(cell, tr);
    17481606
    17491607    // Residual badge toggle
    … …  
    20481906          if (badge) {
    20491907            badge.className = "cf-admin-badge-ai";
    2050             badge.innerHTML = "&#10003; Auto-Identified";
     1908            badge.textContent = "Auto-Identified";
    20511909          }
    20521910          // Remove any confirmed tick left over.
    … …  
    20881946        lockPrompt.setAttribute("data-suggestion-for", n);
    20891947        lockPrompt.innerHTML = '<td colspan="7" class="cf-admin-inline-bar-lock">' +
    2090           'Lock this cookie to prevent future suggestions? ' +
     1948          'Lock it as correct, so CookieFlame stops suggesting changes? ' +
    20911949          '<a href="#" class="cf-admin-lock-yes cf-admin-link-cancel" style="margin-left:4px;font-weight:600;">Yes</a>' +
    20921950          ' / <a href="#" class="cf-admin-lock-no cf-admin-meta-hint" style="margin-left:4px;">No</a>' +
    … …  
    20991957        lockPrompt.querySelector(".cf-admin-lock-yes").addEventListener("click", function (ev) {
    21001958          ev.preventDefault();
    2101           cookieRow.setAttribute("data-lock", "locked");
     1959          setLockState(cookieRow, "locked");
    21021960          queueSave(n, { lock: "locked" });
    2103           // Update padlock visuals.
    2104           var lockBtn = cookieRow.querySelector(".cf-admin-lock-toggle");
    2105           if (lockBtn) { lockBtn.innerHTML = "&#128274;"; lockBtn.style.opacity = "1"; }
    2106           var badge = cookieRow.querySelector("[class^='cf-admin-badge']");
    2107           if (badge) { badge.className = "cf-admin-badge-locked"; badge.innerHTML = "&#128274; Locked"; }
    2108           var reIdLink = cookieRow.querySelector(".cf-admin-identify-link");
    2109           if (reIdLink) { reIdLink.style.color = "#999"; reIdLink.style.cursor = "default"; }
    21101961          lockPrompt.remove();
    21111962        });
    … …  
    22122063          if (badge) {
    22132064            badge.className = "cf-admin-badge-ai";
    2214             badge.innerHTML = "&#10003; Auto-Identified";
     2065            badge.textContent = "Auto-Identified";
    22152066          }
    22162067        }
    … …  
    22992150
    23002151    if (expanded) {
    2301       h += '<p class="cf-admin-ignored-desc">These cookies have been permanently excluded from passive monitoring. They will never trigger notifications or appear in your cookie declaration. Click Restore to start monitoring a cookie again.</p>';
    2302       h += '<table class="widefat striped cf-admin-ignored-table"><thead><tr><th>Cookie</th><th>Ignored</th><th></th></tr></thead><tbody>';
     2152      h += '<p class="cf-admin-ignored-desc">These cookies have been permanently excluded from passive monitoring. They will never trigger notifications or appear in your cookie declaration. Cookies with a reason were ignored automatically because they came with a visitor\'s browser, such as another consent tool\'s cookie written by a browser extension. Click Restore to start monitoring a cookie again.</p>';
     2153      h += '<table class="widefat striped cf-admin-ignored-table"><thead><tr><th>Cookie</th><th>Why</th><th>Ignored</th><th></th></tr></thead><tbody>';
    23032154      for (var i = 0; i < ignored.length; i++) {
    23042155        var ig = ignored[i];
    … …  
    23062157        h += '<tr data-ignored-name="' + esc(ig.name) + '">';
    23072158        h += '<td><code>' + esc(ig.name) + '</code></td>';
     2159        h += '<td>' + esc(ig.reason || "Ignored by you") + '</td>';
    23082160        h += '<td>' + esc(when) + '</td>';
    23092161        h += '<td><button type="button" class="button button-small cf-admin-restore-ignored" data-name="' + esc(ig.name) + '">Restore</button></td>';
    … …  
    23442196          .then(function (data) {
    23452197            if (data.success) {
     2198              // The server remembers restored automatic entries; mirror that so
     2199              // the Cookie Manager shows the cookie again without a reload.
     2200              var rules = cookieflameAdmin.visitorCookieRules;
     2201              var wasAuto = (cookieflameAdmin.ignoredCookies || []).some(function (ig) { return ig.name === n && ig.auto; });
     2202              if (wasAuto && rules && rules.allow && rules.allow.indexOf(n) === -1) rules.allow.push(n);
    23462203              cookieflameAdmin.ignoredCookies = data.ignored || [];
    23472204              renderIgnoredSection();
    … …  
    27122569            tr.setAttribute("data-source", "discovered");
    27132570            tr.className = "";
    2714             // Update source cell badge + lock icon after save completes
     2571            // Update the status label; it no longer needs identifying
    27152572            var srcCell = tr.querySelector(".cf-admin-source-cell");
    27162573            if (srcCell) {
    … …  
    27182575              if (badge) {
    27192576                badge.className = "cf-admin-badge-known";
    2720                 badge.innerHTML = "&#10003; Classified";
     2577                badge.textContent = "Classified";
    27212578              }
     2579              var where = srcCell.querySelector(".cf-admin-status-where");
     2580              if (where) where.remove();
    27222581            }
    27232582          }
  • cookieflame/trunk/assets/js/cookieconsent-discovery.js

    r3766806 r3769546  
    11/**
    2  * CookieFlame Discovery Bundle v5.1.1
     2 * CookieFlame Discovery Bundle v5.3.0
    33 * ─────────────────────────────────────────────────────────────
    44 * Admin-only script detection and discovery bar. Loaded
    … …  
    287287      var seen = {};
    288288      var hasBeforeSnapshot = Object.keys(beforeCookieSet).length > 0;
     289      // The Cookie Manager doesn't list cookies that came with the browser
     290      // (another consent tool's cookie, or one a browser extension wrote) or
     291      // ignored ones, so they aren't counted here either.
     292      var visitor = _cc.visitorCookies ? _cc.visitorCookies(current) : {};
     293      var ignoredNames = (window.cookieflameAdmin && window.cookieflameAdmin.ignoredCookieNames) || [];
    289294      for (var i = 0; i < current.length; i++) {
    290295        var name = current[i];
    … …  
    292297        seen[name] = true;
    293298        if (isSafeForCounter(name)) continue;
     299        if (visitor[name] || ignoredNames.indexOf(name) !== -1) continue;
    294300        if (hasBeforeSnapshot && beforeCookieSet[name]) {
    295301          existing.push(name);
  • cookieflame/trunk/assets/js/cookieconsent-discovery.min.js

    r3766806 r3769546  
    11/**
    2  * CookieFlame Discovery Bundle v5.1.1
     2 * CookieFlame Discovery Bundle v5.3.0
    33 * ─────────────────────────────────────────────────────────────
    44 * Admin-only script detection and discovery bar. Loaded
    … …  
    1313 * the core bundle despite the declared WordPress dependency.
    1414 */
    15 !function(){"use strict";var e=window.__ccInternal;if(e){var t=e.scriptLoaded,o=e.scriptLoadedUrls,n=e.hasCookie,i={"Google Analytics":function(){return t("googletagmanager.com/gtag")||t("google-analytics.com")||t("analytics.google.com")||n("_ga")},"Google Ads":function(){return t("googleads.g.doubleclick.net")||t("googleadservices.com")||t("googlesyndication.com")||n("_gcl_au")||n("_gcl_aw")},"Google Tag Manager":function(){return t("googletagmanager.com/gtm.js")||!!window.google_tag_manager},"Microsoft Ads":function(){return t("bat.bing.com")||!!window.uetq||n("_uetsid")||n("_uetvid")},"Meta / Facebook":function(){return t("connect.facebook.net")||!!window.fbq||n("_fbp")||n("_fbc")},"Twitter / X":function(){return t("ads-twitter.com")||t("static.ads-twitter.com")||!!window.twq||n("_twpid")},LinkedIn:function(){return t("snap.licdn.com")||t("ads.linkedin.com")||!!window._linkedin_data_partner_ids||n("li_sugr")||n("bcookie")||n("lidc")},Pinterest:function(){return t("s.pinimg.com/ct")||t("pintrk")||!!window.pintrk||n("_pin_unauth")},Reddit:function(){return t("alb.reddit.com")||t("events.reddit.com")||!!window.rdt||n("_rdt_uuid")},TikTok:function(){return t("analytics.tiktok.com")||!!window.ttq}};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",a):setTimeout(a,100)}function a(){var t=window.__ccDiscovery;if(t&&t.active&&(!document.body||!document.body.classList.contains("wp-admin"))){var n=function(){try{var e=sessionStorage.getItem("cookieflame_discovery_services");return e?JSON.parse(e):{}}catch(e){return{}}}(),a={};try{var r=localStorage.getItem("cookieflame_discovery_before");if(r)for(var c=JSON.parse(r),s=0;s<c.length;s++)a[c[s]]=!0}catch(e){}var d=["wordpress_","wp-settings","wp_lang","PHPSESSID","cc_consent","cc_anon_id","cookieflame_","woocommerce_","wp_woocommerce_"],l=document.createElement("div");l.id="cf-admin-discovery-bar",l.style.cssText="position:fixed;top:0;left:0;right:0;z-index:999999;background:#1d2327;color:#f0f0f1;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:14px;display:flex;align-items:center;justify-content:space-between;padding:10px 20px;box-shadow:0 2px 8px rgba(0,0,0,.3);gap:16px;flex-wrap:wrap;";var m=document.createElement("div");m.style.cssText="display:flex;align-items:center;gap:12px;flex:1;min-width:200px;";var g=document.createElement("span");g.style.cssText="width:10px;height:10px;border-radius:50%;background:#00ba37;display:inline-block;flex-shrink:0;animation:cf-admin-pulse 2s infinite;";var p=document.createElement("span");p.textContent="Cookie Discovery Active",p.style.fontWeight="600";var f=document.createElement("span");f.id="cf-admin-discovery-count",f.style.cssText="background:#2271b1;color:#fff;padding:2px 10px;border-radius:12px;font-size:13px;font-weight:600;cursor:help;",f.textContent="scanning...",m.appendChild(g),m.appendChild(p),m.appendChild(f);var u=document.createElement("span");u.style.cssText="color:#a7aaad;font-size:13px;display:inline-flex;flex-direction:column;gap:2px;";var h=document.createElement("span");h.textContent="All cookies are temporarily accepted. Browse your pages, then click Finish.";var v=document.createElement("span");v.textContent="Tip: Browse by clicking links on the site. Typed URLs may skip discovery.",v.style.cssText="font-size:11px;opacity:0.75;",u.appendChild(h),u.appendChild(v),C(),setTimeout(C,500),setTimeout(C,2e3);var y=document.createElement("button");y.textContent="Finish Discovery",y.style.cssText="background:#00ba37;color:#fff;border:none;padding:8px 20px;border-radius:4px;font-size:14px;font-weight:600;cursor:pointer;white-space:nowrap;",y.onmouseover=function(){y.style.background="#009929"},y.onmouseout=function(){y.style.background="#00ba37"},l.appendChild(m),l.appendChild(u),l.appendChild(y);var k=document.createElement("style");k.textContent="@keyframes cf-admin-pulse{0%,100%{opacity:1}50%{opacity:.4}}#cf-admin-discovery-bar~*{margin-top:0!important;}body.admin-bar #cf-admin-discovery-bar{top:32px}@media screen and (max-width:782px){body.admin-bar #cf-admin-discovery-bar{top:46px}}",document.head.appendChild(k),document.body.insertBefore(l,document.body.firstChild),document.addEventListener("click",function(e){for(var t=e.target;t&&1===t.nodeType&&"A"!==t.tagName;)t=t.parentNode;if(t&&"A"===t.tagName){var o=t.getAttribute("target");if("_blank"!==o&&"_top"!==o&&"_parent"!==o){var n=t.getAttribute("rel")||"";if(!n||-1===n.split(/\s+/).indexOf("external")){var i=t.getAttribute("href");if(i){var a;try{a=new URL(i,location.href)}catch(e){return}"http:"!==a.protocol&&"https:"!==a.protocol||a.origin===location.origin&&(a.pathname===location.pathname&&a.search===location.search&&a.hash!==location.hash||a.searchParams.has("cookieflame")||(a.searchParams.set("cookieflame","discover"),t.setAttribute("href",a.toString())))}}}}},!0),setTimeout(x,5e3);var w=setInterval(x,3e3);y.addEventListener("click",function(){y.disabled=!0,y.textContent="Finishing...",y.style.opacity="0.7",clearInterval(w),x();var e=n,o=window.__ccConfig||{};try{localStorage.setItem("cookieflame_discovery_results",JSON.stringify(e))}catch(t){var i=encodeURIComponent(JSON.stringify(e)),a=o.cookieDomain?"; domain="+o.cookieDomain:"";document.cookie="cookieflame_discovery_results="+i+"; path=/; max-age=300"+a}fetch(t.finishUrl,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","X-WP-Nonce":t.nonce},body:"{}"}).then(function(e){if(403===e.status)throw new Error("Your session has expired. Please reload the page and try again.");return e.json()}).then(function(e){var i=[location.hostname,"."+location.hostname];o.cookieDomain&&i.push(o.cookieDomain);for(var a=0;a<i.length;a++)document.cookie="cc_consent=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i[a];if(document.cookie="cc_consent=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/",e.restore_dnt){var r=o.cookieDomain?"; domain="+o.cookieDomain:"";document.cookie="cookieflame_dnt=1; path=/; max-age=31536000"+r}try{sessionStorage.removeItem("cookieflame_discovery_services")}catch(e){}for(var c=_(),s=0,d={},m=Object.keys(n),g=0;g<m.length;g++){var p=n[m[g]];if(p.unknown)s++,d[m[g]]=!0;else{var f=p.cookies||[];s+=f.length;for(var u=0;u<f.length;u++)d[f[u]]=!0}}for(var h=c.confirmed.concat(c.existing),v=0;v<h.length;v++)d[h[v]]||s++;l.style.background="#00ba37",l.innerHTML="";var y=document.createElement("span");y.style.cssText="font-weight:600;flex:1;text-align:center;";var k=s+" cookie"+(1!==s?"s":"")+" discovered. Redirecting to Cookie Manager...";y.textContent=k,l.appendChild(y),setTimeout(function(){window.location.href=t.adminUrl},1500)}).catch(function(e){y.disabled=!1,y.textContent="Finish Discovery",y.style.opacity="1",l.style.background="#d63638",u.style.color="#fff",u.textContent="Error: "+e.message})})}function x(){for(var t=function(){var t=[],n=e.getResourceSnapshot();e.setResourceSnapshot(n);for(var a=window.cookieflameAdmin&&window.cookieflameAdmin.serviceDetectors||[],r=[],c=0;c<a.length;c++)for(var s=a[c].scriptDomains||[],d=0;d<s.length;d++)r.push(s[d]);for(var l=0;l<a.length;l++){var m=a[l],g=m.name||"",p=i[g];if(p)try{if(p()){var f=o(g.toLowerCase().replace(/\s+/g,""));if(0===f.length&&-1!==g.indexOf(" "))for(var u=g.toLowerCase().split(" "),h=0;h<u.length&&0===f.length;h++)u[h].length>3&&(f=o(u[h]));t.push({name:g,cookies:m.cookies||[],canonicalDomain:m.canonicalDomain||"",scriptUrls:f})}}catch(e){}}t.pagePlugins=n.pluginScripts;for(var v=["wp.com","wordpress.com","wordpress.org","s.w.org","gravatar.com","cdnjs.cloudflare.com","cdn.jsdelivr.net","unpkg.com","ajax.googleapis.com","fonts.googleapis.com","fonts.gstatic.com","code.jquery.com","stackpath.bootstrapcdn.com","maxcdn.bootstrapcdn.com","www.google.com/recaptcha","www.gstatic.com","apis.google.com","maps.googleapis.com","maps.google.com","challenges.cloudflare.com","static.cloudflareinsights.com","chromium.org","mozilla.org","lazyload","rocket-loader","elementor.com"],y=n.thirdPartyDomains,k=0;k<y.length;k++){for(var w=y[k],x=!1,b=0;b<v.length;b++)if(w===v[b]||-1!==w.indexOf("."+v[b])||-1!==v[b].indexOf(w)){x=!0;break}if(!x){for(var _=!1,C=0;C<r.length;C++)if(-1!==w.indexOf(r[C])||-1!==r[C].indexOf(w)){_=!0;break}_||t.push({name:w,cookies:[],unknown:!0})}}return e.setResourceSnapshot(null),t}(),a=0;a<t.length;a++)n[t[a].name]||(n[t[a].name]=t[a]);if(t.pagePlugins&&t.pagePlugins.length){for(var r=n.__pagePlugins||{},c=0;c<t.pagePlugins.length;c++)r[t.pagePlugins[c].slug]=t.pagePlugins[c].url;n.__pagePlugins=r}!function(e){try{sessionStorage.setItem("cookieflame_discovery_services",JSON.stringify(e))}catch(e){}}(n),function(){var e=document.getElementById("cf-admin-discovery-count");if(e){for(var t=_(),o=(t.confirmed.length,t.existing.length,0),i={},a=Object.keys(n),r=[],c=[],s=0;s<a.length;s++){var d=n[a[s]];if(d.unknown)o++,c.push(a[s]),i[a[s]]=!0;else{r.push(a[s]);var l=d.cookies||[];o+=l.length;for(var m=0;m<l.length;m++)i[l[m]]=!0}}for(var g=t.confirmed.concat(t.existing),p=0;p<g.length;p++)i[g[p]]||o++;if(0===o)e.textContent="no cookies yet",e.title="Browse your site to discover cookies";else{e.textContent=o+" cookie"+(1!==o?"s":"")+" discovered";var f="";r.length>0&&(f+="Services: "+r.sort().join(", ")),c.length>0&&(f&&(f+="\n"),f+="Unknown scripts: "+c.sort().join(", ")),t.confirmed.length>0&&(f&&(f+="\n"),f+="New browser cookies: "+t.confirmed.join(", ")),e.title=f}}}()}function b(e){for(var t=0;t<d.length;t++)if(e===d[t]||0===e.indexOf(d[t]))return!0;return!1}function _(){for(var e=document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean),t=[],o=[],n={},i=Object.keys(a).length>0,r=0;r<e.length;r++){var c=e[r];n[c]||(n[c]=!0,b(c)||(i&&a[c]?o.push(c):t.push(c)))}return{confirmed:t.sort(),existing:o.sort()}}function C(){var e=document.getElementById("cc-overlay");e&&e.classList.remove("cc-show");var t=document.getElementById("cc-banner");t&&t.classList.remove("cc-show");var o=document.getElementById("cc-ccpa-banner");o&&o.classList.remove("cc-show");var n=document.getElementById("cc-icon");n&&n.classList.remove("cc-show")}}}();
     15!function(){"use strict";var e=window.__ccInternal;if(e){var o=e.scriptLoaded,t=e.scriptLoadedUrls,n=e.hasCookie,i={"Google Analytics":function(){return o("googletagmanager.com/gtag")||o("google-analytics.com")||o("analytics.google.com")||n("_ga")},"Google Ads":function(){return o("googleads.g.doubleclick.net")||o("googleadservices.com")||o("googlesyndication.com")||n("_gcl_au")||n("_gcl_aw")},"Google Tag Manager":function(){return o("googletagmanager.com/gtm.js")||!!window.google_tag_manager},"Microsoft Ads":function(){return o("bat.bing.com")||!!window.uetq||n("_uetsid")||n("_uetvid")},"Meta / Facebook":function(){return o("connect.facebook.net")||!!window.fbq||n("_fbp")||n("_fbc")},"Twitter / X":function(){return o("ads-twitter.com")||o("static.ads-twitter.com")||!!window.twq||n("_twpid")},LinkedIn:function(){return o("snap.licdn.com")||o("ads.linkedin.com")||!!window._linkedin_data_partner_ids||n("li_sugr")||n("bcookie")||n("lidc")},Pinterest:function(){return o("s.pinimg.com/ct")||o("pintrk")||!!window.pintrk||n("_pin_unauth")},Reddit:function(){return o("alb.reddit.com")||o("events.reddit.com")||!!window.rdt||n("_rdt_uuid")},TikTok:function(){return o("analytics.tiktok.com")||!!window.ttq}};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",a):setTimeout(a,100)}function a(){var o=window.__ccDiscovery;if(o&&o.active&&(!document.body||!document.body.classList.contains("wp-admin"))){var n=function(){try{var e=sessionStorage.getItem("cookieflame_discovery_services");return e?JSON.parse(e):{}}catch(e){return{}}}(),a={};try{var r=localStorage.getItem("cookieflame_discovery_before");if(r)for(var c=JSON.parse(r),s=0;s<c.length;s++)a[c[s]]=!0}catch(e){}var d=["wordpress_","wp-settings","wp_lang","PHPSESSID","cc_consent","cc_anon_id","cookieflame_","woocommerce_","wp_woocommerce_"],l=document.createElement("div");l.id="cf-admin-discovery-bar",l.style.cssText="position:fixed;top:0;left:0;right:0;z-index:999999;background:#1d2327;color:#f0f0f1;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:14px;display:flex;align-items:center;justify-content:space-between;padding:10px 20px;box-shadow:0 2px 8px rgba(0,0,0,.3);gap:16px;flex-wrap:wrap;";var m=document.createElement("div");m.style.cssText="display:flex;align-items:center;gap:12px;flex:1;min-width:200px;";var g=document.createElement("span");g.style.cssText="width:10px;height:10px;border-radius:50%;background:#00ba37;display:inline-block;flex-shrink:0;animation:cf-admin-pulse 2s infinite;";var p=document.createElement("span");p.textContent="Cookie Discovery Active",p.style.fontWeight="600";var f=document.createElement("span");f.id="cf-admin-discovery-count",f.style.cssText="background:#2271b1;color:#fff;padding:2px 10px;border-radius:12px;font-size:13px;font-weight:600;cursor:help;",f.textContent="scanning...",m.appendChild(g),m.appendChild(p),m.appendChild(f);var u=document.createElement("span");u.style.cssText="color:#a7aaad;font-size:13px;display:inline-flex;flex-direction:column;gap:2px;";var h=document.createElement("span");h.textContent="All cookies are temporarily accepted. Browse your pages, then click Finish.";var v=document.createElement("span");v.textContent="Tip: Browse by clicking links on the site. Typed URLs may skip discovery.",v.style.cssText="font-size:11px;opacity:0.75;",u.appendChild(h),u.appendChild(v),C(),setTimeout(C,500),setTimeout(C,2e3);var y=document.createElement("button");y.textContent="Finish Discovery",y.style.cssText="background:#00ba37;color:#fff;border:none;padding:8px 20px;border-radius:4px;font-size:14px;font-weight:600;cursor:pointer;white-space:nowrap;",y.onmouseover=function(){y.style.background="#009929"},y.onmouseout=function(){y.style.background="#00ba37"},l.appendChild(m),l.appendChild(u),l.appendChild(y);var k=document.createElement("style");k.textContent="@keyframes cf-admin-pulse{0%,100%{opacity:1}50%{opacity:.4}}#cf-admin-discovery-bar~*{margin-top:0!important;}body.admin-bar #cf-admin-discovery-bar{top:32px}@media screen and (max-width:782px){body.admin-bar #cf-admin-discovery-bar{top:46px}}",document.head.appendChild(k),document.body.insertBefore(l,document.body.firstChild),document.addEventListener("click",function(e){for(var o=e.target;o&&1===o.nodeType&&"A"!==o.tagName;)o=o.parentNode;if(o&&"A"===o.tagName){var t=o.getAttribute("target");if("_blank"!==t&&"_top"!==t&&"_parent"!==t){var n=o.getAttribute("rel")||"";if(!n||-1===n.split(/\s+/).indexOf("external")){var i=o.getAttribute("href");if(i){var a;try{a=new URL(i,location.href)}catch(e){return}"http:"!==a.protocol&&"https:"!==a.protocol||a.origin===location.origin&&(a.pathname===location.pathname&&a.search===location.search&&a.hash!==location.hash||a.searchParams.has("cookieflame")||(a.searchParams.set("cookieflame","discover"),o.setAttribute("href",a.toString())))}}}}},!0),setTimeout(x,5e3);var w=setInterval(x,3e3);y.addEventListener("click",function(){y.disabled=!0,y.textContent="Finishing...",y.style.opacity="0.7",clearInterval(w),x();var e=n,t=window.__ccConfig||{};try{localStorage.setItem("cookieflame_discovery_results",JSON.stringify(e))}catch(o){var i=encodeURIComponent(JSON.stringify(e)),a=t.cookieDomain?"; domain="+t.cookieDomain:"";document.cookie="cookieflame_discovery_results="+i+"; path=/; max-age=300"+a}fetch(o.finishUrl,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","X-WP-Nonce":o.nonce},body:"{}"}).then(function(e){if(403===e.status)throw new Error("Your session has expired. Please reload the page and try again.");return e.json()}).then(function(e){var i=[location.hostname,"."+location.hostname];t.cookieDomain&&i.push(t.cookieDomain);for(var a=0;a<i.length;a++)document.cookie="cc_consent=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i[a];if(document.cookie="cc_consent=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/",e.restore_dnt){var r=t.cookieDomain?"; domain="+t.cookieDomain:"";document.cookie="cookieflame_dnt=1; path=/; max-age=31536000"+r}try{sessionStorage.removeItem("cookieflame_discovery_services")}catch(e){}for(var c=_(),s=0,d={},m=Object.keys(n),g=0;g<m.length;g++){var p=n[m[g]];if(p.unknown)s++,d[m[g]]=!0;else{var f=p.cookies||[];s+=f.length;for(var u=0;u<f.length;u++)d[f[u]]=!0}}for(var h=c.confirmed.concat(c.existing),v=0;v<h.length;v++)d[h[v]]||s++;l.style.background="#00ba37",l.innerHTML="";var y=document.createElement("span");y.style.cssText="font-weight:600;flex:1;text-align:center;";var k=s+" cookie"+(1!==s?"s":"")+" discovered. Redirecting to Cookie Manager...";y.textContent=k,l.appendChild(y),setTimeout(function(){window.location.href=o.adminUrl},1500)}).catch(function(e){y.disabled=!1,y.textContent="Finish Discovery",y.style.opacity="1",l.style.background="#d63638",u.style.color="#fff",u.textContent="Error: "+e.message})})}function x(){for(var o=function(){var o=[],n=e.getResourceSnapshot();e.setResourceSnapshot(n);for(var a=window.cookieflameAdmin&&window.cookieflameAdmin.serviceDetectors||[],r=[],c=0;c<a.length;c++)for(var s=a[c].scriptDomains||[],d=0;d<s.length;d++)r.push(s[d]);for(var l=0;l<a.length;l++){var m=a[l],g=m.name||"",p=i[g];if(p)try{if(p()){var f=t(g.toLowerCase().replace(/\s+/g,""));if(0===f.length&&-1!==g.indexOf(" "))for(var u=g.toLowerCase().split(" "),h=0;h<u.length&&0===f.length;h++)u[h].length>3&&(f=t(u[h]));o.push({name:g,cookies:m.cookies||[],canonicalDomain:m.canonicalDomain||"",scriptUrls:f})}}catch(e){}}o.pagePlugins=n.pluginScripts;for(var v=["wp.com","wordpress.com","wordpress.org","s.w.org","gravatar.com","cdnjs.cloudflare.com","cdn.jsdelivr.net","unpkg.com","ajax.googleapis.com","fonts.googleapis.com","fonts.gstatic.com","code.jquery.com","stackpath.bootstrapcdn.com","maxcdn.bootstrapcdn.com","www.google.com/recaptcha","www.gstatic.com","apis.google.com","maps.googleapis.com","maps.google.com","challenges.cloudflare.com","static.cloudflareinsights.com","chromium.org","mozilla.org","lazyload","rocket-loader","elementor.com"],y=n.thirdPartyDomains,k=0;k<y.length;k++){for(var w=y[k],x=!1,b=0;b<v.length;b++)if(w===v[b]||-1!==w.indexOf("."+v[b])||-1!==v[b].indexOf(w)){x=!0;break}if(!x){for(var _=!1,C=0;C<r.length;C++)if(-1!==w.indexOf(r[C])||-1!==r[C].indexOf(w)){_=!0;break}_||o.push({name:w,cookies:[],unknown:!0})}}return e.setResourceSnapshot(null),o}(),a=0;a<o.length;a++)n[o[a].name]||(n[o[a].name]=o[a]);if(o.pagePlugins&&o.pagePlugins.length){for(var r=n.__pagePlugins||{},c=0;c<o.pagePlugins.length;c++)r[o.pagePlugins[c].slug]=o.pagePlugins[c].url;n.__pagePlugins=r}!function(e){try{sessionStorage.setItem("cookieflame_discovery_services",JSON.stringify(e))}catch(e){}}(n),function(){var e=document.getElementById("cf-admin-discovery-count");if(e){for(var o=_(),t=(o.confirmed.length,o.existing.length,0),i={},a=Object.keys(n),r=[],c=[],s=0;s<a.length;s++){var d=n[a[s]];if(d.unknown)t++,c.push(a[s]),i[a[s]]=!0;else{r.push(a[s]);var l=d.cookies||[];t+=l.length;for(var m=0;m<l.length;m++)i[l[m]]=!0}}for(var g=o.confirmed.concat(o.existing),p=0;p<g.length;p++)i[g[p]]||t++;if(0===t)e.textContent="no cookies yet",e.title="Browse your site to discover cookies";else{e.textContent=t+" cookie"+(1!==t?"s":"")+" discovered";var f="";r.length>0&&(f+="Services: "+r.sort().join(", ")),c.length>0&&(f&&(f+="\n"),f+="Unknown scripts: "+c.sort().join(", ")),o.confirmed.length>0&&(f&&(f+="\n"),f+="New browser cookies: "+o.confirmed.join(", ")),e.title=f}}}()}function b(e){for(var o=0;o<d.length;o++)if(e===d[o]||0===e.indexOf(d[o]))return!0;return!1}function _(){for(var o=document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean),t=[],n=[],i={},r=Object.keys(a).length>0,c=e.visitorCookies?e.visitorCookies(o):{},s=window.cookieflameAdmin&&window.cookieflameAdmin.ignoredCookieNames||[],d=0;d<o.length;d++){var l=o[d];i[l]||(i[l]=!0,b(l)||c[l]||-1!==s.indexOf(l)||(r&&a[l]?n.push(l):t.push(l)))}return{confirmed:t.sort(),existing:n.sort()}}function C(){var e=document.getElementById("cc-overlay");e&&e.classList.remove("cc-show");var o=document.getElementById("cc-banner");o&&o.classList.remove("cc-show");var t=document.getElementById("cc-ccpa-banner");t&&t.classList.remove("cc-show");var n=document.getElementById("cc-icon");n&&n.classList.remove("cc-show")}}}();
  • cookieflame/trunk/assets/js/cookieconsent.js

    r3768250 r3769546  
    11/**
    2  * CookieFlame v5.2.0
     2 * CookieFlame v5.3.0
    33 * ─────────────────────────────────────────────────────────────
    44 * Self-hosted cookie consent manager with Google Consent Mode v2,
    … …  
    4040  } catch (e) {}
    4141
    42   var PLUGIN_VERSION = "5.2.0";
     42  var PLUGIN_VERSION = "5.3.0";
    4343
    4444  /* ═══════════════════════════════════════════
    … …  
    14001400    hasCookiePrefix:     hasCookiePrefix,
    14011401    setResourceSnapshot: function (s) { _resourceSnapshot = s; },
     1402    // Discovery bar (v5.3.0): which of these cookies came with the browser,
     1403    // by the same check passive monitoring uses, so its count matches the
     1404    // Cookie Manager.
     1405    visitorCookies: function (names) {
     1406      var domains = getResourceSnapshot().thirdPartyDomains, out = {};
     1407      for (var i = 0; i < names.length; i++) {
     1408        if (isVisitorCookie(names[i], domains, cookieSetter(names[i]))) out[names[i]] = true;
     1409      }
     1410      return out;
     1411    },
    14021412    // Facade module bridge (v4.41.0+). The facade module reads this to
    14031413    // access core state it needs for overlay rendering, and registers
    … …  
    18981908    try {
    18991909      var url = new URL(String(setters[name]).replace(/:\d+(:\d+)?$/, ""));
     1910      if (/^(chrome-extension|moz-extension|safari-web-extension|safari-extension):$/.test(url.protocol)) {
     1911        out.set_by = "browser-extension";
     1912        return out;
     1913      }
    19001914      var host = url.hostname;
    19011915      if (host === location.hostname || host === "www." + location.hostname || "www." + host === location.hostname) {
    … …  
    19081922    } catch (e) {}
    19091923    return out;
     1924  }
     1925
     1926  /**
     1927   * Whether a cookie came with the visitor's browser rather than this site:
     1928   * written by a browser extension, or another consent tool's cookie when
     1929   * that tool isn't on the page (rules from window.__ccVisitorCookieRules,
     1930   * limited to tools whose WordPress plugin isn't active). Skipping these
     1931   * keeps the page's beacon allowance for real cookies; the server makes
     1932   * the same check and records them on the ignore list.
     1933   */
     1934  function isVisitorCookie(name, domains, setter) {
     1935    if (setter.set_by === "browser-extension") return true;
     1936    var rules = window.__ccVisitorCookieRules;
     1937    if (!rules || !rules.tools) return false;
     1938    if (rules.allow && rules.allow.indexOf(name) !== -1) return false;
     1939    if (setter.set_by) return false; // A page script wrote it, so the tool runs here.
     1940    for (var t = 0; t < rules.tools.length; t++) {
     1941      var tool = rules.tools[t], hit = (tool.n || []).indexOf(name) !== -1;
     1942      for (var p = 0; !hit && p < (tool.p || []).length; p++) {
     1943        if (name.indexOf(tool.p[p]) === 0) hit = true;
     1944      }
     1945      if (!hit) continue;
     1946      for (var d = 0; d < domains.length; d++) {
     1947        for (var k = 0; k < (tool.d || []).length; k++) {
     1948          var host = String(domains[d]).toLowerCase(), dom = tool.d[k];
     1949          if (host === dom || host.slice(-dom.length - 1) === "." + dom) return false;
     1950        }
     1951      }
     1952      return true;
     1953    }
     1954    return false;
    19101955  }
    19111956
    … …  
    19882033
    19892034      var setter = cookieSetter(name);
     2035      // Visitors' browsers don't report these. During Cookie Discovery they
     2036      // are reported anyway, so the server records them on the Permanently
     2037      // Ignored list with the reason and the admin sees what was set aside.
     2038      if (isVisitorCookie(name, scriptDomains, setter) && !(window.__ccDiscovery && window.__ccDiscovery.active)) {
     2039        beaconed[name] = true;
     2040        continue;
     2041      }
    19902042
    19912043      var payload = JSON.stringify({
  • cookieflame/trunk/assets/js/cookieconsent.min.js

    r3768250 r3769546  
    11/**
    2  * CookieFlame v5.2.0
     2 * CookieFlame v5.3.0
    33 * ─────────────────────────────────────────────────────────────
    44 * Self-hosted cookie consent manager with Google Consent Mode v2,
    … …  
    1717 *   • Timezone-based geo heuristic with server-side fallback
    1818 */
    19 !function(){"use strict";var e={};try{for(var t=document.cookie.split(";"),n=0;n<t.length;n++){var o=t[n].trim().split("=")[0];o&&(e[o]=!0)}}catch(e){}var c="5.2.0",r={cookieName:"cc_consent",cookieExpiry:365,privacyUrl:"/privacy-policy",ccpaUrl:"/privacy-policy#do-not-sell",geoApi:"",logEndpoint:"",reconsentDays:365,cspSafe:!1,tcfEnabled:!0,cmpId:1,cmpVersion:1,vendorListVersion:2,storageMethod:"cookie",cookieDomain:"",shareSubdomains:!1,theme:"light",showPoweredBy:!1,iconPosition:"left",consentVersion:"",autoBlockPatterns:[{pattern:"googletagmanager.com/gtag",category:"analytics"},{pattern:"google-analytics.com",category:"analytics"},{pattern:"analytics.google.com",category:"analytics"},{pattern:"hotjar.com",category:"analytics"},{pattern:"mixpanel.com",category:"analytics"},{pattern:"segment.com/analytics",category:"analytics"},{pattern:"segment.io",category:"analytics"},{pattern:"clarity.ms",category:"analytics"},{pattern:"plausible.io",category:"analytics"},{pattern:"js.hs-analytics.net",category:"analytics"},{pattern:"connect.facebook.net",category:"marketing"},{pattern:"snap.licdn.com",category:"marketing"},{pattern:"ads.linkedin.com",category:"marketing"},{pattern:"googleads.g.doubleclick.net",category:"marketing"},{pattern:"googlesyndication.com",category:"marketing"},{pattern:"googleadservices.com",category:"marketing"},{pattern:"ads-twitter.com",category:"marketing"},{pattern:"analytics.tiktok.com",category:"marketing"},{pattern:"pintrk",category:"marketing"},{pattern:"s.pinimg.com/ct",category:"marketing"},{pattern:"widget.intercom.io",category:"preferences"},{pattern:"js.intercomcdn.com",category:"preferences"}],regionRules:{gdpr:["AT","BE","BG","CY","CZ","DE","DK","EE","ES","FI","FR","GR","HR","HU","IE","IT","LT","LU","LV","MT","NL","PL","PT","RO","SE","SI","SK","IS","LI","NO","GB","GG","JE","IM","CH","BR","ZA"],ccpa:["US"]},text:{title:"We value your privacy",description:"We use cookies to improve your experience, analyse traffic and personalise content. You can choose which categories to allow.",acceptAllButton:"Accept All",declineAllButton:"Decline All",saveButton:"Save Preferences",showDetailsButton:"Manage preferences",hideDetailsButton:"Hide preferences",alwaysOnLabel:"(always on)",ccpaTitle:"Your Privacy Choices",ccpaDescription:"We use cookies and similar technologies. Under California law, you have the right to opt out of the sale or sharing of your personal information.",ccpaAcceptButton:"OK",ccpaDnsmpiLink:"Do Not Sell My Personal Information"},cookieTypes:[{id:"necessary",name:"Necessary",description:"Required for the site to function. Cannot be disabled.",required:!0,consentMode:["security_storage","functionality_storage"],tcfPurposes:[1],onAccept:function(){}},{id:"preferences",name:"Preferences",description:"Remember your settings and personalisation choices.",required:!1,defaultValue:!1,consentMode:["functionality_storage","personalization_storage"],tcfPurposes:[2,3],onAccept:function(){},onReject:function(){}},{id:"analytics",name:"Analytics",description:"Help us understand how visitors use the site.",required:!1,defaultValue:!1,consentMode:["analytics_storage"],tcfPurposes:[4,5,6],onAccept:function(){},onReject:function(){}},{id:"marketing",name:"Marketing",description:"Used to deliver relevant ads and track campaigns.",required:!1,defaultValue:!1,consentMode:["ad_storage","ad_user_data","ad_personalization"],tcfPurposes:[7,8,9,10],onAccept:function(){},onReject:function(){}}]};if(window.__ccConfig&&"object"==typeof window.__ccConfig){var a=window.__ccConfig;["cookieName","cookieExpiry","privacyUrl","ccpaUrl","geoApi","logEndpoint","reconsentDays","cspSafe","tcfEnabled","cmpId","cmpVersion","vendorListVersion","storageMethod","cookieDomain","shareSubdomains","theme","showPoweredBy","iconPosition","consentVersion"].forEach(function(e){void 0!==a[e]&&(r[e]=a[e])}),a.text&&"object"==typeof a.text&&Object.keys(a.text).forEach(function(e){r.text[e]=a.text[e]}),Array.isArray(a.cookieTypes)&&(r.cookieTypes=a.cookieTypes),Array.isArray(a.autoBlockPatterns)&&(r.autoBlockPatterns=a.autoBlockPatterns),a.regionRules&&(r.regionRules=a.regionRules)}window.__ccConsentVersion&&(r.consentVersion=window.__ccConsentVersion);var i=!!window.__ccGPC;window.__ccBlockRules&&Array.isArray(window.__ccBlockRules)&&window.__ccBlockRules.length&&(r.autoBlockPatterns=window.__ccBlockRules);var s={cc_consent:{vendor:"CookieFlame",category:"necessary",purpose:"Stores your consent preferences",expiry:"1 year"},cc_anon_id:{vendor:"CookieFlame",category:"necessary",purpose:"Anonymous ID for consent logging",expiry:"1 year"},cookieflame_dnt:{vendor:"CookieFlame",category:"necessary",purpose:"Do-not-track opt-out flag",expiry:"1 year"},PHPSESSID:{vendor:"PHP",category:"necessary",purpose:"Server-side session identifier",expiry:"Session"},wp_settings_:{vendor:"WordPress",category:"necessary",purpose:"Admin interface preferences",prefix:!0,expiry:"1 year"},"wp-settings-":{vendor:"WordPress",category:"necessary",purpose:"Admin interface preferences",prefix:!0,expiry:"1 year"},"wp-settings-time-":{vendor:"WordPress",category:"necessary",purpose:"Admin settings timestamp",prefix:!0,expiry:"1 year"},wordpress_logged_in_:{vendor:"WordPress",category:"necessary",purpose:"Login session",prefix:!0,expiry:"Session"},wordpress_sec_:{vendor:"WordPress",category:"necessary",purpose:"Secure login session",prefix:!0,expiry:"Session"},wordpress_test_cookie:{vendor:"WordPress",category:"necessary",purpose:"Checks cookie support",expiry:"Session"},wp_lang:{vendor:"WordPress",category:"necessary",purpose:"Language preference",expiry:"Session"},woocommerce_cart_hash:{vendor:"WooCommerce",category:"necessary",purpose:"Shopping cart contents",expiry:"Session"},woocommerce_items_in_cart:{vendor:"WooCommerce",category:"necessary",purpose:"Cart item count",expiry:"Session"},wp_woocommerce_session_:{vendor:"WooCommerce",category:"necessary",purpose:"Shopping session",prefix:!0,expiry:"2 days"},_ga:{vendor:"Google Analytics",category:"analytics",purpose:"Distinguishes visitors",expiry:"2 years"},_ga_:{vendor:"Google Analytics",category:"analytics",purpose:"Stores session state",prefix:!0,expiry:"2 years"},_gid:{vendor:"Google Analytics",category:"analytics",purpose:"Distinguishes visitors (24h)",expiry:"24 hours"},_gat:{vendor:"Google Analytics",category:"analytics",purpose:"Throttles request rate",expiry:"1 minute"},wp_wk_ga_untrack_:{vendor:"Google Analytics",category:"analytics",purpose:"GA opt-out flag",prefix:!0,expiry:"Persistent"},_gcl_au:{vendor:"Google Ads",category:"marketing",purpose:"Conversion linker",expiry:"90 days"},_gcl_aw:{vendor:"Google Ads",category:"marketing",purpose:"Stores ad click info",expiry:"90 days"},_gac_:{vendor:"Google Ads",category:"marketing",purpose:"Campaign information",prefix:!0,expiry:"90 days"},IDE:{vendor:"Google DoubleClick",category:"marketing",purpose:"Ad serving and frequency",expiry:"1 year"},test_cookie:{vendor:"Google DoubleClick",category:"marketing",purpose:"Checks cookie support",expiry:"15 minutes"},NID:{vendor:"Google",category:"preferences",purpose:"Stores preferences",expiry:"6 months"}},d=window.__ccCustomCookies||[];function u(e){for(var t=0;t<d.length;t++){var n=d[t];if("exact"===n.match&&e===n.name)return n;if("prefix"===n.match&&0===e.indexOf(n.name))return n;if("contains"===n.match&&-1!==e.indexOf(n.name))return n}var o=s[e];return o||(Object.keys(s).forEach(function(t){!o&&s[t].prefix&&0===e.indexOf(t)&&(o=s[t])}),o||null)}var l=null,p="none",f=null,m=null,g=!1,y=0;function v(e){return"string"!=typeof e&&(e=String(e)),e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function h(e){if("localStorage"===r.storageMethod)try{return localStorage.getItem(e)}catch(e){return null}var t=document.cookie.match(new RegExp("(?:^|;\\s*)"+e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"=([^;]*)"));if(!t)return null;try{return decodeURIComponent(t[1])}catch(e){return null}}function w(e,t,n){if("localStorage"!==r.storageMethod){var o=new Date;o.setTime(o.getTime()+864e5*n);var c=e+"="+encodeURIComponent(t)+";expires="+o.toUTCString()+";path=/;SameSite=Lax;Secure";r.cookieDomain&&(c+=";domain="+r.cookieDomain),document.cookie=c}else try{localStorage.setItem(e,t)}catch(e){}}function b(){if(m)return m;var e=h("cc_anon_id");return e?(m=e,e):(w("cc_anon_id",m="cc_"+function(){if("undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID)try{return crypto.randomUUID()}catch(e){}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}(),730),m)}function k(){var e={timestamp:l?l.ts:null,complianceMode:p,country:f,gpc:i,consentVersion:r.consentVersion};return r.cookieTypes.forEach(function(t){e[t.id]=l?!!l[t.id]:!!t.required}),e}function _(e,t){window.dispatchEvent(new CustomEvent(e,{detail:t||k()}))}function x(){window.dataLayer=window.dataLayer||[],window.dataLayer.push(arguments)}function E(e){var t={};r.cookieTypes.forEach(function(n){n.consentMode&&n.consentMode.forEach(function(o){t[o]=e[n.id]?"granted":"denied"})}),i&&(t.ad_storage="denied",t.ad_user_data="denied",t.ad_personalization="denied"),x("consent","update",t)}var A=new Map,S=!1;function L(e){var t={};return r.cookieTypes.forEach(function(n){n.tcfPurposes&&n.tcfPurposes.forEach(function(o){t[o]=!!e[n.id]})}),{tcString:"",tcfPolicyVersion:4,cmpId:r.cmpId,cmpVersion:r.cmpVersion,gdprApplies:"gdpr"===p,eventStatus:"useractioncomplete",cmpStatus:"loaded",listenerId:void 0,isServiceSpecific:!0,useNonStandardTexts:!1,publisherCC:"GB",purposeOneTreatment:!1,purpose:{consents:t,legitimateInterests:{}},vendor:{consents:{},legitimateInterests:{}},specialFeatureOptins:{},publisher:{consents:{},legitimateInterests:{},customPurpose:{consents:{},legitimateInterests:{}},restrictions:{}}}}function C(e,t,n,o){if("function"==typeof n)if("addEventListener"===e){var c=++y;if(A.set(c,n),l){var a=L(l);a.listenerId=c,n(a,!0)}}else"removeEventListener"===e?n({listenerId:o},A.delete(o)):"getTCData"===e?n(l?L(l):null,!!l):"ping"===e?n({gdprApplies:"gdpr"===p,cmpLoaded:S,cmpStatus:S?"loaded":"stub",displayStatus:"hidden",apiVersion:"2.2",cmpVersion:r.cmpVersion,cmpId:r.cmpId,gvlVersion:r.vendorListVersion,tcfPolicyVersion:4},!0):n(null,!1)}function B(e){A.forEach(function(t,n){var o=L(e);o.listenerId=n;try{t(o,!0)}catch(e){}})}function I(e){if(!e)return null;for(var t=e.toLowerCase(),n=0;n<r.autoBlockPatterns.length;n++){var o=r.autoBlockPatterns[n];if(o.pattern instanceof RegExp){if(o.pattern.test(t))return o.category}else if(-1!==t.indexOf(o.pattern.toLowerCase()))return o.category}return null}function T(e){document.querySelectorAll('script[type="text/plain"][data-cc]').forEach(function(t){var n=(t.getAttribute("data-cc")||"").toLowerCase();if(e[n]&&!t.getAttribute("data-cc-activated")){t.setAttribute("data-cc-activated","1");var o=document.createElement("script");o.isWPRocket=!0,Array.from(t.attributes).forEach(function(e){"type"!==e.name&&"data-cc"!==e.name&&"data-cc-activated"!==e.name&&"data-cc-auto"!==e.name&&o.setAttribute(e.name,e.value)}),!t.src&&t.textContent&&(o.textContent=t.textContent),t.parentNode.insertBefore(o,t.nextSibling),t.parentNode.removeChild(t)}}),["img","iframe","link"].forEach(function(t){document.querySelectorAll(t+"[data-cc][data-cc-src]").forEach(function(n){var o=(n.getAttribute("data-cc")||"").toLowerCase();e[o]&&!n.getAttribute("data-cc-activated")&&(n.setAttribute("data-cc-activated","1"),"link"===t?n.href=n.getAttribute("data-cc-src"):n.src=n.getAttribute("data-cc-src"))})})}var O=[{pattern:"youtube.com",category:"marketing"},{pattern:"youtube-nocookie.com",category:"marketing"},{pattern:"youtu.be",category:"marketing"},{pattern:"player.vimeo.com",category:"marketing"},{pattern:"vimeo.com",category:"marketing"},{pattern:"dailymotion.com",category:"marketing"},{pattern:"open.spotify.com",category:"preferences"},{pattern:"soundcloud.com",category:"preferences"},{pattern:"google.com/maps",category:"preferences"},{pattern:"maps.google.com",category:"preferences"},{pattern:"facebook.com/plugins",category:"marketing"},{pattern:"instagram.com",category:"marketing"},{pattern:"tiktok.com/embed",category:"marketing"},{pattern:"twitter.com",category:"marketing"},{pattern:"x.com",category:"marketing"}];function P(){return!(!document.body||!(document.body.classList.contains("elementor-editor-active")||document.body.classList.contains("fl-builder-edit")||document.body.classList.contains("et-fb-root-ancestor")))}var D=[],M=0,R=null,q=!1;function V(){if(R&&D.length){var e=D;D=[];try{R(e)}catch(e){}}}function N(){0!==D.length&&(2!==M?1!==M&&3!==M&&(M=1,function(){var e=window.__ccConfig&&window.__ccConfig.facadesUrl||null;if(!e)return void(M=3);var t=document.createElement("script");t.src=e,t.async=!0,t.onload=function(){M=2,V()},t.onerror=function(){M=3},(document.head||document.documentElement).appendChild(t)}()):V())}function U(e){var t=e.src||e.getAttribute("data-src")||"";if(t&&!e.getAttribute("data-cc-facade")&&!P()){var n=function(e){if(!e)return null;for(var t=e.toLowerCase(),n=0;n<O.length;n++)if(-1!==t.indexOf(O[n].pattern))return O[n].category;return null}(t);if(n){var o=l||$();o&&o[n]||(e.setAttribute("data-cc-facade","1"),e.setAttribute("data-cc-iframe-cat",n),e.setAttribute("data-cc-original-src",t),e.removeAttribute("src"),e.style.display="none",D.push({type:"iframe",node:e,category:n,src:t}),N())}}}function G(e){if(!e.getAttribute("data-cc-facade")&&!P()){var t=e.getAttribute("data-settings");if(t){var n;try{n=JSON.parse(t)}catch(e){return}var o=n.youtube_url||n.vimeo_url||"";if(o){var c=o.toLowerCase(),r=null;if((-1!==c.indexOf("youtube")||-1!==c.indexOf("youtu.be")||-1!==c.indexOf("vimeo")||-1!==c.indexOf("dailymotion"))&&(r="marketing"),r&&(!l||!l[r])){if(!l){var a=$();if(a&&a[r])return}e.setAttribute("data-cc-facade","1"),q=!0,D.push({type:"elementor",node:e,category:r,url:o}),N()}}}}}function j(){for(var e=document.querySelectorAll("iframe[src]"),t=0;t<e.length;t++)U(e[t]);for(var n=document.querySelectorAll(".elementor-widget-video"),o=0;o<n.length;o++)G(n[o]);for(var c=document.querySelectorAll(".wp-block-embed__wrapper iframe[src]"),r=0;r<c.length;r++)U(c[r])}function J(){new MutationObserver(function(e){e.forEach(function(e){e.addedNodes.forEach(function(e){if(1===e.nodeType){if("IFRAME"===e.tagName&&U(e),e.querySelectorAll){for(var t=e.querySelectorAll("iframe[src]"),n=0;n<t.length;n++)U(t[n]);for(var o=e.querySelectorAll(".elementor-widget-video"),c=0;c<o.length;c++)G(o[c])}e.classList&&e.classList.contains("elementor-widget-video")&&G(e)}})})}).observe(document.documentElement,{childList:!0,subtree:!0})}function W(){for(var e=document.querySelectorAll("iframe[src]"),t=0;t<e.length;t++){var n=e[t];if(!n.getAttribute("data-cc-facade")){var o=n.src||"";if(o)for(var c=0;c<O.length;c++)if(-1!==o.indexOf(O[c].pattern)){n.src=o;break}}}}function H(e,t){r.cookieTypes.forEach(function(n){var o=!!e[n.id];if((t?!!t[n.id]:null)!==o||null===t)if(o&&"function"==typeof n.onAccept)try{n.onAccept()}catch(e){console.warn("[CookieConsent] onAccept error for "+n.id,e)}else if(!o&&"function"==typeof n.onReject)try{n.onReject()}catch(e){console.warn("[CookieConsent] onReject error for "+n.id,e)}})}function F(e,t){var n=l;if(l=e,w(r.cookieName,JSON.stringify(e),r.cookieExpiry),E(e),q&&t)for(var o=document.querySelectorAll(".cc-iframe-facade[data-cc-elementor-widget]"),a=0;a<o.length;a++){var s=o[a].getAttribute("data-cc-iframe-cat");if(s&&e[s])return void window.location.reload()}z(e,n),T(e),function(e){for(var t=document.querySelectorAll(".cc-iframe-facade"),n=0;n<t.length;n++)if(!t[n].getAttribute("data-cc-elementor-widget")&&e[t[n].getAttribute("data-cc-iframe-cat")]){var o=t[n].nextElementSibling;if(o&&"IFRAME"===o.tagName&&o.getAttribute("data-cc-facade")){var c=o.getAttribute("data-cc-original-src");c&&(o.src=c,o.style.display="",o.removeAttribute("data-cc-facade"))}t[n].parentNode.removeChild(t[n])}}(e),t&&"auto_grant_non_eu"!==t&&"ccpa_auto_grant"!==t&&setTimeout(W,300),H(e,n),Z(e),r.tcfEnabled&&B(e),t&&function(e,t){if(r.logEndpoint){var n={};r.cookieTypes.forEach(function(e){n[e.id]=!!t[e.id]});var o={anonymousId:b(),timestamp:(new Date).toISOString(),action:e,consent:n,complianceMode:p,country:f,url:location.href,userAgent:navigator.userAgent,tcfEnabled:r.tcfEnabled,gpc:i,consentVersion:r.consentVersion,version:c};try{var a=JSON.stringify(o);navigator.sendBeacon?navigator.sendBeacon(r.logEndpoint,new Blob([a],{type:"application/json"})):fetch(r.logEndpoint,{method:"POST",body:a,headers:{"Content-Type":"application/json"},keepalive:!0})}catch(e){}}}(t,e)}function Z(e){r.cookieTypes.forEach(function(t){if(!t.required){var n=document.getElementById("cc-cb-"+t.id);n&&(n.checked=!!e[t.id])}});var t=document.getElementById("cc-cats"),n=document.getElementById("cc-details"),o=document.getElementById("cc-pref-buttons");t&&t.classList.remove("cc-open"),n&&(n.textContent=r.text.showDetailsButton),o&&o.classList.remove("cc-open")}function $(){var e=h(r.cookieName);if(!e)return null;try{var t=JSON.parse(e);return!t||"object"!=typeof t||Array.isArray(t)||(!t.ts||isNaN(new Date(t.ts).getTime())||(Date.now()-new Date(t.ts).getTime())/864e5>r.reconsentDays||r.consentVersion&&t.cv!==r.consentVersion)?null:t}catch(e){return null}}function Y(){var e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f};return r.cookieTypes.forEach(function(t){e[t.id]=!0}),e}function K(){var e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f};return r.cookieTypes.forEach(function(t){e[t.id]=!!t.required}),e}function z(e,t){if(t){var n=[];if(r.cookieTypes.forEach(function(o){o.required||t[o.id]&&!e[o.id]&&n.push(o.id)}),n.length)document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean).forEach(function(e){var t=u(e);if(t&&-1!==n.indexOf(t.category)){var o=location.hostname,c="."+o;document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/",document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT",document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+c,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+o,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; Secure",r.cookieDomain&&(document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+r.cookieDomain,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+r.cookieDomain+"; Secure");var a=o.split(".");if(a.length>2){var i="."+a.slice(1).join(".");document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i+"; Secure"}}})}}function Q(e){return(f=e?e.toUpperCase():null)?r.regionRules.gdpr&&-1!==r.regionRules.gdpr.indexOf(f)?"gdpr":r.regionRules.ccpa&&-1!==r.regionRules.ccpa.indexOf(f)?"ccpa":"none":"gdpr"}function X(e){if(window.__ccGeo)return e(Q(window.__ccGeo));r.geoApi?fetch(r.geoApi).then(function(e){return e.json()}).then(function(t){var n=t.country_code||t.country||"";n?e(Q(n)):ee(e)}).catch(function(){ee(e)}):ee(e)}function ee(e){var t="";try{t=Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch(e){}return 0===t.indexOf("Europe/")||"Atlantic/Reykjavik"===t?(f=null,e("gdpr")):0===t.indexOf("US/")||0===t.indexOf("America/")&&["New_York","Chicago","Denver","Los_Angeles","Phoenix","Anchorage","Boise","Detroit","Indianapolis","Louisville","Menominee","Nome","Adak","Juneau","Sitka","Yakutat","Metlakatla","Kentucky","Indiana","North_Dakota"].some(function(e){return-1!==t.indexOf(e)})||"Pacific/Honolulu"===t?(f="US",e("ccpa")):(f=null,void e("gdpr"))}function te(){"dark"===r.theme&&document.documentElement.classList.add("cc-dark"),"right"===r.iconPosition&&document.documentElement.classList.add("cc-icon-right")}function ne(){var e=r.text,t=document.createElement("div");t.id="cc-overlay",document.body.appendChild(t);var n=document.createElement("div");n.id="cc-banner",n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-label","Cookie consent"),n.setAttribute("tabindex","-1");var o=r.cookieTypes.map(function(t){var n=t.required?' <span class="cc-always-on">'+v(e.alwaysOnLabel)+"</span>":"";return'<div class="cc-cat"><div><div class="cc-cat-label">'+v(t.name)+n+'</div><div class="cc-cat-desc">'+v(t.description||"")+'</div></div><label class="cc-toggle"><input type="checkbox" id="cc-cb-'+v(t.id)+'" aria-label="'+v(t.name)+'"'+(t.required||t.defaultValue?" checked":"")+(t.required?" disabled":"")+'><span class="cc-slider"></span></label></div>'}).join("");n.innerHTML='<div class="cc-inner"><p class="cc-title">'+v(e.title)+'</p><p class="cc-text">'+v(e.description)+' <a href="'+v(r.privacyUrl)+'">Privacy Policy</a></p><div class="cc-buttons"><button class="cc-btn cc-btn-accept" id="cc-accept">'+v(e.acceptAllButton)+'</button><button class="cc-btn cc-btn-details" id="cc-details" aria-expanded="false">'+v(e.showDetailsButton)+'</button></div><div class="cc-categories" id="cc-cats">'+o+'</div><div class="cc-pref-buttons" id="cc-pref-buttons"><button class="cc-btn cc-btn-save" id="cc-save">'+v(e.saveButton)+'</button><button class="cc-btn cc-btn-decline" id="cc-decline">'+v(e.declineAllButton)+"</button></div></div>"+(r.showPoweredBy?'<div class="cc-powered"><a href="https://www.cookieflame.com" target="_blank" rel="noopener">Powered by CookieFlame</a></div>':""),document.body.appendChild(n);var c=document.createElement("button");c.id="cc-icon",c.setAttribute("aria-label","Cookie settings"),c.innerHTML='<svg width="24" height="24" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M19.1 1.16c-.06-.42-.38-.77-.8-.83A11.8 11.8 0 0 0 13.28 1.2L7.5 4.3A11.8 11.8 0 0 0 3.95 7.98L1.09 13.9A11.8 11.8 0 0 0 .38 18.96l1.13 6.47a11.8 11.8 0 0 0 3.53 4.99l4.71 4.56a11.8 11.8 0 0 0 4.59 2.24l6.48.91a11.8 11.8 0 0 0 5.52-.89l5.78-3.09a11.8 11.8 0 0 0 3.55-3.69l2.87-5.9a11.8 11.8 0 0 0 .71-5.08c-.07-.41-.42-.73-.83-.79a14.8 14.8 0 0 1-8.16-7.61c-.14-.59-.63-1.05-1.23-1.14A14.8 14.8 0 0 1 19.1 1.16ZM15.25 10.25a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm-5 15a2.5 2.5 0 1 1 5 0 2.5 2.5 0 0 1-5 0Zm17.5-5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Z" fill="currentColor"/></svg>',document.body.appendChild(c),document.getElementById("cc-accept").addEventListener("click",se),document.getElementById("cc-decline").addEventListener("click",de),document.getElementById("cc-save").addEventListener("click",ue),document.getElementById("cc-details").addEventListener("click",le),c.addEventListener("click",function(){re("gdpr")})}var oe=null;function ce(e){if("Tab"===e.key){var t=document.getElementById("cc-banner")||document.getElementById("cc-ccpa-banner");if(t&&t.classList.contains("cc-show")){for(var n=[],o=t.querySelectorAll('button, a[href], input:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])'),c=0;c<o.length;c++){for(var r=o[c],a=!1,i=r.parentElement;i&&i!==t;){if(i.style&&"none"===window.getComputedStyle(i).display){a=!0;break}i=i.parentElement}a||n.push(r)}if(0!==n.length){var s=n[0],d=n[n.length-1];e.shiftKey?document.activeElement===s&&(e.preventDefault(),d.focus()):document.activeElement===d&&(e.preventDefault(),s.focus())}}}}function re(e){"ccpa"!==e?(oe=document.activeElement,requestAnimationFrame(function(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");if(e&&e.classList.add("cc-show"),t){t.classList.add("cc-show");var o=t.querySelector(".cc-btn-accept");o&&o.focus(),document.addEventListener("keydown",ce)}n&&n.classList.remove("cc-show")})):requestAnimationFrame(function(){var e=document.getElementById("cc-ccpa-banner");e&&(e.classList.add("cc-show"),oe=document.activeElement,e.focus(),document.addEventListener("keydown",ce))})}function ae(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");if(e&&e.classList.remove("cc-show"),t&&t.classList.remove("cc-show"),n&&"gdpr"===p&&n.classList.add("cc-show"),document.removeEventListener("keydown",ce),oe&&"function"==typeof oe.focus)try{oe.focus()}catch(e){}oe=null}function ie(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");e&&e.classList.remove("cc-show"),t&&t.classList.remove("cc-show"),n&&n.classList.remove("cc-show"),document.removeEventListener("keydown",ce)}function se(){F(Y(),"accept_all"),ae(),_("cc:accept-all"),_("cc:update")}function de(){F(K(),"decline_all"),ae(),_("cc:decline-all"),_("cc:update")}function ue(){var e;F((e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f},r.cookieTypes.forEach(function(t){if(t.required)e[t.id]=!0;else{var n=document.getElementById("cc-cb-"+t.id);e[t.id]=!!n&&n.checked}}),e),"save_preferences"),ae(),_("cc:save"),_("cc:update")}function le(){var e=document.getElementById("cc-cats"),t=document.getElementById("cc-details"),n=document.getElementById("cc-pref-buttons"),o=e.classList.toggle("cc-open");t.textContent=o?r.text.hideDetailsButton:r.text.showDetailsButton,t.setAttribute("aria-expanded",o?"true":"false"),n&&(o?n.classList.add("cc-open"):n.classList.remove("cc-open"))}function pe(){var e=document.getElementById("cc-declaration");if(e){for(var t=k(),n={},o=document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean),c=0;c<o.length;c++){var a=u(o[c]);a&&(n[o[c]]={name:o[c],vendor:a.vendor,category:a.category,purpose:a.purpose,expiry:a.expiry||"",active:!0})}for(var i=0;i<d.length;i++){var s=d[i];s.name&&s.vendor&&s.category&&"unclassified"!==s.category&&!n[s.name]&&(n[s.name]={name:s.name,vendor:s.vendor,category:s.category,purpose:s.purpose||"",expiry:s.expiry||"",active:!1})}for(var l=0;l<o.length;l++)n[o[l]]||(n[o[l]]={name:o[l],vendor:"",category:"unclassified",purpose:"",expiry:"",active:!0});var p=["necessary","preferences","analytics","marketing"],f={};r.cookieTypes.forEach(function(e){f[e.id]=e.name});var m={};p.forEach(function(e){m[e]=[]}),Object.keys(n).forEach(function(e){var t=n[e],o=t.category;m[o]||(m[o]=[]),m[o].push(t)}),p.forEach(function(e){m[e]&&m[e].sort(function(e,t){return e.name.localeCompare(t.name)})});var g="<div>";if(t.timestamp){var y=new Date(t.timestamp);g+="<p>"+v("Your consent was last updated on "+y.toLocaleDateString(void 0,{year:"numeric",month:"long",day:"numeric"})+" at "+y.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"})+".")+"</p>"}else g+="<p>"+v("You have not yet made a cookie preference choice on this site.")+"</p>";for(var h=0;h<p.length;h++){var w=p[h],b=m[w];if(b&&b.length){var _,x=f[w]||w,E=r.cookieTypes.filter(function(e){return e.id===w})[0],A=E&&E.description||"";_=!!E&&E.required?"Always active":t[w]?"Active":"Inactive",g+="<div>",g+="<p><b>"+v(x)+"</b> ("+v(_)+")</p>",A&&(g+="<p>"+v(A)+"</p>"),g+="<table><thead><tr>",g+="<th>Name</th><th>Provider</th><th>Purpose</th><th>Expiry</th>",g+="</tr></thead><tbody>";for(var S=0;S<b.length;S++){var L=b[S];g+="<tr>",g+="<td><code>"+v(L.name)+"</code></td>",g+="<td>"+v(L.vendor||"Unknown")+"</td>",g+="<td>"+v(L.purpose||"Not specified")+"</td>",g+="<td>"+v(L.expiry||"—")+"</td>",g+="</tr>"}g+="</tbody></table></div>"}}g+="</div>",e.innerHTML=g}}window.__ccInternal={getResourceSnapshot:ye,scriptLoaded:function(e){for(var t=ge||ye(),n=0;n<t.allUrls.length;n++)if(-1!==t.allUrls[n].indexOf(e))return!0;return!1},scriptLoadedUrls:function(e){for(var t=ge||ye(),n=[],o=0;o<t.allUrls.length;o++)-1!==t.allUrls[o].indexOf(e)&&n.push(t.allUrls[o]);return n},hasCookie:function(e){return document.cookie.split(";").some(function(t){return t.trim().split("=")[0]===e})},hasCookiePrefix:function(e){return document.cookie.split(";").some(function(t){return 0===t.trim().split("=")[0].indexOf(e)})},setResourceSnapshot:function(e){ge=e},facadeBridge:{getConfig:function(){return r},getConsentState:function(){return l},loadConsent:$,saveConsent:function(e,t){F(e,t)},hideAllBanners:ie,registerRenderer:function(e){R=e,M=2,V()}}},window.CookieConsent={getState:k,getConfig:function(){return r},getComplianceMode:function(){return p},getCountry:function(){return f},show:function(){re("gdpr")},acceptAll:se,declineAll:de,getGPC:function(){return i},getConsentVersion:function(){return r.consentVersion},on:function(e,t){window.addEventListener(e,t)},off:function(e,t){window.removeEventListener(e,t)},VERSION:c},window.addEventListener("cc:ready",pe),window.addEventListener("cc:update",pe),function(){if(!window.__ccDefaultsSet){var e={wait_for_update:500};r.cookieTypes.forEach(function(t){t.consentMode&&t.consentMode.forEach(function(n){e[n]=t.required?"granted":"denied"})}),x("consent","default",e)}}(),function(){if(r.tcfEnabled){var e=window.__tcfapi&&window.__tcfapi.queue?window.__tcfapi.queue:[];window.__tcfapi=C,e.forEach(function(e){C.apply(null,e)}),S=!0,window===window.top&&window.addEventListener("message",function(e){var t;try{t="string"==typeof e.data?JSON.parse(e.data):e.data}catch(e){return}if(t&&t.__tcfapiCall){var n=t.__tcfapiCall;C(n.command,n.version,function(t,o){var c={__tcfapiReturn:{returnValue:t,success:o,callId:n.callId}};e.source&&e.source.postMessage("string"==typeof e.data?JSON.stringify(c):c,"*")},n.parameter)}})}}();var fe=$();function me(){var e=window.__ccDiscovery&&window.__ccDiscovery.active;if(!e&&(window.__ccDNT||document.cookie.split(";").some(function(e){return 0===e.trim().indexOf("cookieflame_dnt=")})))return E(l=K()),z(l,Y()),J(),j(),p="dnt",te(),ne(),ie(),Z(l),_("cc:ready"),void pe();if(e){E(l=Y()),T(l),J(),H(l,null),r.tcfEnabled&&B(l),window.dataLayer=window.dataLayer||[];for(var t=["consent_accepted_analytics","consent_accepted_marketing","consent_accepted_preferences","consent_accepted_necessary","consent_update","consent_given","cookie_consent_given","cookie_consent_update","consent_accepted"],n=0;n<t.length;n++)window.dataLayer.push({event:t[n]});return p="discovery",te(),ne(),ie(),_("cc:ready"),_("cc:update"),void pe()}var o=$();if(o)if(i&&(o.marketing=!1),l=o,E(o),T(o),J(),j(),H(o,null),r.tcfEnabled&&B(o),te(),ne(),ie(),Z(o),o.cc){if("gdpr"===(p=Q(o.cc))){var c=document.getElementById("cc-icon");c&&c.classList.add("cc-show")}_("cc:ready"),pe()}else X(function(e){if(p=e,"gdpr"===e){var t=document.getElementById("cc-icon");t&&t.classList.add("cc-show")}_("cc:ready"),pe()});else X(function(e){if(p=e,"none"===e)return J(),F(Y(),"auto_grant_non_eu"),_("cc:ready"),_("cc:update"),void pe();if("ccpa"===e){if(J(),i){var t=Y();t.marketing=!1,F(t,"ccpa_gpc_optout")}else F(Y(),"ccpa_auto_grant");return te(),n=r.text,(o=document.createElement("div")).id="cc-ccpa-banner",o.setAttribute("role","dialog"),o.setAttribute("aria-modal","true"),o.setAttribute("aria-label","Privacy notice"),o.setAttribute("tabindex","-1"),o.innerHTML='<div class="cc-ccpa-inner"><div class="cc-ccpa-text"><p>'+v(n.ccpaDescription)+'</p></div><div class="cc-ccpa-actions"><a href="'+v(r.ccpaUrl)+'" class="cc-ccpa-link">'+v(n.ccpaDnsmpiLink)+'</a><button class="cc-btn cc-btn-accept" id="cc-ccpa-ok">'+v(n.ccpaAcceptButton)+"</button></div></div>",document.body.appendChild(o),document.getElementById("cc-ccpa-ok").addEventListener("click",function(){F(Y(),"ccpa_acknowledge"),o.classList.remove("cc-show"),_("cc:update")}),re("ccpa"),_("cc:ready"),_("cc:update"),void pe()}var n,o;te(),J(),j(),ne(),re("gdpr"),_("cc:ready"),pe()})}fe&&(l=fe,E(fe)),function(){if(!g&&r.autoBlockPatterns&&r.autoBlockPatterns.length){g=!0;var e=document.createElement.bind(document),t=Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,"src");t&&t.set&&(document.createElement=function(){var n=e.apply(document,arguments);if("script"!==(arguments[0]||"").toLowerCase())return n;function o(e,t){if(t&&"string"==typeof t&&!e.getAttribute("data-cc")&&!e.getAttribute("data-cc-auto")&&-1===t.indexOf("cookieconsent")){var n=I(t);!n||l&&l[n]||(e.type="text/plain",e.setAttribute("data-cc",n),e.setAttribute("data-cc-auto","1"))}}Object.defineProperty(n,"src",{configurable:!0,enumerable:!0,get:function(){return t.get.call(this)},set:function(e){return o(this,e),t.set.call(this,e)}});var c=n.setAttribute.bind(n);return n.setAttribute=function(e,t){return"src"===e.toLowerCase()&&o(n,t),c(e,t)},n}),new MutationObserver(function(e){e.forEach(function(e){e.addedNodes.forEach(function(e){if(1===e.nodeType){var t=[];"SCRIPT"===e.tagName&&t.push(e),e.querySelectorAll&&e.querySelectorAll("script[src]").forEach(function(e){t.push(e)}),t.forEach(function(e){if(!(e.getAttribute("data-cc-auto")||e.getAttribute("data-cc")||e.src&&-1!==e.src.indexOf("cookieconsent"))){var t=I(e.src);t&&(l&&l[t]||(e.type="text/plain",e.setAttribute("data-cc",t),e.setAttribute("data-cc-auto","1")))}})}})})}).observe(document.documentElement,{childList:!0,subtree:!0})}}(),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",me):me();var ge=null;function ye(){for(var e=[],t=[],n=[],o={},c={},r=document.querySelectorAll("script[src]"),a=0;a<r.length;a++)r[a].src&&e.push(r[a].src);for(var i=document.querySelectorAll("iframe[src]"),s=0;s<i.length;s++)i[s].src&&e.push(i[s].src);if(window.performance&&performance.getEntriesByType)for(var d=performance.getEntriesByType("resource"),u=0;u<d.length;u++){var l=d[u].name||"";if(l){-1===e.indexOf(l)&&e.push(l);var p=d[u].initiatorType||"";if("script"===p||"iframe"===p||"xmlhttprequest"===p||"fetch"===p||-1!==l.indexOf(".js"))try{var f=new URL(l),m=f.hostname;if(m===location.hostname||m==="www."+location.hostname||"www."+m===location.hostname){var g=f.pathname.match(/\/wp-content\/plugins\/([^\/]+)\//);g&&!c[g[1]]&&(c[g[1]]=!0,n.push({slug:g[1],url:l}))}else o[m]||(o[m]=!0,t.push(m))}catch(e){}}}return{allUrls:e,thirdPartyDomains:t,pluginScripts:n}}var ve={};function he(e){var t={set_by:"",set_by_plugin:""},n=window.__ccCookieSetters;if(!n||!Object.prototype.hasOwnProperty.call(n,e)||!n[e])return t;try{var o=new URL(String(n[e]).replace(/:\d+(:\d+)?$/,"")),c=o.hostname;if(c===location.hostname||c==="www."+location.hostname||"www."+c===location.hostname){t.set_by="first-party";var r=o.pathname.match(/\/wp-content\/(?:plugins|themes)\/([^\/]+)\//);r&&(t.set_by_plugin=r[1].toLowerCase())}else t.set_by=c.toLowerCase()}catch(e){}return t}function we(){var t=window.__ccClassified||[],n=window.__ccUnclassified||[],o=window.__ccBeaconUrl;if(o){var c,r={};for(c=0;c<t.length;c++)r[t[c]]=!0;for(c=0;c<n.length;c++)r[n[c]]=!0;var a=Object.keys(ve);for(c=0;c<a.length;c++)r[a[c]]=!0;var i=Object.keys(s);for(c=0;c<i.length;c++)r[i[c]]=!0;var d={},u=[],l=document.cookie.split(";");for(c=0;c<l.length;c++){var p=l[c].indexOf("="),f=(p>-1?l[c].substring(0,p):l[c]).trim(),m=p>-1?l[c].substring(p+1).trim():"";f&&(u.push(f),d[f]=m)}var g=[];for(c=0;c<u.length;c++)if(!r[u[c]]){for(var y=!1,v=Object.keys(s),h=0;h<v.length;h++)if(s[v[h]].prefix&&0===u[c].indexOf(v[h])){y=!0;break}y||g.push(u[c])}if(0!==g.length){for(var w=ye(),b=w.thirdPartyDomains,k=[],_=0;_<w.pluginScripts.length;_++)k.push(w.pluginScripts[_].slug);for(var x=location.pathname,E=0;E<g.length;E++){var A=g[E],S=!!e[A]&&!r[A],L=be(d[A]||""),C=he(A),B=JSON.stringify({cookie_name:A,script_domains:b,plugin_scripts:k,page_path:x,pre_existing:S,value_shape:L,set_by:C.set_by,set_by_plugin:C.set_by_plugin});ve[A]=!0,navigator.sendBeacon&&navigator.sendBeacon(o,new Blob([B],{type:"application/json"}))}}}}function be(e){if(!e||0===e.length)return"empty";if("true"===e||"false"===e||"1"===e||"0"===e)return"boolean";if(/^\d+$/.test(e))return e.length>12?"long-numeric":"numeric";if(/^\d+\.\d+$/.test(e))return"decimal";if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return"uuid";if(/^[A-Za-z0-9+\/]{20,}={0,2}$/.test(e))return"base64";if(/^(GA\d+\.\d+\.\d+\.\d+|G-[A-Z0-9]+)/.test(e))return"ga-id";try{if("object"==typeof JSON.parse(decodeURIComponent(e)))return"json"}catch(e){}return/^[a-f0-9]{24,}$/i.test(e)?"hex-id":/^\d{10,13}$/.test(e)?"timestamp":e.length<=20?"short-string":e.length<=100?"medium-string":"long-string"}setTimeout(we,3e3),setTimeout(we,15e3),setTimeout(we,45e3),window.addEventListener("cc:update",function(){setTimeout(we,3e3)}),window.addEventListener("pagehide",we)}();
     19!function(){"use strict";var e={};try{for(var t=document.cookie.split(";"),n=0;n<t.length;n++){var o=t[n].trim().split("=")[0];o&&(e[o]=!0)}}catch(e){}var c="5.3.0",r={cookieName:"cc_consent",cookieExpiry:365,privacyUrl:"/privacy-policy",ccpaUrl:"/privacy-policy#do-not-sell",geoApi:"",logEndpoint:"",reconsentDays:365,cspSafe:!1,tcfEnabled:!0,cmpId:1,cmpVersion:1,vendorListVersion:2,storageMethod:"cookie",cookieDomain:"",shareSubdomains:!1,theme:"light",showPoweredBy:!1,iconPosition:"left",consentVersion:"",autoBlockPatterns:[{pattern:"googletagmanager.com/gtag",category:"analytics"},{pattern:"google-analytics.com",category:"analytics"},{pattern:"analytics.google.com",category:"analytics"},{pattern:"hotjar.com",category:"analytics"},{pattern:"mixpanel.com",category:"analytics"},{pattern:"segment.com/analytics",category:"analytics"},{pattern:"segment.io",category:"analytics"},{pattern:"clarity.ms",category:"analytics"},{pattern:"plausible.io",category:"analytics"},{pattern:"js.hs-analytics.net",category:"analytics"},{pattern:"connect.facebook.net",category:"marketing"},{pattern:"snap.licdn.com",category:"marketing"},{pattern:"ads.linkedin.com",category:"marketing"},{pattern:"googleads.g.doubleclick.net",category:"marketing"},{pattern:"googlesyndication.com",category:"marketing"},{pattern:"googleadservices.com",category:"marketing"},{pattern:"ads-twitter.com",category:"marketing"},{pattern:"analytics.tiktok.com",category:"marketing"},{pattern:"pintrk",category:"marketing"},{pattern:"s.pinimg.com/ct",category:"marketing"},{pattern:"widget.intercom.io",category:"preferences"},{pattern:"js.intercomcdn.com",category:"preferences"}],regionRules:{gdpr:["AT","BE","BG","CY","CZ","DE","DK","EE","ES","FI","FR","GR","HR","HU","IE","IT","LT","LU","LV","MT","NL","PL","PT","RO","SE","SI","SK","IS","LI","NO","GB","GG","JE","IM","CH","BR","ZA"],ccpa:["US"]},text:{title:"We value your privacy",description:"We use cookies to improve your experience, analyse traffic and personalise content. You can choose which categories to allow.",acceptAllButton:"Accept All",declineAllButton:"Decline All",saveButton:"Save Preferences",showDetailsButton:"Manage preferences",hideDetailsButton:"Hide preferences",alwaysOnLabel:"(always on)",ccpaTitle:"Your Privacy Choices",ccpaDescription:"We use cookies and similar technologies. Under California law, you have the right to opt out of the sale or sharing of your personal information.",ccpaAcceptButton:"OK",ccpaDnsmpiLink:"Do Not Sell My Personal Information"},cookieTypes:[{id:"necessary",name:"Necessary",description:"Required for the site to function. Cannot be disabled.",required:!0,consentMode:["security_storage","functionality_storage"],tcfPurposes:[1],onAccept:function(){}},{id:"preferences",name:"Preferences",description:"Remember your settings and personalisation choices.",required:!1,defaultValue:!1,consentMode:["functionality_storage","personalization_storage"],tcfPurposes:[2,3],onAccept:function(){},onReject:function(){}},{id:"analytics",name:"Analytics",description:"Help us understand how visitors use the site.",required:!1,defaultValue:!1,consentMode:["analytics_storage"],tcfPurposes:[4,5,6],onAccept:function(){},onReject:function(){}},{id:"marketing",name:"Marketing",description:"Used to deliver relevant ads and track campaigns.",required:!1,defaultValue:!1,consentMode:["ad_storage","ad_user_data","ad_personalization"],tcfPurposes:[7,8,9,10],onAccept:function(){},onReject:function(){}}]};if(window.__ccConfig&&"object"==typeof window.__ccConfig){var a=window.__ccConfig;["cookieName","cookieExpiry","privacyUrl","ccpaUrl","geoApi","logEndpoint","reconsentDays","cspSafe","tcfEnabled","cmpId","cmpVersion","vendorListVersion","storageMethod","cookieDomain","shareSubdomains","theme","showPoweredBy","iconPosition","consentVersion"].forEach(function(e){void 0!==a[e]&&(r[e]=a[e])}),a.text&&"object"==typeof a.text&&Object.keys(a.text).forEach(function(e){r.text[e]=a.text[e]}),Array.isArray(a.cookieTypes)&&(r.cookieTypes=a.cookieTypes),Array.isArray(a.autoBlockPatterns)&&(r.autoBlockPatterns=a.autoBlockPatterns),a.regionRules&&(r.regionRules=a.regionRules)}window.__ccConsentVersion&&(r.consentVersion=window.__ccConsentVersion);var i=!!window.__ccGPC;window.__ccBlockRules&&Array.isArray(window.__ccBlockRules)&&window.__ccBlockRules.length&&(r.autoBlockPatterns=window.__ccBlockRules);var s={cc_consent:{vendor:"CookieFlame",category:"necessary",purpose:"Stores your consent preferences",expiry:"1 year"},cc_anon_id:{vendor:"CookieFlame",category:"necessary",purpose:"Anonymous ID for consent logging",expiry:"1 year"},cookieflame_dnt:{vendor:"CookieFlame",category:"necessary",purpose:"Do-not-track opt-out flag",expiry:"1 year"},PHPSESSID:{vendor:"PHP",category:"necessary",purpose:"Server-side session identifier",expiry:"Session"},wp_settings_:{vendor:"WordPress",category:"necessary",purpose:"Admin interface preferences",prefix:!0,expiry:"1 year"},"wp-settings-":{vendor:"WordPress",category:"necessary",purpose:"Admin interface preferences",prefix:!0,expiry:"1 year"},"wp-settings-time-":{vendor:"WordPress",category:"necessary",purpose:"Admin settings timestamp",prefix:!0,expiry:"1 year"},wordpress_logged_in_:{vendor:"WordPress",category:"necessary",purpose:"Login session",prefix:!0,expiry:"Session"},wordpress_sec_:{vendor:"WordPress",category:"necessary",purpose:"Secure login session",prefix:!0,expiry:"Session"},wordpress_test_cookie:{vendor:"WordPress",category:"necessary",purpose:"Checks cookie support",expiry:"Session"},wp_lang:{vendor:"WordPress",category:"necessary",purpose:"Language preference",expiry:"Session"},woocommerce_cart_hash:{vendor:"WooCommerce",category:"necessary",purpose:"Shopping cart contents",expiry:"Session"},woocommerce_items_in_cart:{vendor:"WooCommerce",category:"necessary",purpose:"Cart item count",expiry:"Session"},wp_woocommerce_session_:{vendor:"WooCommerce",category:"necessary",purpose:"Shopping session",prefix:!0,expiry:"2 days"},_ga:{vendor:"Google Analytics",category:"analytics",purpose:"Distinguishes visitors",expiry:"2 years"},_ga_:{vendor:"Google Analytics",category:"analytics",purpose:"Stores session state",prefix:!0,expiry:"2 years"},_gid:{vendor:"Google Analytics",category:"analytics",purpose:"Distinguishes visitors (24h)",expiry:"24 hours"},_gat:{vendor:"Google Analytics",category:"analytics",purpose:"Throttles request rate",expiry:"1 minute"},wp_wk_ga_untrack_:{vendor:"Google Analytics",category:"analytics",purpose:"GA opt-out flag",prefix:!0,expiry:"Persistent"},_gcl_au:{vendor:"Google Ads",category:"marketing",purpose:"Conversion linker",expiry:"90 days"},_gcl_aw:{vendor:"Google Ads",category:"marketing",purpose:"Stores ad click info",expiry:"90 days"},_gac_:{vendor:"Google Ads",category:"marketing",purpose:"Campaign information",prefix:!0,expiry:"90 days"},IDE:{vendor:"Google DoubleClick",category:"marketing",purpose:"Ad serving and frequency",expiry:"1 year"},test_cookie:{vendor:"Google DoubleClick",category:"marketing",purpose:"Checks cookie support",expiry:"15 minutes"},NID:{vendor:"Google",category:"preferences",purpose:"Stores preferences",expiry:"6 months"}},d=window.__ccCustomCookies||[];function u(e){for(var t=0;t<d.length;t++){var n=d[t];if("exact"===n.match&&e===n.name)return n;if("prefix"===n.match&&0===e.indexOf(n.name))return n;if("contains"===n.match&&-1!==e.indexOf(n.name))return n}var o=s[e];return o||(Object.keys(s).forEach(function(t){!o&&s[t].prefix&&0===e.indexOf(t)&&(o=s[t])}),o||null)}var l=null,p="none",f=null,m=null,g=!1,y=0;function v(e){return"string"!=typeof e&&(e=String(e)),e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function h(e){if("localStorage"===r.storageMethod)try{return localStorage.getItem(e)}catch(e){return null}var t=document.cookie.match(new RegExp("(?:^|;\\s*)"+e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"=([^;]*)"));if(!t)return null;try{return decodeURIComponent(t[1])}catch(e){return null}}function w(e,t,n){if("localStorage"!==r.storageMethod){var o=new Date;o.setTime(o.getTime()+864e5*n);var c=e+"="+encodeURIComponent(t)+";expires="+o.toUTCString()+";path=/;SameSite=Lax;Secure";r.cookieDomain&&(c+=";domain="+r.cookieDomain),document.cookie=c}else try{localStorage.setItem(e,t)}catch(e){}}function b(){if(m)return m;var e=h("cc_anon_id");return e?(m=e,e):(w("cc_anon_id",m="cc_"+function(){if("undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID)try{return crypto.randomUUID()}catch(e){}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}(),730),m)}function k(){var e={timestamp:l?l.ts:null,complianceMode:p,country:f,gpc:i,consentVersion:r.consentVersion};return r.cookieTypes.forEach(function(t){e[t.id]=l?!!l[t.id]:!!t.required}),e}function _(e,t){window.dispatchEvent(new CustomEvent(e,{detail:t||k()}))}function x(){window.dataLayer=window.dataLayer||[],window.dataLayer.push(arguments)}function E(e){var t={};r.cookieTypes.forEach(function(n){n.consentMode&&n.consentMode.forEach(function(o){t[o]=e[n.id]?"granted":"denied"})}),i&&(t.ad_storage="denied",t.ad_user_data="denied",t.ad_personalization="denied"),x("consent","update",t)}var A=new Map,S=!1;function L(e){var t={};return r.cookieTypes.forEach(function(n){n.tcfPurposes&&n.tcfPurposes.forEach(function(o){t[o]=!!e[n.id]})}),{tcString:"",tcfPolicyVersion:4,cmpId:r.cmpId,cmpVersion:r.cmpVersion,gdprApplies:"gdpr"===p,eventStatus:"useractioncomplete",cmpStatus:"loaded",listenerId:void 0,isServiceSpecific:!0,useNonStandardTexts:!1,publisherCC:"GB",purposeOneTreatment:!1,purpose:{consents:t,legitimateInterests:{}},vendor:{consents:{},legitimateInterests:{}},specialFeatureOptins:{},publisher:{consents:{},legitimateInterests:{},customPurpose:{consents:{},legitimateInterests:{}},restrictions:{}}}}function C(e,t,n,o){if("function"==typeof n)if("addEventListener"===e){var c=++y;if(A.set(c,n),l){var a=L(l);a.listenerId=c,n(a,!0)}}else"removeEventListener"===e?n({listenerId:o},A.delete(o)):"getTCData"===e?n(l?L(l):null,!!l):"ping"===e?n({gdprApplies:"gdpr"===p,cmpLoaded:S,cmpStatus:S?"loaded":"stub",displayStatus:"hidden",apiVersion:"2.2",cmpVersion:r.cmpVersion,cmpId:r.cmpId,gvlVersion:r.vendorListVersion,tcfPolicyVersion:4},!0):n(null,!1)}function B(e){A.forEach(function(t,n){var o=L(e);o.listenerId=n;try{t(o,!0)}catch(e){}})}function I(e){if(!e)return null;for(var t=e.toLowerCase(),n=0;n<r.autoBlockPatterns.length;n++){var o=r.autoBlockPatterns[n];if(o.pattern instanceof RegExp){if(o.pattern.test(t))return o.category}else if(-1!==t.indexOf(o.pattern.toLowerCase()))return o.category}return null}function T(e){document.querySelectorAll('script[type="text/plain"][data-cc]').forEach(function(t){var n=(t.getAttribute("data-cc")||"").toLowerCase();if(e[n]&&!t.getAttribute("data-cc-activated")){t.setAttribute("data-cc-activated","1");var o=document.createElement("script");o.isWPRocket=!0,Array.from(t.attributes).forEach(function(e){"type"!==e.name&&"data-cc"!==e.name&&"data-cc-activated"!==e.name&&"data-cc-auto"!==e.name&&o.setAttribute(e.name,e.value)}),!t.src&&t.textContent&&(o.textContent=t.textContent),t.parentNode.insertBefore(o,t.nextSibling),t.parentNode.removeChild(t)}}),["img","iframe","link"].forEach(function(t){document.querySelectorAll(t+"[data-cc][data-cc-src]").forEach(function(n){var o=(n.getAttribute("data-cc")||"").toLowerCase();e[o]&&!n.getAttribute("data-cc-activated")&&(n.setAttribute("data-cc-activated","1"),"link"===t?n.href=n.getAttribute("data-cc-src"):n.src=n.getAttribute("data-cc-src"))})})}var O=[{pattern:"youtube.com",category:"marketing"},{pattern:"youtube-nocookie.com",category:"marketing"},{pattern:"youtu.be",category:"marketing"},{pattern:"player.vimeo.com",category:"marketing"},{pattern:"vimeo.com",category:"marketing"},{pattern:"dailymotion.com",category:"marketing"},{pattern:"open.spotify.com",category:"preferences"},{pattern:"soundcloud.com",category:"preferences"},{pattern:"google.com/maps",category:"preferences"},{pattern:"maps.google.com",category:"preferences"},{pattern:"facebook.com/plugins",category:"marketing"},{pattern:"instagram.com",category:"marketing"},{pattern:"tiktok.com/embed",category:"marketing"},{pattern:"twitter.com",category:"marketing"},{pattern:"x.com",category:"marketing"}];function D(){return!(!document.body||!(document.body.classList.contains("elementor-editor-active")||document.body.classList.contains("fl-builder-edit")||document.body.classList.contains("et-fb-root-ancestor")))}var P=[],M=0,R=null,q=!1;function V(){if(R&&P.length){var e=P;P=[];try{R(e)}catch(e){}}}function N(){0!==P.length&&(2!==M?1!==M&&3!==M&&(M=1,function(){var e=window.__ccConfig&&window.__ccConfig.facadesUrl||null;if(!e)return void(M=3);var t=document.createElement("script");t.src=e,t.async=!0,t.onload=function(){M=2,V()},t.onerror=function(){M=3},(document.head||document.documentElement).appendChild(t)}()):V())}function U(e){var t=e.src||e.getAttribute("data-src")||"";if(t&&!e.getAttribute("data-cc-facade")&&!D()){var n=function(e){if(!e)return null;for(var t=e.toLowerCase(),n=0;n<O.length;n++)if(-1!==t.indexOf(O[n].pattern))return O[n].category;return null}(t);if(n){var o=l||Z();o&&o[n]||(e.setAttribute("data-cc-facade","1"),e.setAttribute("data-cc-iframe-cat",n),e.setAttribute("data-cc-original-src",t),e.removeAttribute("src"),e.style.display="none",P.push({type:"iframe",node:e,category:n,src:t}),N())}}}function G(e){if(!e.getAttribute("data-cc-facade")&&!D()){var t=e.getAttribute("data-settings");if(t){var n;try{n=JSON.parse(t)}catch(e){return}var o=n.youtube_url||n.vimeo_url||"";if(o){var c=o.toLowerCase(),r=null;if((-1!==c.indexOf("youtube")||-1!==c.indexOf("youtu.be")||-1!==c.indexOf("vimeo")||-1!==c.indexOf("dailymotion"))&&(r="marketing"),r&&(!l||!l[r])){if(!l){var a=Z();if(a&&a[r])return}e.setAttribute("data-cc-facade","1"),q=!0,P.push({type:"elementor",node:e,category:r,url:o}),N()}}}}}function j(){for(var e=document.querySelectorAll("iframe[src]"),t=0;t<e.length;t++)U(e[t]);for(var n=document.querySelectorAll(".elementor-widget-video"),o=0;o<n.length;o++)G(n[o]);for(var c=document.querySelectorAll(".wp-block-embed__wrapper iframe[src]"),r=0;r<c.length;r++)U(c[r])}function J(){new MutationObserver(function(e){e.forEach(function(e){e.addedNodes.forEach(function(e){if(1===e.nodeType){if("IFRAME"===e.tagName&&U(e),e.querySelectorAll){for(var t=e.querySelectorAll("iframe[src]"),n=0;n<t.length;n++)U(t[n]);for(var o=e.querySelectorAll(".elementor-widget-video"),c=0;c<o.length;c++)G(o[c])}e.classList&&e.classList.contains("elementor-widget-video")&&G(e)}})})}).observe(document.documentElement,{childList:!0,subtree:!0})}function W(){for(var e=document.querySelectorAll("iframe[src]"),t=0;t<e.length;t++){var n=e[t];if(!n.getAttribute("data-cc-facade")){var o=n.src||"";if(o)for(var c=0;c<O.length;c++)if(-1!==o.indexOf(O[c].pattern)){n.src=o;break}}}}function H(e,t){r.cookieTypes.forEach(function(n){var o=!!e[n.id];if((t?!!t[n.id]:null)!==o||null===t)if(o&&"function"==typeof n.onAccept)try{n.onAccept()}catch(e){console.warn("[CookieConsent] onAccept error for "+n.id,e)}else if(!o&&"function"==typeof n.onReject)try{n.onReject()}catch(e){console.warn("[CookieConsent] onReject error for "+n.id,e)}})}function F(e,t){var n=l;if(l=e,w(r.cookieName,JSON.stringify(e),r.cookieExpiry),E(e),q&&t)for(var o=document.querySelectorAll(".cc-iframe-facade[data-cc-elementor-widget]"),a=0;a<o.length;a++){var s=o[a].getAttribute("data-cc-iframe-cat");if(s&&e[s])return void window.location.reload()}K(e,n),T(e),function(e){for(var t=document.querySelectorAll(".cc-iframe-facade"),n=0;n<t.length;n++)if(!t[n].getAttribute("data-cc-elementor-widget")&&e[t[n].getAttribute("data-cc-iframe-cat")]){var o=t[n].nextElementSibling;if(o&&"IFRAME"===o.tagName&&o.getAttribute("data-cc-facade")){var c=o.getAttribute("data-cc-original-src");c&&(o.src=c,o.style.display="",o.removeAttribute("data-cc-facade"))}t[n].parentNode.removeChild(t[n])}}(e),t&&"auto_grant_non_eu"!==t&&"ccpa_auto_grant"!==t&&setTimeout(W,300),H(e,n),$(e),r.tcfEnabled&&B(e),t&&function(e,t){if(r.logEndpoint){var n={};r.cookieTypes.forEach(function(e){n[e.id]=!!t[e.id]});var o={anonymousId:b(),timestamp:(new Date).toISOString(),action:e,consent:n,complianceMode:p,country:f,url:location.href,userAgent:navigator.userAgent,tcfEnabled:r.tcfEnabled,gpc:i,consentVersion:r.consentVersion,version:c};try{var a=JSON.stringify(o);navigator.sendBeacon?navigator.sendBeacon(r.logEndpoint,new Blob([a],{type:"application/json"})):fetch(r.logEndpoint,{method:"POST",body:a,headers:{"Content-Type":"application/json"},keepalive:!0})}catch(e){}}}(t,e)}function $(e){r.cookieTypes.forEach(function(t){if(!t.required){var n=document.getElementById("cc-cb-"+t.id);n&&(n.checked=!!e[t.id])}});var t=document.getElementById("cc-cats"),n=document.getElementById("cc-details"),o=document.getElementById("cc-pref-buttons");t&&t.classList.remove("cc-open"),n&&(n.textContent=r.text.showDetailsButton),o&&o.classList.remove("cc-open")}function Z(){var e=h(r.cookieName);if(!e)return null;try{var t=JSON.parse(e);return!t||"object"!=typeof t||Array.isArray(t)||(!t.ts||isNaN(new Date(t.ts).getTime())||(Date.now()-new Date(t.ts).getTime())/864e5>r.reconsentDays||r.consentVersion&&t.cv!==r.consentVersion)?null:t}catch(e){return null}}function Y(){var e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f};return r.cookieTypes.forEach(function(t){e[t.id]=!0}),e}function z(){var e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f};return r.cookieTypes.forEach(function(t){e[t.id]=!!t.required}),e}function K(e,t){if(t){var n=[];if(r.cookieTypes.forEach(function(o){o.required||t[o.id]&&!e[o.id]&&n.push(o.id)}),n.length)document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean).forEach(function(e){var t=u(e);if(t&&-1!==n.indexOf(t.category)){var o=location.hostname,c="."+o;document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/",document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT",document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+c,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+o,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; Secure",r.cookieDomain&&(document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+r.cookieDomain,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+r.cookieDomain+"; Secure");var a=o.split(".");if(a.length>2){var i="."+a.slice(1).join(".");document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i,document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain="+i+"; Secure"}}})}}function Q(e){return(f=e?e.toUpperCase():null)?r.regionRules.gdpr&&-1!==r.regionRules.gdpr.indexOf(f)?"gdpr":r.regionRules.ccpa&&-1!==r.regionRules.ccpa.indexOf(f)?"ccpa":"none":"gdpr"}function X(e){if(window.__ccGeo)return e(Q(window.__ccGeo));r.geoApi?fetch(r.geoApi).then(function(e){return e.json()}).then(function(t){var n=t.country_code||t.country||"";n?e(Q(n)):ee(e)}).catch(function(){ee(e)}):ee(e)}function ee(e){var t="";try{t=Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch(e){}return 0===t.indexOf("Europe/")||"Atlantic/Reykjavik"===t?(f=null,e("gdpr")):0===t.indexOf("US/")||0===t.indexOf("America/")&&["New_York","Chicago","Denver","Los_Angeles","Phoenix","Anchorage","Boise","Detroit","Indianapolis","Louisville","Menominee","Nome","Adak","Juneau","Sitka","Yakutat","Metlakatla","Kentucky","Indiana","North_Dakota"].some(function(e){return-1!==t.indexOf(e)})||"Pacific/Honolulu"===t?(f="US",e("ccpa")):(f=null,void e("gdpr"))}function te(){"dark"===r.theme&&document.documentElement.classList.add("cc-dark"),"right"===r.iconPosition&&document.documentElement.classList.add("cc-icon-right")}function ne(){var e=r.text,t=document.createElement("div");t.id="cc-overlay",document.body.appendChild(t);var n=document.createElement("div");n.id="cc-banner",n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-label","Cookie consent"),n.setAttribute("tabindex","-1");var o=r.cookieTypes.map(function(t){var n=t.required?' <span class="cc-always-on">'+v(e.alwaysOnLabel)+"</span>":"";return'<div class="cc-cat"><div><div class="cc-cat-label">'+v(t.name)+n+'</div><div class="cc-cat-desc">'+v(t.description||"")+'</div></div><label class="cc-toggle"><input type="checkbox" id="cc-cb-'+v(t.id)+'" aria-label="'+v(t.name)+'"'+(t.required||t.defaultValue?" checked":"")+(t.required?" disabled":"")+'><span class="cc-slider"></span></label></div>'}).join("");n.innerHTML='<div class="cc-inner"><p class="cc-title">'+v(e.title)+'</p><p class="cc-text">'+v(e.description)+' <a href="'+v(r.privacyUrl)+'">Privacy Policy</a></p><div class="cc-buttons"><button class="cc-btn cc-btn-accept" id="cc-accept">'+v(e.acceptAllButton)+'</button><button class="cc-btn cc-btn-details" id="cc-details" aria-expanded="false">'+v(e.showDetailsButton)+'</button></div><div class="cc-categories" id="cc-cats">'+o+'</div><div class="cc-pref-buttons" id="cc-pref-buttons"><button class="cc-btn cc-btn-save" id="cc-save">'+v(e.saveButton)+'</button><button class="cc-btn cc-btn-decline" id="cc-decline">'+v(e.declineAllButton)+"</button></div></div>"+(r.showPoweredBy?'<div class="cc-powered"><a href="https://www.cookieflame.com" target="_blank" rel="noopener">Powered by CookieFlame</a></div>':""),document.body.appendChild(n);var c=document.createElement("button");c.id="cc-icon",c.setAttribute("aria-label","Cookie settings"),c.innerHTML='<svg width="24" height="24" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M19.1 1.16c-.06-.42-.38-.77-.8-.83A11.8 11.8 0 0 0 13.28 1.2L7.5 4.3A11.8 11.8 0 0 0 3.95 7.98L1.09 13.9A11.8 11.8 0 0 0 .38 18.96l1.13 6.47a11.8 11.8 0 0 0 3.53 4.99l4.71 4.56a11.8 11.8 0 0 0 4.59 2.24l6.48.91a11.8 11.8 0 0 0 5.52-.89l5.78-3.09a11.8 11.8 0 0 0 3.55-3.69l2.87-5.9a11.8 11.8 0 0 0 .71-5.08c-.07-.41-.42-.73-.83-.79a14.8 14.8 0 0 1-8.16-7.61c-.14-.59-.63-1.05-1.23-1.14A14.8 14.8 0 0 1 19.1 1.16ZM15.25 10.25a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm-5 15a2.5 2.5 0 1 1 5 0 2.5 2.5 0 0 1-5 0Zm17.5-5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Z" fill="currentColor"/></svg>',document.body.appendChild(c),document.getElementById("cc-accept").addEventListener("click",se),document.getElementById("cc-decline").addEventListener("click",de),document.getElementById("cc-save").addEventListener("click",ue),document.getElementById("cc-details").addEventListener("click",le),c.addEventListener("click",function(){re("gdpr")})}var oe=null;function ce(e){if("Tab"===e.key){var t=document.getElementById("cc-banner")||document.getElementById("cc-ccpa-banner");if(t&&t.classList.contains("cc-show")){for(var n=[],o=t.querySelectorAll('button, a[href], input:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])'),c=0;c<o.length;c++){for(var r=o[c],a=!1,i=r.parentElement;i&&i!==t;){if(i.style&&"none"===window.getComputedStyle(i).display){a=!0;break}i=i.parentElement}a||n.push(r)}if(0!==n.length){var s=n[0],d=n[n.length-1];e.shiftKey?document.activeElement===s&&(e.preventDefault(),d.focus()):document.activeElement===d&&(e.preventDefault(),s.focus())}}}}function re(e){"ccpa"!==e?(oe=document.activeElement,requestAnimationFrame(function(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");if(e&&e.classList.add("cc-show"),t){t.classList.add("cc-show");var o=t.querySelector(".cc-btn-accept");o&&o.focus(),document.addEventListener("keydown",ce)}n&&n.classList.remove("cc-show")})):requestAnimationFrame(function(){var e=document.getElementById("cc-ccpa-banner");e&&(e.classList.add("cc-show"),oe=document.activeElement,e.focus(),document.addEventListener("keydown",ce))})}function ae(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");if(e&&e.classList.remove("cc-show"),t&&t.classList.remove("cc-show"),n&&"gdpr"===p&&n.classList.add("cc-show"),document.removeEventListener("keydown",ce),oe&&"function"==typeof oe.focus)try{oe.focus()}catch(e){}oe=null}function ie(){var e=document.getElementById("cc-overlay"),t=document.getElementById("cc-banner"),n=document.getElementById("cc-icon");e&&e.classList.remove("cc-show"),t&&t.classList.remove("cc-show"),n&&n.classList.remove("cc-show"),document.removeEventListener("keydown",ce)}function se(){F(Y(),"accept_all"),ae(),_("cc:accept-all"),_("cc:update")}function de(){F(z(),"decline_all"),ae(),_("cc:decline-all"),_("cc:update")}function ue(){var e;F((e={ts:(new Date).toISOString(),cv:r.consentVersion,cc:f},r.cookieTypes.forEach(function(t){if(t.required)e[t.id]=!0;else{var n=document.getElementById("cc-cb-"+t.id);e[t.id]=!!n&&n.checked}}),e),"save_preferences"),ae(),_("cc:save"),_("cc:update")}function le(){var e=document.getElementById("cc-cats"),t=document.getElementById("cc-details"),n=document.getElementById("cc-pref-buttons"),o=e.classList.toggle("cc-open");t.textContent=o?r.text.hideDetailsButton:r.text.showDetailsButton,t.setAttribute("aria-expanded",o?"true":"false"),n&&(o?n.classList.add("cc-open"):n.classList.remove("cc-open"))}function pe(){var e=document.getElementById("cc-declaration");if(e){for(var t=k(),n={},o=document.cookie.split(";").map(function(e){return e.trim().split("=")[0]}).filter(Boolean),c=0;c<o.length;c++){var a=u(o[c]);a&&(n[o[c]]={name:o[c],vendor:a.vendor,category:a.category,purpose:a.purpose,expiry:a.expiry||"",active:!0})}for(var i=0;i<d.length;i++){var s=d[i];s.name&&s.vendor&&s.category&&"unclassified"!==s.category&&!n[s.name]&&(n[s.name]={name:s.name,vendor:s.vendor,category:s.category,purpose:s.purpose||"",expiry:s.expiry||"",active:!1})}for(var l=0;l<o.length;l++)n[o[l]]||(n[o[l]]={name:o[l],vendor:"",category:"unclassified",purpose:"",expiry:"",active:!0});var p=["necessary","preferences","analytics","marketing"],f={};r.cookieTypes.forEach(function(e){f[e.id]=e.name});var m={};p.forEach(function(e){m[e]=[]}),Object.keys(n).forEach(function(e){var t=n[e],o=t.category;m[o]||(m[o]=[]),m[o].push(t)}),p.forEach(function(e){m[e]&&m[e].sort(function(e,t){return e.name.localeCompare(t.name)})});var g="<div>";if(t.timestamp){var y=new Date(t.timestamp);g+="<p>"+v("Your consent was last updated on "+y.toLocaleDateString(void 0,{year:"numeric",month:"long",day:"numeric"})+" at "+y.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"})+".")+"</p>"}else g+="<p>"+v("You have not yet made a cookie preference choice on this site.")+"</p>";for(var h=0;h<p.length;h++){var w=p[h],b=m[w];if(b&&b.length){var _,x=f[w]||w,E=r.cookieTypes.filter(function(e){return e.id===w})[0],A=E&&E.description||"";_=!!E&&E.required?"Always active":t[w]?"Active":"Inactive",g+="<div>",g+="<p><b>"+v(x)+"</b> ("+v(_)+")</p>",A&&(g+="<p>"+v(A)+"</p>"),g+="<table><thead><tr>",g+="<th>Name</th><th>Provider</th><th>Purpose</th><th>Expiry</th>",g+="</tr></thead><tbody>";for(var S=0;S<b.length;S++){var L=b[S];g+="<tr>",g+="<td><code>"+v(L.name)+"</code></td>",g+="<td>"+v(L.vendor||"Unknown")+"</td>",g+="<td>"+v(L.purpose||"Not specified")+"</td>",g+="<td>"+v(L.expiry||"—")+"</td>",g+="</tr>"}g+="</tbody></table></div>"}}g+="</div>",e.innerHTML=g}}window.__ccInternal={getResourceSnapshot:ye,scriptLoaded:function(e){for(var t=ge||ye(),n=0;n<t.allUrls.length;n++)if(-1!==t.allUrls[n].indexOf(e))return!0;return!1},scriptLoadedUrls:function(e){for(var t=ge||ye(),n=[],o=0;o<t.allUrls.length;o++)-1!==t.allUrls[o].indexOf(e)&&n.push(t.allUrls[o]);return n},hasCookie:function(e){return document.cookie.split(";").some(function(t){return t.trim().split("=")[0]===e})},hasCookiePrefix:function(e){return document.cookie.split(";").some(function(t){return 0===t.trim().split("=")[0].indexOf(e)})},setResourceSnapshot:function(e){ge=e},visitorCookies:function(e){for(var t=ye().thirdPartyDomains,n={},o=0;o<e.length;o++)we(e[o],t,he(e[o]))&&(n[e[o]]=!0);return n},facadeBridge:{getConfig:function(){return r},getConsentState:function(){return l},loadConsent:Z,saveConsent:function(e,t){F(e,t)},hideAllBanners:ie,registerRenderer:function(e){R=e,M=2,V()}}},window.CookieConsent={getState:k,getConfig:function(){return r},getComplianceMode:function(){return p},getCountry:function(){return f},show:function(){re("gdpr")},acceptAll:se,declineAll:de,getGPC:function(){return i},getConsentVersion:function(){return r.consentVersion},on:function(e,t){window.addEventListener(e,t)},off:function(e,t){window.removeEventListener(e,t)},VERSION:c},window.addEventListener("cc:ready",pe),window.addEventListener("cc:update",pe),function(){if(!window.__ccDefaultsSet){var e={wait_for_update:500};r.cookieTypes.forEach(function(t){t.consentMode&&t.consentMode.forEach(function(n){e[n]=t.required?"granted":"denied"})}),x("consent","default",e)}}(),function(){if(r.tcfEnabled){var e=window.__tcfapi&&window.__tcfapi.queue?window.__tcfapi.queue:[];window.__tcfapi=C,e.forEach(function(e){C.apply(null,e)}),S=!0,window===window.top&&window.addEventListener("message",function(e){var t;try{t="string"==typeof e.data?JSON.parse(e.data):e.data}catch(e){return}if(t&&t.__tcfapiCall){var n=t.__tcfapiCall;C(n.command,n.version,function(t,o){var c={__tcfapiReturn:{returnValue:t,success:o,callId:n.callId}};e.source&&e.source.postMessage("string"==typeof e.data?JSON.stringify(c):c,"*")},n.parameter)}})}}();var fe=Z();function me(){var e=window.__ccDiscovery&&window.__ccDiscovery.active;if(!e&&(window.__ccDNT||document.cookie.split(";").some(function(e){return 0===e.trim().indexOf("cookieflame_dnt=")})))return E(l=z()),K(l,Y()),J(),j(),p="dnt",te(),ne(),ie(),$(l),_("cc:ready"),void pe();if(e){E(l=Y()),T(l),J(),H(l,null),r.tcfEnabled&&B(l),window.dataLayer=window.dataLayer||[];for(var t=["consent_accepted_analytics","consent_accepted_marketing","consent_accepted_preferences","consent_accepted_necessary","consent_update","consent_given","cookie_consent_given","cookie_consent_update","consent_accepted"],n=0;n<t.length;n++)window.dataLayer.push({event:t[n]});return p="discovery",te(),ne(),ie(),_("cc:ready"),_("cc:update"),void pe()}var o=Z();if(o)if(i&&(o.marketing=!1),l=o,E(o),T(o),J(),j(),H(o,null),r.tcfEnabled&&B(o),te(),ne(),ie(),$(o),o.cc){if("gdpr"===(p=Q(o.cc))){var c=document.getElementById("cc-icon");c&&c.classList.add("cc-show")}_("cc:ready"),pe()}else X(function(e){if(p=e,"gdpr"===e){var t=document.getElementById("cc-icon");t&&t.classList.add("cc-show")}_("cc:ready"),pe()});else X(function(e){if(p=e,"none"===e)return J(),F(Y(),"auto_grant_non_eu"),_("cc:ready"),_("cc:update"),void pe();if("ccpa"===e){if(J(),i){var t=Y();t.marketing=!1,F(t,"ccpa_gpc_optout")}else F(Y(),"ccpa_auto_grant");return te(),n=r.text,(o=document.createElement("div")).id="cc-ccpa-banner",o.setAttribute("role","dialog"),o.setAttribute("aria-modal","true"),o.setAttribute("aria-label","Privacy notice"),o.setAttribute("tabindex","-1"),o.innerHTML='<div class="cc-ccpa-inner"><div class="cc-ccpa-text"><p>'+v(n.ccpaDescription)+'</p></div><div class="cc-ccpa-actions"><a href="'+v(r.ccpaUrl)+'" class="cc-ccpa-link">'+v(n.ccpaDnsmpiLink)+'</a><button class="cc-btn cc-btn-accept" id="cc-ccpa-ok">'+v(n.ccpaAcceptButton)+"</button></div></div>",document.body.appendChild(o),document.getElementById("cc-ccpa-ok").addEventListener("click",function(){F(Y(),"ccpa_acknowledge"),o.classList.remove("cc-show"),_("cc:update")}),re("ccpa"),_("cc:ready"),_("cc:update"),void pe()}var n,o;te(),J(),j(),ne(),re("gdpr"),_("cc:ready"),pe()})}fe&&(l=fe,E(fe)),function(){if(!g&&r.autoBlockPatterns&&r.autoBlockPatterns.length){g=!0;var e=document.createElement.bind(document),t=Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,"src");t&&t.set&&(document.createElement=function(){var n=e.apply(document,arguments);if("script"!==(arguments[0]||"").toLowerCase())return n;function o(e,t){if(t&&"string"==typeof t&&!e.getAttribute("data-cc")&&!e.getAttribute("data-cc-auto")&&-1===t.indexOf("cookieconsent")){var n=I(t);!n||l&&l[n]||(e.type="text/plain",e.setAttribute("data-cc",n),e.setAttribute("data-cc-auto","1"))}}Object.defineProperty(n,"src",{configurable:!0,enumerable:!0,get:function(){return t.get.call(this)},set:function(e){return o(this,e),t.set.call(this,e)}});var c=n.setAttribute.bind(n);return n.setAttribute=function(e,t){return"src"===e.toLowerCase()&&o(n,t),c(e,t)},n}),new MutationObserver(function(e){e.forEach(function(e){e.addedNodes.forEach(function(e){if(1===e.nodeType){var t=[];"SCRIPT"===e.tagName&&t.push(e),e.querySelectorAll&&e.querySelectorAll("script[src]").forEach(function(e){t.push(e)}),t.forEach(function(e){if(!(e.getAttribute("data-cc-auto")||e.getAttribute("data-cc")||e.src&&-1!==e.src.indexOf("cookieconsent"))){var t=I(e.src);t&&(l&&l[t]||(e.type="text/plain",e.setAttribute("data-cc",t),e.setAttribute("data-cc-auto","1")))}})}})})}).observe(document.documentElement,{childList:!0,subtree:!0})}}(),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",me):me();var ge=null;function ye(){for(var e=[],t=[],n=[],o={},c={},r=document.querySelectorAll("script[src]"),a=0;a<r.length;a++)r[a].src&&e.push(r[a].src);for(var i=document.querySelectorAll("iframe[src]"),s=0;s<i.length;s++)i[s].src&&e.push(i[s].src);if(window.performance&&performance.getEntriesByType)for(var d=performance.getEntriesByType("resource"),u=0;u<d.length;u++){var l=d[u].name||"";if(l){-1===e.indexOf(l)&&e.push(l);var p=d[u].initiatorType||"";if("script"===p||"iframe"===p||"xmlhttprequest"===p||"fetch"===p||-1!==l.indexOf(".js"))try{var f=new URL(l),m=f.hostname;if(m===location.hostname||m==="www."+location.hostname||"www."+m===location.hostname){var g=f.pathname.match(/\/wp-content\/plugins\/([^\/]+)\//);g&&!c[g[1]]&&(c[g[1]]=!0,n.push({slug:g[1],url:l}))}else o[m]||(o[m]=!0,t.push(m))}catch(e){}}}return{allUrls:e,thirdPartyDomains:t,pluginScripts:n}}var ve={};function he(e){var t={set_by:"",set_by_plugin:""},n=window.__ccCookieSetters;if(!n||!Object.prototype.hasOwnProperty.call(n,e)||!n[e])return t;try{var o=new URL(String(n[e]).replace(/:\d+(:\d+)?$/,""));if(/^(chrome-extension|moz-extension|safari-web-extension|safari-extension):$/.test(o.protocol))return t.set_by="browser-extension",t;var c=o.hostname;if(c===location.hostname||c==="www."+location.hostname||"www."+c===location.hostname){t.set_by="first-party";var r=o.pathname.match(/\/wp-content\/(?:plugins|themes)\/([^\/]+)\//);r&&(t.set_by_plugin=r[1].toLowerCase())}else t.set_by=c.toLowerCase()}catch(e){}return t}function we(e,t,n){if("browser-extension"===n.set_by)return!0;var o=window.__ccVisitorCookieRules;if(!o||!o.tools)return!1;if(o.allow&&-1!==o.allow.indexOf(e))return!1;if(n.set_by)return!1;for(var c=0;c<o.tools.length;c++){for(var r=o.tools[c],a=-1!==(r.n||[]).indexOf(e),i=0;!a&&i<(r.p||[]).length;i++)0===e.indexOf(r.p[i])&&(a=!0);if(a){for(var s=0;s<t.length;s++)for(var d=0;d<(r.d||[]).length;d++){var u=String(t[s]).toLowerCase(),l=r.d[d];if(u===l||u.slice(-l.length-1)==="."+l)return!1}return!0}}return!1}function be(){var t=window.__ccClassified||[],n=window.__ccUnclassified||[],o=window.__ccBeaconUrl;if(o){var c,r={};for(c=0;c<t.length;c++)r[t[c]]=!0;for(c=0;c<n.length;c++)r[n[c]]=!0;var a=Object.keys(ve);for(c=0;c<a.length;c++)r[a[c]]=!0;var i=Object.keys(s);for(c=0;c<i.length;c++)r[i[c]]=!0;var d={},u=[],l=document.cookie.split(";");for(c=0;c<l.length;c++){var p=l[c].indexOf("="),f=(p>-1?l[c].substring(0,p):l[c]).trim(),m=p>-1?l[c].substring(p+1).trim():"";f&&(u.push(f),d[f]=m)}var g=[];for(c=0;c<u.length;c++)if(!r[u[c]]){for(var y=!1,v=Object.keys(s),h=0;h<v.length;h++)if(s[v[h]].prefix&&0===u[c].indexOf(v[h])){y=!0;break}y||g.push(u[c])}if(0!==g.length){for(var w=ye(),b=w.thirdPartyDomains,k=[],_=0;_<w.pluginScripts.length;_++)k.push(w.pluginScripts[_].slug);for(var x=location.pathname,E=0;E<g.length;E++){var A=g[E],S=!!e[A]&&!r[A],L=ke(d[A]||""),C=he(A);if(!we(A,b,C)||window.__ccDiscovery&&window.__ccDiscovery.active){var B=JSON.stringify({cookie_name:A,script_domains:b,plugin_scripts:k,page_path:x,pre_existing:S,value_shape:L,set_by:C.set_by,set_by_plugin:C.set_by_plugin});ve[A]=!0,navigator.sendBeacon&&navigator.sendBeacon(o,new Blob([B],{type:"application/json"}))}else ve[A]=!0}}}}function ke(e){if(!e||0===e.length)return"empty";if("true"===e||"false"===e||"1"===e||"0"===e)return"boolean";if(/^\d+$/.test(e))return e.length>12?"long-numeric":"numeric";if(/^\d+\.\d+$/.test(e))return"decimal";if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return"uuid";if(/^[A-Za-z0-9+\/]{20,}={0,2}$/.test(e))return"base64";if(/^(GA\d+\.\d+\.\d+\.\d+|G-[A-Z0-9]+)/.test(e))return"ga-id";try{if("object"==typeof JSON.parse(decodeURIComponent(e)))return"json"}catch(e){}return/^[a-f0-9]{24,}$/i.test(e)?"hex-id":/^\d{10,13}$/.test(e)?"timestamp":e.length<=20?"short-string":e.length<=100?"medium-string":"long-string"}setTimeout(be,3e3),setTimeout(be,15e3),setTimeout(be,45e3),window.addEventListener("cc:update",function(){setTimeout(be,3e3)}),window.addEventListener("pagehide",be)}();
  • cookieflame/trunk/cookieflame.php

    r3768250 r3769546  
    44 * Plugin URI:        https://cookieflame.com
    55 * Description:       A lightweight, self-hosted cookie consent manager with Google Consent Mode v2, IAB TCF v2.2, automatic script blocking, multi-region compliance (GDPR/CCPA/LGPD/POPIA), passive cookie monitoring, and server-side geo detection via Cloudflare.
    6  * Version:           5.2.0
     6 * Version:           5.3.0
    77 * Requires at least: 6.0
    88 * Requires PHP:      8.0
    … …  
    1919}
    2020
    21 define( 'COOKIEFLAME_VERSION', '5.2.0' );
     21define( 'COOKIEFLAME_VERSION', '5.3.0' );
    2222define( 'COOKIEFLAME_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    2323define( 'COOKIEFLAME_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    … …  
    219219// cookieconsent-discovery.js via the cookieflameAdmin global.
    220220require_once COOKIEFLAME_PLUGIN_DIR . 'includes/service-detectors.php';
     221require_once COOKIEFLAME_PLUGIN_DIR . 'includes/consent-tools.php';
    221222
    222223/* ═══════════════════════════════════════════════════════════
    … …  
    11401141    foreach ( $ignored as $ig ) {
    11411142        if ( ( $ig['name'] ?? '' ) === $cookie_name ) {
    1142             if ( ! $discovery_active ) {
     1143            // Cookies ignored automatically as visitor-side cookies stay ignored
     1144            // during discovery unless their source now checks out (the admin's
     1145            // own browser may carry the same extension).
     1146            $still_visitor = ! empty( $ig['auto'] ) && '' !== cookieflame_visitor_cookie_reason( $cookie_name, $script_domains, $set_by );
     1147            if ( ! $discovery_active || $still_visitor ) {
    11431148                return new WP_REST_Response( array( 'received' => true, 'status' => 'ignored' ), 200 );
    11441149            }
    … …  
    11501155            break;
    11511156        }
     1157    }
     1158
     1159    // Another consent tool's cookie that doesn't run here, or a cookie a
     1160    // browser extension wrote: it came with the visitor's browser, not from
     1161    // this site. Record it on the ignore list with the reason.
     1162    $visitor_reason = cookieflame_visitor_cookie_reason( $cookie_name, $script_domains, $set_by );
     1163    if ( '' !== $visitor_reason ) {
     1164        global $wpdb;
     1165        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- MySQL named lock; a lock must not be cached.
     1166        $locked = (int) $wpdb->get_var( "SELECT GET_LOCK( 'cookieflame_beacon', 5 )" );
     1167        wp_cache_delete( 'alloptions', 'options' );
     1168        cookieflame_ignore_visitor_cookie( $cookie_name, $visitor_reason );
     1169        if ( $locked ) {
     1170            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Releases the MySQL named lock taken above.
     1171            $wpdb->query( "SELECT RELEASE_LOCK( 'cookieflame_beacon' )" );
     1172        }
     1173        return new WP_REST_Response( array( 'received' => true, 'status' => 'visitor_cookie' ), 200 );
    11521174    }
    11531175
    … …  
    13641386function cookieflame_sanitize_set_by( $value ) {
    13651387    $value = strtolower( trim( is_scalar( $value ) ? (string) $value : '' ) );
    1366     if ( 'first-party' === $value ) {
     1388    if ( 'first-party' === $value || 'browser-extension' === $value ) {
    13671389        return $value;
    13681390    }
    … …  
    18791901
    18801902    if ( $action === 'restore' ) {
     1903        // Restoring a cookie that was ignored automatically as a visitor-side
     1904        // cookie is the admin overruling that check, so remember it.
     1905        foreach ( $ignored as $ig ) {
     1906            if ( ( $ig['name'] ?? '' ) === $name && ! empty( $ig['auto'] ) ) {
     1907                $allow   = (array) get_option( 'cookieflame_visitor_cookie_allow', array() );
     1908                $allow[] = $name;
     1909                update_option( 'cookieflame_visitor_cookie_allow', array_values( array_unique( $allow ) ) );
     1910            }
     1911        }
    18811912        $ignored = array_values( array_filter( $ignored, function( $ig ) use ( $name ) {
    18821913            return ( $ig['name'] ?? '' ) !== $name;
    … …  
    22162247        'ignoredCookies'      => get_option( 'cookieflame_ignored_cookies', array() ),
    22172248        'ignoredCookiesUrl'   => rest_url( 'cookieflame/v1/ignored-cookies' ),
     2249        'visitorCookieRules'  => cookieflame_visitor_cookie_rules(),
     2250        'siteScriptDomains'   => cookieflame_site_script_domains(),
    22182251        'serviceDetectors'    => cookieflame_get_service_detectors(),
    22192252    );
    … …  
    24702503        . "set:function(v){try{var n=String(v).split('=')[0].trim();"
    24712504        . "if(n&&!Object.prototype.hasOwnProperty.call(w,n)){"
    2472         . "var u=String((new Error()).stack||'').match(/https?:\\/\\/[^\\s)'\"]+/g)||[];"
     2505        . "var u=String((new Error()).stack||'').match(/(?:https?|chrome-extension|moz-extension|safari-web-extension|safari-extension):\\/\\/[^\\s)'\"]+/g)||[];"
    24732506        . "w[n]=u.length>1?u[1]:'';}}catch(e){}"
    24742507        . "d.set.call(this,v);}});"
    … …  
    26432676        // active discovery only; no REST URLs or nonces are exposed.
    26442677        $parts[] = 'window.cookieflameAdmin=Object.assign(window.cookieflameAdmin||{},' . wp_json_encode( array(
    2645             'serviceDetectors' => cookieflame_get_service_detectors(),
     2678            'serviceDetectors'   => cookieflame_get_service_detectors(),
     2679            'ignoredCookieNames' => array_values( array_filter( array_column( (array) get_option( 'cookieflame_ignored_cookies', array() ), 'name' ) ) ),
    26462680        ) ) . ');';
    26472681    }
    … …  
    26802714    if ( ! cookieflame_monitoring_is_off() ) {
    26812715        $parts[] = 'window.__ccBeaconUrl=' . wp_json_encode( rest_url( 'cookieflame/v1/beacon' ) ) . ';';
     2716        // Other consent tools' cookies to skip (see includes/consent-tools.php).
     2717        $parts[] = 'window.__ccVisitorCookieRules=' . wp_json_encode( cookieflame_visitor_cookie_rules() ) . ';';
    26822718    }
    26832719
    … …  
    26862722        implode( '', $parts ),
    26872723        array(
     2724            'id'                    => 'cookieflame-config',
    26882725            'data-cfasync'          => 'false',
    26892726            'data-no-optimize'      => '1',
    … …  
    30143051    }
    30153052
    3016     // 2. Duplicate consent plugins
     3053    // 2. Duplicate consent plugins. CookieFlame's own page settings name
     3054    // other consent tools (the cookies it skips, cookie names it has seen),
     3055    // so they are left out of this check.
     3056    $consent_html   = preg_replace( '#<script\b[^>]*\bid=(["\'])cookieflame-config\1[^>]*>[\s\S]*?</script>#i', '', $html );
    30173057    $consent_checks = array(
    30183058        array( 'cookiebot.com',      'Cookiebot' ),
    … …  
    30263066        array( 'usercentrics.eu',    'Usercentrics' ),
    30273067    );
     3068    // The check reads the page's code, so the other tool may come from a
     3069    // plugin, a page builder's custom code, the theme or a scripts plugin:
     3070    // say what was found and where, not just "deactivate the plugin".
     3071    $consent_page = substr( (string) wp_parse_url( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ?? '/' ) ), PHP_URL_PATH ), 0, 120 );
    30283072    foreach ( $consent_checks as $check ) {
    3029         if ( stripos( $html, $check[0] ) !== false ) {
     3073        if ( stripos( (string) $consent_html, $check[0] ) !== false ) {
    30303074            $conflicts[] = array(
    30313075                'type'        => 'duplicate_consent',
    30323076                'severity'    => 'critical',
    3033                 'title'       => $check[1] . ' consent plugin detected',
    3034                 'description' => 'Running two cookie consent plugins simultaneously causes unpredictable behaviour. Visitors may see two consent banners, scripts may be blocked or allowed inconsistently, and consent signals can conflict.',
    3035                 'action'      => 'Deactivate ' . $check[1] . ' if you intend to use CookieFlame as your consent solution.',
     3077                'title'       => $check[1] . ' found on your pages',
     3078                'description' => 'Your pages load ' . $check[1] . '\'s code as well as CookieFlame. Two consent tools on one site can show visitors two banners, block or allow scripts unpredictably, and mix up the choices visitors make. Found on ' . ( in_array( $consent_page, array( '', '/' ), true ) ? 'your home page' : $consent_page ) . '. In that page\'s source, search for "' . $check[0] . '".',
     3079                'action'      => 'Remove ' . $check[1] . '\'s code if CookieFlame is your consent tool. It isn\'t always a plugin: also check your page builder\'s custom code (for example Elementor > Custom Code), your theme\'s header, and any header and footer scripts plugin. Then visit a page on your site again to update this check.',
    30363080            );
    30373081        }
    … …  
    46224666                            </th>
    46234667                            <th style="width:14%">
    4624                                 <?php esc_html_e( 'Source', 'cookieflame' ); ?>
    4625                                 <button type="button" class="cf-admin-help-btn" data-help-title="<?php esc_attr_e( 'Source', 'cookieflame' ); ?>" data-help="<?php esc_attr_e( 'How this cookie was identified. Known: recognised from our built-in database. Auto-Identified: classified automatically by the CookieFlame service. Edited: you changed the classification from the default. Reported: discovered by visitors browsing your site. Unclassified: not yet identified.', 'cookieflame' ); ?>">?</button>
     4668                                <?php esc_html_e( 'Status', 'cookieflame' ); ?>
     4669                                <?php
     4670                                $cookieflame_status_help = array(
     4671                                    array( '', __( 'Where each cookie stands, and what you can do with it.', 'cookieflame' ) ),
     4672                                    array( __( 'Not identified yet', 'cookieflame' ), __( 'CookieFlame doesn\'t know this cookie yet, so your banner doesn\'t list it. Red rows were reported by visitors or found by your scan. Yellow rows were only seen in your own browser.', 'cookieflame' ) ),
     4673                                    array( __( 'Known, Auto-Identified, Classified', 'cookieflame' ), __( 'In CookieFlame\'s built-in list, named by the CookieFlame service, or sorted by Cookie Discovery or by you in this table.', 'cookieflame' ) ),
     4674                                    array( __( 'Added by you, Changed by you', 'cookieflame' ), __( 'Your own settings. Re-identify only ever suggests changes to these.', 'cookieflame' ) ),
     4675                                    array( __( 'Lock as correct (Pro)', 'cookieflame' ), __( 'Once you\'ve checked a cookie, CookieFlame won\'t change it, even when you re-identify.', 'cookieflame' ) ),
     4676                                    array( __( 'Auto-Identify, Re-identify (Pro)', 'cookieflame' ), __( 'Ask the CookieFlame service what the cookie is.', 'cookieflame' ) ),
     4677                                    array( __( 'Remove', 'cookieflame' ), __( 'Asks why first. If your site doesn\'t use it any more, it\'s taken off your list and comes back if it\'s seen again. If it isn\'t from your site, it\'s hidden for good on the Permanently Ignored list.', 'cookieflame' ) ),
     4678                                );
     4679                                $cookieflame_status_html = '';
     4680                                foreach ( $cookieflame_status_help as $cookieflame_help_item ) {
     4681                                    $cookieflame_status_html .= '<p>' . ( $cookieflame_help_item[0] ? '<strong>' . esc_html( $cookieflame_help_item[0] ) . ':</strong> ' : '' ) . esc_html( $cookieflame_help_item[1] ) . '</p>';
     4682                                }
     4683                                ?>
     4684                                <button type="button" class="cf-admin-help-btn" data-help-title="<?php esc_attr_e( 'Status', 'cookieflame' ); ?>" data-help-html="<?php echo esc_attr( '<div class="cf-admin-help-list">' . $cookieflame_status_html . '</div>' ); ?>">?</button>
    46264685                            </th>
    46274686                        </tr>
  • cookieflame/trunk/readme.txt

    r3768250 r3769546  
    55Tested up to: 7.1
    66Requires PHP: 8.0
    7 Stable tag: 5.2.0
     7Stable tag: 5.3.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    195195== Changelog ==
    196196
     197= 5.3.0 =
     198* Cookie monitoring now ignores cookies that come with a visitor's browser rather than from your site. Some browser extensions write the "already accepted" cookies of other consent tools (such as Complianz, CookieYes, Cookiebot, OneTrust or Didomi) onto every site they visit, and these were being reported as your site's cookies. A consent tool's cookie is now ignored when that tool doesn't run on your site: its plugin isn't active, its script isn't on the page, and no script on the page wrote the cookie.
     199* Cookies written by a browser extension's own code are ignored too.
     200* Cookies ignored this way go on the Permanently Ignored list with the reason, and the list now shows why each cookie was ignored. Restoring one keeps it monitored from then on.
     201* The Cookie Manager and the Cookie Discovery count leave these cookies out too, including ones your own browser extensions add. Cookies that came with your browser during Cookie Discovery go on the Permanently Ignored list with the reason. Cookies on the Permanently Ignored list no longer show in the Cookie Manager just because they are in your own browser.
     202* On update, other consent tools' cookies that were already reported are moved to the Permanently Ignored list. Cookies you classified yourself are left alone.
     203* Fixed: the "consent plugin detected" conflict warning could be set off by CookieFlame's own list of cookie names rather than by another consent plugin on the page.
     204* Cookie Manager: the last column is now "Status". Each cookie shows one label for where it stands (Known, Auto-Identified, Classified, Added by you, Changed by you, or Not identified yet), and cookies that aren't identified yet say where they came from: found by your scan, reported by visitors, or seen in your own browser.
     205* Cookie Manager: Remove and Ignore permanently are now one "Remove…" link that asks why. "My site doesn't use it any more" takes the cookie off your list, and it comes back if it's seen again. "It's not from my site" puts it on the Permanently Ignored list for good.
     206* Cookie Manager: with CookieFlame Pro, the padlock is now a "Lock as correct" button. A locked cookie keeps its vendor, category and purpose, even when you re-identify.
     207* Cookie Manager: the small help buttons beside each link are gone. Each link explains itself when you hover over it, and the Status column's help button explains every label and action.
     208* Improved: when another consent tool's code is on your pages, the warning now says so in plain words ("Cookiebot found on your pages"), names the page and the text to look for, and lists where such code usually lives (plugins, page builder custom code such as Elementor > Custom Code, the theme's header, or a header and footer scripts plugin). It used to say a plugin was installed even when the code came from somewhere else.
     209
    197210= 5.2.0 =
    198211* Cookie monitoring now records which script first wrote each new cookie: a third-party script's domain, or the WordPress plugin or theme that set it. Previously only the full list of third-party scripts on the page was known. Cookie values are never read or sent.
    … …  
    229242== Upgrade Notice ==
    230243
     244= 5.3.0 =
     245Cookie monitoring now ignores other consent tools' cookies that visitors' browser extensions add, so they no longer appear as your site's cookies. The Cookie Manager's last column is tidier and easier to understand.
     246
    231247= 5.2.0 =
    232248Better cookie detection: CookieFlame now records which script set each new cookie and keeps checking after consent is given.
  • cookieflame/trunk/uninstall.php

    r3766806 r3769546  
    3131delete_option( 'cookieflame_dismissed_cookies' );
    3232delete_option( 'cookieflame_ignored_cookies' );
     33delete_option( 'cookieflame_visitor_cookie_allow' );
     34delete_option( 'cookieflame_visitor_cleanup' );
    3335delete_option( 'cookieflame_discovery_backup' );
    3436delete_option( 'cookieflame_maxmind_last_update' );
Note: See TracChangeset for help on using the changeset viewer.