There's a ton of duplicated javascript that can be consolidated - see below for all functions that could be created in a centralized util to replace multiple (3-5 instances of each).
Logic for checking if data layer is enabled and getting a reference to it
var dataLayerEnabled;
var dataLayer;
var isDataLayerEnabled = function() {
if (dataLayerEnabled == null) {
dataLayerEnabled = document.body.hasAttribute('data-cmp-data-layer-enabled');
}
return dataLayerEnabled;
}
var getDataLayer = function() {
if (isDataLayerEnabled() && dataLayer == null) {
var dataLayerName = document.body.getAttribute('data-cmp-data-layer-name') || 'adobeDataLayer';
dataLayer = window[dataLayerName] = window[dataLayerName] || [];
}
return dataLayer;
}
Logic for constructing component JS objects
var constructComponents = function (selector, is, clazz) {
var elements = document.querySelectorAll(selector);
for (var i = 0; i < elements.length; i++) {
new clazz({ element: elements[i], options: readData(elements[i], is) });
}
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
var body = document.querySelector('body');
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
// needed for IE
var nodesArray = [].slice.call(mutation.addedNodes);
if (nodesArray.length > 0) {
nodesArray.forEach(function (addedNode) {
if (addedNode.querySelectorAll) {
var elementsArray = [].slice.call(addedNode.querySelectorAll(selector));
elementsArray.forEach(function (element) {
new clazz({ element: element, options: readData(element, is) });
});
}
});
}
});
});
observer.observe(body, {
subtree: true,
childList: true,
characterData: true,
});
};
var readData = function (element, is) {
var data = element.dataset;
var options = [];
var capitalized = is;
capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
var reserved = ['is', 'hook' + capitalized];
for (var key in data) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
var value = data[key];
if (key.indexOf(NS) === 0) {
key = key.slice(NS.length);
key = key.charAt(0).toLowerCase() + key.substring(1);
if (reserved.indexOf(key) === -1) {
options[key] = value;
}
}
}
}
return options;
};
var setupProperties = function (options, properties) {
var transformedProperties = {};
for (var key in properties) {
if (Object.prototype.hasOwnProperty.call(properties, key)) {
var property = properties[key];
var value = null;
if (options && options[key] != null) {
if (property && typeof property.transform === 'function') {
value = property.transform(options[key]);
} else {
value = options[key];
}
}
if (value === null) {
// value still null, take the property default
value = properties[key]['default'];
}
transformedProperties[key] = value;
}
}
return transformedProperties;
};
Logic for grabbing a components hookable children elements
var getCacheElements = function(wrapper, is, is_dash = is) {
var elements = {};
elements.self = wrapper;
var hooks = elements.self.querySelectorAll('[data-' + NS + '-hook-' + is_dash + ']');
for (var i = 0; i < hooks.length; i++) {
var hook = hooks[i];
if (hook.closest('.' + NS + '-' + is_dash) === elements.self) {
// only process own elements
var capitalized = is;
capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
var key = hook.dataset[NS + 'Hook' + capitalized];
if (elements[key]) {
if (!Array.isArray(elements[key])) {
var tmp = elements[key];
elements[key] = [tmp];
}
elements[key].push(hook);
} else {
elements[key] = hook;
}
}
}
return elements;
};
Logic for getting a component's datalayer ID
var getDataLayerId = function(item) {
if (item) {
if (item.dataset.cmpDataLayer) {
return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0];
} else {
return item.id;
}
}
return null;
};
Logic for panel containers (Accordion, Carousel, Tabs) listening for panel selection events from author
var subscribeToAuthorPanelSelect = function(panelContainer, type, navigate) {
if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) {
/*
* Editor message handling:
* - subscribe to "cmp.panelcontainer" message requests sent by the editor frame
* - check that the message data panel container type is correct and that the id (path) matches this specific component
* - if so, route the "navigate" operation to enact a navigation of the component based on index data
*/
window.CQ = window.CQ || {};
window.CQ.CoreComponents = window.CQ.CoreComponents || {};
window.CQ.CoreComponents.MESSAGE_CHANNEL =
window.CQ.CoreComponents.MESSAGE_CHANNEL ||
new window.Granite.author.MessageChannel('cqauthor', window);
window.CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage(
'cmp.panelcontainer',
function (message) {
if (
message.data &&
message.data.type === type &&
message.data.id === panelContainer.dataset['cmpPanelcontainerId']
) {
if (message.data.operation === 'navigate') {
navigate(message.data.index);
}
}
},
);
}
};
There's a ton of duplicated javascript that can be consolidated - see below for all functions that could be created in a centralized util to replace multiple (3-5 instances of each).
Logic for checking if data layer is enabled and getting a reference to it
Logic for constructing component JS objects
Logic for grabbing a components hookable children elements
Logic for getting a component's datalayer ID
Logic for panel containers (Accordion, Carousel, Tabs) listening for panel selection events from author