Skip to content
Draft
1 change: 1 addition & 0 deletions 404.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ layout: default
heading_anchors: false
nav_exclude: true
---
{% include ubi.html %}

## Oops, this isn't the page you're looking for.

Expand Down
19 changes: 19 additions & 0 deletions _includes/ubi.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!--*****************************************
Add an include of ubi.html to the markdown file
to log page loads or redirect errors
***************************************** -->
<script type="module" src="{{ '/assets/js/ubi.js' | relative_url }}"></script>
<script>
window.addEventListener("DOMContentLoaded", function (e) {
try{
if(document.getElementById('oops-this-isnt-the-page-youre-looking-for')){
logUbiMessage('404_redirect', 'ERROR', `Loaded ${document.title} instead of ${window.location.href}`);
}
else {
logUbiMessage('page_load', 'INFO',`Loaded ${document.title} from ${window.location.pathname}`);
}
} catch(error){
console.warn('UBI error: ' + error);
}
});
</script>
6 changes: 4 additions & 2 deletions _layouts/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,13 @@ <h2>Related articles</h2>
anchors.add().remove('.subfooter h1, .subfooter h2');
</script>
{% endif %}
<script src="{{ '/assets/js/timeme.min.js' | relative_url }}"></script>
<script type="module" src="{{ '/assets/js/ubi.js' | relative_url }}"></script>
{% if site.search_enabled == false and site.use_custom_search == true %}
<script src="{{ '/assets/js/search.js' | relative_url }}"></script>
<script type="module" defer src="{{ '/assets/js/search.js' | relative_url }}"></script>
{% endif %}
<script src="{{ '/assets/js/copy-button.js' | relative_url }}"></script>
<script src="{{ '/assets/js/nav-scroll.js' | relative_url }}"></script>
<script src="{{ '/assets/js/listener.js' | relative_url }}"></script>
<script type="module" src="{{ '/assets/js/listener.js' | relative_url }}"></script>
</body>
</html>
14 changes: 12 additions & 2 deletions _layouts/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

{% include header.html %}


<div class="main-content-wrap-home">
<div id="main-content" class="main-content" role="main">
{{ content }}
Expand All @@ -30,7 +29,18 @@
</div>

{% include footer.html %}
<script src="{{ '/assets/js/search.js' | relative_url }}"></script>
<script src="{{ '/assets/js/timeme.min.js' | relative_url }}"></script>
<script type="text/javascript">
// Initialize library and start tracking time
TimeMe.initialize({
currentPageName: window.location.pathname,
idleTimeoutInSeconds: 5 // seconds
});
TimeMe.startTimer(window.location.pathname);
</script>

<script type="module" src="{{ '/assets/js/ubi.js' | relative_url }}"></script>
<script type="module" defer src="{{ '/assets/js/search.js' | relative_url }}"></script>
<script src="{{ '/assets/js/home-listener.js' | relative_url }}"></script>
</body>
</html>
20 changes: 19 additions & 1 deletion assets/js/listener.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as UBI from "./ubi.js";

const yesButton = document.getElementById('yes');
const noButton = document.getElementById('no');
const numCharsLabel = document.getElementById('num-chars');
Expand Down Expand Up @@ -48,7 +50,7 @@ function updateTextArea() {
}

// calculate the number of characters remaining
counter = 350 - commentTextArea.value.length;
const counter = 350 - commentTextArea.value.length;
numCharsLabel.innerText = counter + " characters left";
}

