File: D:/web/hyperflight/! bs3/gtm-lib.asp
<%
' (SS,13/10/23) Google Tag Manager module
' (SS,19/10/23) Version 1.00
' (SS,20/10/23) Version 1.01 - Added id and and google_business_vertical for Google Ads Remarketing
' (SS,24/10/23) Version 1.02 - Added dataLayer = [] to DoGoogleTagManagerScript, not actually required, just to keep James/Jimmy happy
' (SS,23/10/23) Version 1.03 - Added EventContactForm, renamed DoEcommerceEvents to DoEvents
' (SS,10/11/23) Version 1.04 - Tidied up enhanced conversion event and tested it, also event contact form code (not yet in use)
' (SS,15/01/24) Version 1.05 - Changes to add Backona version of the GTM script, new properties BackonaAnalyticsEnabled and BackonaAnalyticsDomain, new sub DoBackonaGTMScript
' (SS,Public Sub DoGoogleTagManagerScript
' (SS,27/02/24) Version 1.06 - Change to Public Sub DoGoogleTagManagerScript, removed at Backona's request the dataLayer = [] line that this was causing an issue with their settings.
Class GTMDef
' can't use a const, using a variable instead, see https://stackoverflow.com/questions/21052084/constant-inside-class
' Const GTM_DATA_INDENT_SPACES = 2
Private INDENT_SPACES ' used as a constant
Private FGTMContainerID, FAffiliation
Private FBackonaAnalyticsEnabled, FBackonaAnalyticsDomain ' (SS,15/1/24)
Private FDataIndentLevel, FGTMData, FItemDataCount
' Private FProductCode, FProductName, FProductVariant, FBrand, FCategory, FSubcategory, FPrice
Private FEventName, FCurrency, FDiscountCode, FSearchTerm ' FDiscount
Private FTransactionID, FPurchaseValue, FPurchaseTax, FShipping
' field constants
Private F_EVENT_NAME, F_PRODUCT_CODE, F_PRODUCT_NAME, F_PRODUCT_VARIANT, F_PRICE, F_QUANTITY, F_BRAND, F_CATEGORY, F_SUBCATEGORY, F_FIELD_COUNT
' items array
Private FItems, FFieldNo
' event name constants
Private E_VIEW_ITEM, E_VIEW_CART, E_ADD_TO_CART, E_REMOVE_FROM_CART, E_BEGIN_CHECKOUT, E_ADD_SHIPPING_INFO, E_ADD_PAYMENT_INFO, E_PURCHASE, E_SEARCH, E_ENHANCED_CONVERSION
' Private FViewItemCount, FViewCartCount, FAddToCartCount, FRemoveFromCartCount
' (SS,23/10/23)
Private FContactFormName
' (SS,23/10/23) enhanced conversion data
Private FHasEnhancedConversionData, FEmail, FPhone, FFirstName, FLastName, FCity, FRegion, FPostcode, FCountryCode
Private Function FieldNo
FFieldNo = FFieldNo + 1
F_FIELD_COUNT = FFieldNo
FieldNo = FFieldNo
End Function
Private Sub Class_Initialize
INDENT_SPACES = 2
FGTMContainerID = ""
FAffiliation = ""
FBackonaAnalyticsEnabled = False ' (SS,15/1/24)
FBackonaAnalyticsDomain = "" ' (SS,15/1/24)
'FGTMDataIndentLevel = 0
'FGTMData = ""
'FItemDataCount = 0
E_VIEW_ITEM = "view_item"
E_VIEW_CART = "view_cart"
E_ADD_TO_CART = "add_to_cart"
E_REMOVE_FROM_CART = "remove_from_cart"
E_BEGIN_CHECKOUT = "begin_checkout"
E_ADD_SHIPPING_INFO = "add_shipping_info"
E_ADD_PAYMENT_INFO = "add_payment_info"
E_PURCHASE = "purchase"
E_SEARCH = "search"
E_ENHANCED_CONVERSION = "enhanced_conversion" ' custom (not built in event) to pass on enhanced conversion data
FEventName = ""
FCurrency = "GBP"
' FDiscount = 0
FDiscountCode = ""
FSearchTerm = ""
FHasEnhancedConversionData = False
FTransactionID = ""
FPurchaseValue = 0
FPurchaseTax = 0
FShipping = 0
' field contants
FFieldNo = -1 ' held in FieldNo and increments in each call
F_EVENT_NAME = FieldNo
F_PRODUCT_CODE = FieldNo
F_PRODUCT_NAME = FieldNo
F_PRODUCT_VARIANT = FieldNo
F_PRICE = FieldNo
F_QUANTITY = FieldNo
F_BRAND = FieldNo
F_CATEGORY = FieldNo
F_SUBCATEGORY = FieldNo
' (SS,23/10/23)
FContactFormName = ""
ReDim FItems(F_FIELD_COUNT, -1)
End Sub
Public Sub Class_Terminate
End Sub
' contruct with parameter see http://johna.compoutpost.com/blog/806/classic-asp-class-constructors-with-parameters/
Public Default Function Init(AContainerID, AAffiliation)
FGTMContainerID = AContainerID
FAffiliation = AAffiliation
Set Init = Me
End Function
' (SS,15/1/24)
Public Property Let BackonaAnalyticsEnabled(AValue)
FBackonaAnalyticsEnabled = AValue
End Property
' (SS,15/1/24)
Public Property Let BackonaAnalyticsDomain(AValue)
FBackonaAnalyticsDomain = AValue
End Property
' Public Property Let Discount(AValue)
' FDiscount = AValue
' End Property
Public Property Let DiscountCode(AValue)
FDiscountCode = AValue
End Property
Public Property Let TransactionID(AValue)
FTransactionID = AValue
End Property
Public Property Let PurchaseValue(AValue)
FPurchaseValue = AValue
End Property
Public Property Let PurchaseTax(AValue)
FPurchaseTax = AValue
End Property
Public Property Let Shipping(AValue)
FShipping = AValue
End Property
Private Sub SetDataIndentLevel(AIndentLevel)
FDataIndentLevel = AIndentLevel
End Sub
Private Sub IncDataIndentLevel
FDataIndentLevel = FDataIndentLevel + 1
End Sub
Private Sub DecDataIndentLevel
FDataIndentLevel = FDataIndentLevel - 1
End Sub
Private Function GetDataIndentSpaces
GetDataIndentSpaces = Space(INDENT_SPACES * FDataIndentLevel)
End Function
Private Sub StartGTMData
FGTMData = ""
End Sub
Private Sub EndGTMData
End Sub
Private Sub ShowGTMData
Response.Write FGTMData
End Sub
Private Sub StartItem
FItemDataCount = 0
FGTMData = FGTMData + GetDataIndentSpaces + "{" + NL
IncDataIndentLevel
End Sub
Private Sub NextItem
FGTMData = FGTMData + "," + NL
End Sub
Private Sub EndItem
DecDataIndentLevel
FGTMData = FGTMData + NL + GetDataIndentSpaces + "}"
End Sub
' *** ? to be improved with JSON object handling
' NB. thought number needs to be passed without quotes around it, later discovered the debug view multiplies by 1000000 causing confusion
Private Sub AddGTMDataLine(AName, AValue)
Dim LValue
LValue = NB(AValue) ' converts to string including the null
If LValue = "" Then Exit Sub
' add quote around string types only
Dim LQuote
If VarType(AValue) = VBString Then
LQuote = """"
Else
LQuote = ""
End If
If FItemDataCount > 0 Then FGTMData = FGTMData + "," + NL
FItemDataCount = FItemDataCount + 1
FGTMData = FGTMData + GetDataIndentSpaces + AName + ": " + LQuote + JavaScriptStrEscape(AValue) + LQuote
End Sub
Private Function GetNameValueStr(AName, AValue, AAppendComma)
Dim LResult, LValue
LValue = NB(AValue) ' converts to string including the null
If LValue <> "" Then
' add quote around string types only
Dim LQuote
If VarType(AValue) = VBString Then
LQuote = """"
Else
LQuote = ""
End If
LResult = GetDataIndentSpaces + AName + ": " + LQuote + JavaScriptStrEscape(AValue) + LQuote + iif(AAppendComma, ",", "") + NL
Else
LResult = ""
End If
GetNameValueStr = LResult
End Function
Private Sub ShowEcommerceValue(AName, AValue, AAppendComma)
Response.Write GetNameValueStr(AName, AValue, AAppendComma)
End Sub
Private Sub ShowEcommerceScriptHeader
' may not need first line, to check // Clear the previous ecommerce object.
' (SS,10/11/23) removed the following, don't think it's required
' dataLayer.push({ ecommerce: null });
%>
<script><%
End Sub
Private Sub ShowEcommerceEventHeader(AEventName)
' (SS,23/10/23) added following variable to also do user_data for enhanced_conversion as well as existing ecommerce for the events
Dim LDataName
If AEventName = E_ENHANCED_CONVERSION Then
LDataName = "user_data"
Else
LDataName = "ecommerce"
End If
%>
dataLayer.push({
event: "<%=AEventName%>",
<%=LDataName%>: {
<%
End Sub
Private Sub ShowEcommerceEventFooter
%> }
});<%
End Sub
Private Sub ShowEcommerceScriptFooter
%>
</script>
<%
End Sub
' (SS,23/10/23)
' (SS,10/11/23) not yet used or fully developed
Private Sub ShowContactFormEvent(AFormName)
%>
dataLayer.push({
event: "contact_form",
form_name: ""<%=AFormName%>")
}
});
<%
End Sub
Private Sub ShowEcommerceEventItemsStart
Response.Write GetDataIndentSpaces + "items: [" + NL
End Sub
Private Sub ShowEcommerceEventItemsEnd
Response.Write NL + GetDataIndentSpaces + "]" + NL
End Sub
' returns true if there is at least one event
Private Function IsEvent
IsEvent = UBound(FItems, 2) > -1
End Function
Private Function EventCount(AEventName)
Dim i, LResult
LResult = 0
For i = 0 To UBound(FItems, 2)
If FItems(F_EVENT_NAME, i) = AEventName Then
LResult = LResult + 1
End If
Next
EventCount = LResult
End Function
Private Function EventValue(AEventName)
Dim i, LResult
LResult = 0
For i = 0 To UBound(FItems, 2)
If FItems(F_EVENT_NAME, i) = AEventName Then
LResult = Round(LResult + Round(FItems(F_PRICE, i) * FItems(F_QUANTITY, i), 2), 2)
End If
Next
EventValue = LResult
End Function
Private Sub ShowEcommerceEventItems(AEventName)
StartGTMData
Dim i, LIndex, LProductCode
LIndex = 0
For i = 0 To UBound(FItems, 2)
If FItems(F_EVENT_NAME, i) = AEventName Then
If LIndex > 0 Then NextItem
StartItem
' only item_id and item_name are required
'AddGTMDataLine "event_name_**_to_remove", FItems(F_EVENT_NAME, i)
LProductCode = FItems(F_PRODUCT_CODE, i)
AddGTMDataLine "item_id", LProductCode
AddGTMDataLine "item_name", FItems(F_PRODUCT_NAME, i)
AddGTMDataLine "item_variant", FItems(F_PRODUCT_VARIANT, i)
AddGTMDataLine "affiliation", FAffiliation
AddGTMDataLine "index", LIndex
AddGTMDataLine "item_brand", FItems(F_BRAND, i)
AddGTMDataLine "item_category", FItems(F_CATEGORY, i)
AddGTMDataLine "item_category2", FItems(F_SUBCATEGORY, i)
AddGTMDataLine "price", FItems(F_PRICE, i)
AddGTMDataLine "quantity", FItems(F_QUANTITY, i)
' for Google Ads remarketing, which requires id instead of item_id and also google_business_vertical, don't include special DISCOUNT item
If LProductCode <> "DISCOUNT" Then
AddGTMDataLine "id", LProductCode
AddGTMDataLine "google_business_vertical", "retail"
End If
EndItem
LIndex = LIndex + 1
End If
Next
EndGTMData
ShowGTMData
End Sub
Public Sub EventViewItem
FEventName = E_VIEW_ITEM
End Sub
Public Sub EventViewCart
FEventName = E_VIEW_CART
End Sub
Public Sub EventAddToCart
FEventName = E_ADD_TO_CART
End Sub
Public Sub EventRemoveFromCart
FEventName = E_REMOVE_FROM_CART
End Sub
Public Sub EventBeginCheckout
FEventName = E_BEGIN_CHECKOUT
End Sub
Public Sub EventAddShippingInfo
FEventName = E_ADD_SHIPPING_INFO
End Sub
Public Sub EventAddPayment
FEventName = E_ADD_PAYMENT_INFO
End Sub
Public Sub EventPurchase
FEventName = E_PURCHASE
End Sub
' (SS,23/10/23)
Public Sub AddEnchancedConversionData(AEmail, APhone, AFirstName, ALastName, ACity, ARegion, APostcode, ACountryCode)
FEventName = E_ENHANCED_CONVERSION ' doesn't really get used for anything
FHasEnhancedConversionData = True
FEmail = AEmail
FPhone = APhone
FFirstName = AFirstName
FLastName = ALastName
FCity = ACity
FRegion = ARegion
FPostcode = APostcode
FCountryCode = ACountryCode
End Sub
' (SS,19/10/23) *** to be completed
Public Sub EventSearch(ASearchTerm)
FEventName = E_SEARCH
FSearchTerm = ASearchTerm
End Sub
' quick single line method of adding an item, to be called after calling EventViewCart or EventAddToCart
Public Sub AddItem(AProductCode, AProductName, AProductVariant, APrice, AQuantity, ABrand, ACategory, ASubcategory)
' add to array to store new item
Dim i
i = UBound(FItems, 2) + 1
Redim Preserve FItems(UBound(FItems, 1), i) ' resize the array preserving the existing data
FItems(F_EVENT_NAME, i) = FEventName
FItems(F_PRODUCT_CODE, i) = AProductCode
FItems(F_PRODUCT_NAME, i) = AProductName
FItems(F_PRODUCT_VARIANT, i) = AProductVariant
FItems(F_PRICE, i) = APrice
FItems(F_QUANTITY, i) = AQuantity
FItems(F_BRAND, i) = ABrand
FItems(F_CATEGORY, i) = ACategory
FItems(F_SUBCATEGORY, i) = ASubcategory
End Sub
' (SS,23/10/23)
' (SS,10/11/23) not yet used/developed fully
Public Sub EventContactForm(AFormName)
FContactFormName = AFormName
End Sub
Public Function Enabled
Enabled = FGTMContainerID <> ""
End Function
' (SS,25/10/23) calls DoEvents below, to be removed once DoEvents has been added to apputils.asp
Public Sub DoEcommerceEvents
DoEvents
End Sub
' (SS,23/10/23) renamed DoEcommerceEvents to DoEvents
Public Sub DoEvents
If Not Enabled Then Exit Sub
' If IsEvent Then ' if at least one event
' (SS,10/11/23) replaced above with following
If IsEvent Or FContactFormName <> "" Then ' if at least one event
ShowEcommerceScriptHeader
If IsEvent Then
DoEcommerceEvent E_REMOVE_FROM_CART
DoEcommerceEvent E_ADD_TO_CART
DoEcommerceEvent E_VIEW_CART
DoEcommerceEvent E_VIEW_ITEM
DoEcommerceEvent E_BEGIN_CHECKOUT
DoEcommerceEvent E_ADD_SHIPPING_INFO
DoEcommerceEvent E_ADD_PAYMENT_INFO
DoEcommerceEvent E_PURCHASE
End If
DoEnchancedConversionData ' will only be added for E_PURCHASE event
' (SS,23/10/23) custom contact form event
' *** to be continued
If FContactFormName <> "" Then
ShowContactFormEvent FContactFormName
End If
ShowEcommerceScriptFooter
End If
End Sub
Private Sub DoEcommerceEvent(AEventName)
If EventCount(AEventName) > 0 Then
ShowEcommerceEventHeader AEventName
SetDataIndentLevel 2
ShowEcommerceValue "currency", FCurrency, True
'ShowEcommerceValue "value", FValue, True
If AEventName = E_PURCHASE Then
ShowEcommerceValue "transaction_id", FTransactionID, True
End If
If AEventName = E_ADD_SHIPPING_INFO Or AEventName = E_ADD_PAYMENT_INFO Or AEventName = E_PURCHASE Then
ShowEcommerceValue "value", FPurchaseValue, True
Else
ShowEcommerceValue "value", EventValue(AEventName), True
End If
' coupon for begin_checkout, add_shipping_info, add_payment_info, purchase
If FDiscountCode <> "" And AEventName = E_BEGIN_CHECKOUT Or AEventName = E_ADD_SHIPPING_INFO Or AEventName = E_ADD_PAYMENT_INFO Or AEventName = E_PURCHASE Then
ShowEcommerceValue "coupon", FDiscountCode, True
End If
'? to add payment info with payment type
If AEventName = E_PURCHASE Then
ShowEcommerceValue "shipping", FShipping, True
ShowEcommerceValue "tax", FPurchaseTax, True
End If
ShowEcommerceEventItemsStart
ShowEcommerceEventItems AEventName
ShowEcommerceEventItemsEnd
ShowEcommerceEventFooter
End If
End Sub
' (SS,23/10/23)
' (SS,10/11/23) finished off
Private Sub DoEnchancedConversionData
If FHasEnhancedConversionData Then
ShowEcommerceEventHeader E_ENHANCED_CONVERSION
SetDataIndentLevel 2
ShowEcommerceValue "email", FEmail, True
ShowEcommerceValue "first_name", FFirstName, True
ShowEcommerceValue "last_name", FLastName, True
ShowEcommerceValue "city", FCity, True
ShowEcommerceValue "region", FRegion, True
ShowEcommerceValue "postal_code", FPostcode, True
ShowEcommerceValue "country", FCountryCode, False
ShowEcommerceEventFooter
End If
End Sub
Public Sub DoGoogleTagManagerScript
If Enabled Then
' (SS,15/1/24) added following to do the alternative Backona version instead of standard if enabled
If FBackonaAnalyticsEnabled Then
DoBackonaGTMScript
Exit Sub
Else
' (SS,24/10/23) added dataLayer = []; requested by James/Jimmy, even though it's not required because it's in the Google Tag Manager script below (not obvious), may take it out later
' (SS,27/02/24) removed at Backona's request that this was causing an issue with their settings.
'<script>
'dataLayer = [];
'</script>
%>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','<%=FGTMContainerID%>');</script>
<!-- End Google Tag Manager -->
<%
End If
End If
End Sub
' (SS,15/1/24) do the alternative Backona version
Private Sub DoBackonaGTMScript
Dim LDomain
LDomain = FBackonaAnalyticsDomain
%>
<!-- Google Tag Manager - Backona Version -->
<script>
(function (w, d, s, l, i) {
w[l] = w[l] || [];
w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" });
var f = d.getElementsByTagName(s)[0], j = d.createElement(s), dl = l != "dataLayer" ? "&l=" + l : "";
j.async = true;
if (window.alx === false) {
fetch("https://www.googletagmanager.com/gtm.js?id=" + i, {mode: 'no-cors', credentials: 'same-origin'})
.then(function(){
j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl;
}).catch(function(){
window.alx = true;
j.src = "https://<%=FBackonaAnalyticsDomain%>/customer/1?id=" + i + dl;
})
} else {
j.src = "https://<%=FBackonaAnalyticsDomain%>/customer/1?id=" + i + dl;
}
f.parentNode.insertBefore(j, f);
})(window, document, "script", "dataLayer", "<%=FGTMContainerID%>");
</script>
<!-- End Google Tag Manager - Backona Version -->
<%
End Sub
' (SS,5/10/23) following called from HTMLBodyEnd adds recommended <noscript> GTM script for Javascript being disabled the Tag Assistant recommends it (otherwise I wouldn't have added it)
Public Sub DoGoogleTagManagerNoScript
If GTMEnabled Then
%>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=<%=FGTMContainerID%>"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<%
End If
End Sub
End Class
%>