|
1 | | -/* Simple Analytics - Privacy friendly analytics (docs.simpleanalytics.com/script; 2023-05-03; 0dec; v11) */ |
| 1 | +/* Simple Analytics - Privacy-first analytics (docs.simpleanalytics.com/script; 2025-06-13; dd16; v12) */ |
2 | 2 | /* eslint-env browser */ |
3 | 3 |
|
4 | 4 | (function ( |
|
112 | 112 | return Array.isArray(csv) |
113 | 113 | ? csv |
114 | 114 | : isString(csv) && csv.length |
115 | | - ? csv.split(/, ?/) |
116 | | - : []; |
| 115 | + ? csv.split(/, ?/) |
| 116 | + : []; |
117 | 117 | }; |
118 | 118 |
|
119 | 119 | var isObject = function (object) { |
|
193 | 193 | attr(scriptElement, namespaceText) || |
194 | 194 | defaultNamespace; |
195 | 195 |
|
196 | | - var metadataObject = window[namespace + "_metadata"]; |
197 | 196 | var appendMetadata = function (metadata, data) { |
| 197 | + var metadataObject = window[namespace + "_metadata"]; |
198 | 198 | if (isObject(metadataObject)) metadata = assign(metadata, metadataObject); |
199 | 199 | var metadataCollectorFunction = window[metadataCollector]; |
200 | 200 | if (!isFunction(metadataCollectorFunction)) return metadata; |
|
218 | 218 | overwriteOptions.strictUtm || |
219 | 219 | attr(scriptElement, "strict-utm") == trueText; |
220 | 220 |
|
221 | | - var getQueryParams = function (ignoreSource) { |
| 221 | + var getQueryParams = function (ignoreSource, overwriteSearch) { |
222 | 222 | return ( |
223 | | - loc.search |
| 223 | + (overwriteSearch || loc.search) |
224 | 224 | .slice(1) |
225 | 225 | .split("&") |
226 | 226 | .filter(function (keyValue) { |
|
433 | 433 | // PAYLOAD FOR BOTH PAGE VIEWS AND EVENTS |
434 | 434 | // |
435 | 435 |
|
| 436 | + var phantom = window.phantom; |
436 | 437 | var bot = |
437 | 438 | nav.webdriver || |
438 | 439 | window.__nightmare || |
439 | 440 | window.callPhantom || |
440 | 441 | window._phantom || |
441 | | - window.phantom || |
| 442 | + (phantom && !phantom.solana) || |
442 | 443 | window.__polypane || |
443 | 444 | window._bot || |
444 | 445 | isBotAgent || |
|
512 | 513 | var lastSendPath; |
513 | 514 |
|
514 | 515 | var getReferrer = function () { |
| 516 | + // Customers can overwrite their referrer, here we check for that |
| 517 | + var overwrittenReferrer = |
| 518 | + overwriteOptions.referrer || attr(scriptElement, "referrer"); |
| 519 | + |
515 | 520 | return ( |
516 | | - (doc.referrer || "") |
| 521 | + (overwrittenReferrer || doc.referrer || "") |
517 | 522 | .replace(locationHostname, definedHostname) |
518 | 523 | .replace(/^https?:\/\/((m|l|w{2,3}([0-9]+)?)\.)?([^?#]+)(.*)$/, "$4") |
519 | 524 | .replace(/^([^/]+)$/, "$1") || undefinedVar |
|
554 | 559 | // sendData will assign payload to request |
555 | 560 | sendData(append, undefinedVar, trueVar); |
556 | 561 | } else { |
557 | | - nav.sendBeacon(fullApiUrl + "/append", stringify(append)); |
| 562 | + try { |
| 563 | + nav.sendBeacon.bind(nav)(fullApiUrl + "/append", stringify(append)); |
| 564 | + } catch (e) { |
| 565 | + // Fallback for browsers throwing "Illegal invocation" when the URL is invalid |
| 566 | + sendData(append, undefinedVar, trueVar); |
| 567 | + } |
558 | 568 | } |
559 | 569 | }; |
560 | 570 |
|
|
651 | 661 | isPushState, |
652 | 662 | deleteSourceInfo, |
653 | 663 | sameSite, |
654 | | - metadata |
| 664 | + query, |
| 665 | + metadata, |
| 666 | + callback |
655 | 667 | ) { |
656 | 668 | if (isPushState) sendOnLeave("" + payload.page_id, trueVar); |
657 | 669 | if (collectDataOnLeave) payload.page_id = uuid(); |
658 | 670 |
|
659 | 671 | var currentPage = definedHostname + getPath(); |
660 | 672 |
|
661 | | - sendData({ |
662 | | - id: payload.page_id, |
663 | | - type: pageviewText, |
664 | | - referrer: !deleteSourceInfo || sameSite ? referrer : null, |
665 | | - query: getQueryParams(deleteSourceInfo), |
| 673 | + sendData( |
| 674 | + { |
| 675 | + id: payload.page_id, |
| 676 | + type: pageviewText, |
| 677 | + referrer: !deleteSourceInfo || sameSite ? referrer : null, |
| 678 | + query: query || getQueryParams(deleteSourceInfo), |
666 | 679 |
|
667 | | - metadata: stringify(metadata), |
668 | | - }); |
| 680 | + metadata: stringify(metadata), |
| 681 | + }, |
| 682 | + callback |
| 683 | + ); |
669 | 684 |
|
670 | 685 | previousReferrer = referrer; |
671 | 686 | referrer = currentPage; |
|
675 | 690 |
|
676 | 691 | var sameSite, userNavigated; |
677 | 692 |
|
678 | | - var pageview = function (isPushState, pathOverwrite, metadata) { |
| 693 | + var pageview = function ( |
| 694 | + isPushState, |
| 695 | + pathOverwrite, |
| 696 | + metadata, |
| 697 | + callbackRaw |
| 698 | + ) { |
| 699 | + if (!callbackRaw && isFunction(metadata)) callbackRaw = metadata; |
| 700 | + var callback = isFunction(callbackRaw) ? callbackRaw : function () {}; |
| 701 | + var querySearch; |
| 702 | + if (isString(pathOverwrite) && pathOverwrite.indexOf("?") > -1) { |
| 703 | + // keep query from manual path |
| 704 | + var parts = pathOverwrite.split("?"); |
| 705 | + pathOverwrite = parts.shift(); |
| 706 | + querySearch = "?" + parts.join("?"); |
| 707 | + } |
679 | 708 | // Obfuscate personal data in URL by dropping the search and hash |
680 | 709 | var path = getPath(pathOverwrite); |
681 | 710 |
|
|
738 | 767 | : falseVar; |
739 | 768 |
|
740 | 769 | // We set unique variable based on pushstate or back navigation, if no match we check the referrer |
741 | | - page.unique = isPushState || userNavigated ? falseVar : !sameSite; |
| 770 | + page.unique = |
| 771 | + /__cf_/.test(getReferrer()) || isPushState || userNavigated |
| 772 | + ? falseVar |
| 773 | + : !sameSite; |
742 | 774 |
|
743 | 775 | metadata = appendMetadata(metadata, { |
744 | 776 | type: pageviewText, |
|
747 | 779 |
|
748 | 780 | var triggerSendPageView = function () { |
749 | 781 | fetchedHighEntropyValues = trueVar; |
| 782 | + var delSrc = |
| 783 | + isPushState || userNavigated || !collectMetricByString("r"); |
750 | 784 | sendPageView( |
751 | 785 | isPushState, |
752 | | - isPushState || userNavigated || !collectMetricByString("r"), // r = referrers |
| 786 | + delSrc, // r = referrers |
753 | 787 | sameSite, |
754 | | - metadata |
| 788 | + querySearch ? getQueryParams(delSrc, querySearch) : undefinedVar, |
| 789 | + metadata, |
| 790 | + callback |
755 | 791 | ); |
756 | 792 | }; |
757 | 793 |
|
|
841 | 877 | } |
842 | 878 |
|
843 | 879 | if (autoCollect) pageview(); |
844 | | - else { |
845 | | - window.sa_pageview = function (path, metadata) { |
846 | | - pageview(0, path, metadata); |
847 | | - }; |
848 | | - } |
| 880 | + |
| 881 | + window.sa_pageview = function (path, metadata, callback) { |
| 882 | + pageview(0, path, metadata, callback); |
| 883 | + }; |
| 884 | + |
849 | 885 |
|
850 | 886 | ///////////////////// |
851 | 887 | // EVENTS |
|
938 | 974 | {"hostname":INSTALL_OPTIONS.hostname,"collectDnt":typeof INSTALL_OPTIONS.collect_dnt === 'boolean' ? INSTALL_OPTIONS.collect_dnt : null,"mode":INSTALL_OPTIONS.hash_mode ? 'hash' : 'normal',"strictUtm":INSTALL_OPTIONS.advanced_settings_toggle && INSTALL_OPTIONS.strict_utm,"allowParams":INSTALL_OPTIONS.advanced_settings_toggle && INSTALL_OPTIONS.allow_url_parameters,"nonUniqueHostnames":INSTALL_OPTIONS.advanced_settings_toggle && INSTALL_OPTIONS.non_unique_hostnames,"ignorePages":INSTALL_OPTIONS.advanced_settings_toggle && INSTALL_OPTIONS.ignore_pages,"namespace":INSTALL_OPTIONS.overwrite_namespace && INSTALL_OPTIONS.namespace}, |
939 | 975 | INSTALL_OPTIONS.custom_domain || "queue.simpleanalyticscdn.com", |
940 | 976 | "", |
941 | | - "cloudflare_11", |
| 977 | + "cloudflare_12", |
942 | 978 | "sa" |
943 | 979 | ); |
0 commit comments