Expand All @@ -68,6 +70,22 @@ function sendFeedback() {

if (helpful === 'none' && comment === 'none') return;

try{
let e = new UBI.UbiEvent('user_feedback', {
message: `Relevance: ${helpful}, Comment: ${comment}`,
event_attributes:{
url:location.pathname,
helpful:helpful,
comment:comment
}
});
e.message_type = 'USER';
UBI.logEvent(e);

} catch(e){
console.warn(`UBI Error: ${e}`)
}

// split the comment into 100-char parts because of GA limitation on custom dimensions
const commentLines = ["", "", "", ""];
for (let i = 0; i <= (comment.length - 1)/100; i++) {
Expand Down
108 changes: 107 additions & 1 deletion assets/js/search.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import * as UBI from "./ubi.js";

(() => {

document.addEventListener('DOMContentLoaded', () => {
//
// Search field behaviors
Expand Down Expand Up @@ -66,6 +69,10 @@
highlightResult(e.target?.closest('.top-banner-search--field-with-results--field--wrapper--search-component--search-results--result'));
}, true);

elResults.addEventListener('click', e => {
clickResult(e.target?.closest('.top-banner-search--field-with-results--field--wrapper--search-component--search-results--result'));
}, true);

const debounceInput = () => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(doSearch, 300);
Expand Down Expand Up @@ -97,6 +104,7 @@
lastQuery = query;

abortPreviousCalls();
UBI.clearCache();

elSpinner?.classList.add(CLASSNAME_SPINNING);
if (!_showingResults) document.documentElement.classList.add('search-active');
Expand All @@ -118,10 +126,18 @@
if (!Array.isArray(data?.results) || data.results.length === 0) {
return showNoResults();
}
const [qid, result_ids] = UBI.cacheQueryResults(data.results);
let ubi_event = makeUbiEvent('search', 'search_results', {
id:UBI.hash(query),
query:query,
result_ids:result_ids
});
UBI.logEvent(ubi_event);

const chunks = data.results.map(result => result
? `
<div class="${searchResultClassName}">
<a href="${sanitizeAttribute(result.url)}">
<a id="${UBI.hash(result.url)}" href="${sanitizeAttribute(result.url)}">
<cite>${getBreadcrumbs(result)}</cite>
${sanitizeText(result.title || 'Unnamed Document')}
</a>
Expand Down Expand Up @@ -206,6 +222,8 @@
const searchResultClassName = 'top-banner-search--field-with-results--field--wrapper--search-component--search-results--result';
if (!node || !_showingResults || node.classList.contains(CLASSNAME_HIGHLIGHTED)) return;

// ToDo: UBI item_hover can go here...hover, but no click implies irrelevance in results

elResults.querySelectorAll(`.${searchResultClassName}.highlighted`).forEach(el => {
el.classList.remove(CLASSNAME_HIGHLIGHTED);
});
Expand Down Expand Up @@ -247,14 +265,102 @@
}
};

const clickResult = node => {
const searchResultClassName = 'top-banner-search--field-with-results--field--wrapper--search-component--search-results--result';
if (!node || !_showingResults) return;

const link = node.querySelector('a');
if(link){
logUbiEvent('item_click', link);
}

return true;
};

const navToHighlightedResult = () => {
const searchResultClassName = 'top-banner-search--field-with-results--field--wrapper--search-component--search-results--result';
elResults.querySelector(`.${searchResultClassName}.highlighted a[href]`)?.click?.();
};

/**
* Find item and position clicked
* Modifies the ubi event data if the item is found
* @param {UbiEventData} ubiEvent - UBI.UbiEventData object
* @param {*} link - link clicked
* @returns
*/
const setUbiClickData = (ubiEvent, link) => {
ubiEvent.event_attributes.position = new UBI.UbiPosition({x:link.offsetLeft, y:link.offsetTop});

if(link.hasAttribute('id')){
let id = link.id;
//try to find the item ordinal within the result list
let resultIds = sessionStorage.getItem('result_ids');
if(resultIds != null && resultIds.length > 0){
resultIds = resultIds.split(',');
let ordinal = resultIds.findIndex( i => i===id );
//if found, ordinal starts at 1
if(ordinal != -1){
ubiEvent.event_attributes.position.ordinal = ordinal + 1;
if(ubiEvent.message == undefined || ubi_event.message == null){
ubiEvent.message = `Clicked item ${ordinal+1} out of ${resultIds.length}`
}

try{
let searchResults = JSON.parse(sessionStorage.getItem('search_results'));
let obj = searchResults[id];
if(obj != null){
ubiEvent.event_attributes.object = obj;
ubiEvent.event_attributes.position.trail = getBreadcrumbs(obj);
}
}catch(e){
console.warn(e);
}
}
}
}
return ubiEvent;
};


/**
* Helper function to populate the event structure for common
* event data elements
* @param {*} name - name of the event to build
* @param {*} event_type - type of event to tease out event parameters
* @param {*} data - an object associated with the event
* @returns
*/
const makeUbiEvent = (name, event_type, data=null) => {
let e = new UBI.UbiEvent(name);

if(name == 'search'){
e.message_type = 'QUERY';
e.message = data.search_term;
e.event_attributes.object = data;
} else if(name == 'item_click') {
e = setUbiClickData(e, data);
} else if(e.event_attributes.object == null){
e.event_attributes.object = data;
}
return e;
}


/**
* A method to retrofit and funnel gtag logging to ubi
* @param {*} name
* @param {*} data
*/
const logUbiEvent = (name, data) => {
let event = makeUbiEvent(name, 'default', data)
UBI.logEvent(event);
};

const recordEvent = (name, data) => {
try {
gtag?.('event', name, data);
logUbiEvent(name, data);
} catch (e) {
// Do nothing
}
Expand Down
2 changes: 2 additions & 0 deletions assets/js/timeme.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading