mobify-analytics-sdk
Version:
A unified way to setup Mobify analytics
2,425 lines • 77.2 kB
JavaScript
// [AIV] Build version: 0.0.24
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["mobify-analytics-sdk"] = factory();
else
root["mobify-analytics-sdk"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "https://localhost:8443/public/bundle/";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.u = exports.t = exports.debug = exports.setDebug = exports.init = exports.version = undefined;
var _configuration = __webpack_require__(2);
Object.keys(_configuration).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _configuration[key];
}
});
});
var _events = __webpack_require__(5);
Object.keys(_events).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _events[key];
}
});
});
__webpack_require__(3);
var configuration = _interopRequireWildcard(_configuration);
var eventSender = _interopRequireWildcard(_events);
var _tester = __webpack_require__(10);
var tester = _interopRequireWildcard(_tester);
var _utils = __webpack_require__(6);
var utils = _interopRequireWildcard(_utils);
var _sandy = __webpack_require__(12);
var _sandy2 = _interopRequireDefault(_sandy);
var _debugger = __webpack_require__(13);
var _debugger2 = _interopRequireDefault(_debugger);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
var version = exports.version = '0.0.24';
/**
* Initialize the SDK - the first method to call
*
* @param {Object} options - options include config, integrations
* instrumentDCL - if we setup domReady listener
* pollFrequency - frequency of polling function
* SPA - if was force single page app mode - even if not on progressive
* firstPageview - if we want to send first pageview
* mobilePageview - if we want to send mobile pageview
*/
/**
* Analytic SDK - Main function from analyticsSDK
*
* @module analyticsSDK/main
*/
var init = exports.init = function init(options) {
configuration.parseConfig(options.config);
if (options.integrations) {
utils.forEach(options.integrations, function (integration) {
if (integration === 'sandy') {
eventSender.addIntegration('sandy', new _sandy2.default());
}
});
}
configuration.setupInstrument(options);
return exports;
};
/**
* Set the debug mode
*/
var setDebug = exports.setDebug = function setDebug() {
eventSender.addIntegration('debugger', new _debugger2.default());
return exports;
};
/**
* Sets debug and highlight mode
*/
var debug = exports.debug = function debug() {
setDebug();
configuration.highlight();
return exports;
};
var t = exports.t = tester;
var u = exports.u = utils;
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.listInstrumented = exports.setupInstrument = exports.registerRouteChange = exports.parseConfig = exports.getConfig = exports.highlight = exports.clear = exports.clearInstrumented = exports.uninstrument = exports.contentDimension = exports.checkerDimension = exports.elementTarget = exports.checker = exports.extractor = exports.lifeCycle = exports.components = exports.events = exports.routeTimer = exports.numberOfTimerTrigger = exports.instrumentTimer = undefined;
__webpack_require__(3);
var _domready = __webpack_require__(4);
var _domready2 = _interopRequireDefault(_domready);
var _events = __webpack_require__(5);
var eventSender = _interopRequireWildcard(_events);
var _utils = __webpack_require__(6);
var utils = _interopRequireWildcard(_utils);
var _dimensions = __webpack_require__(7);
var dimensionsModule = _interopRequireWildcard(_dimensions);
var _events2 = __webpack_require__(9);
var eventsModule = _interopRequireWildcard(_events2);
var _configUtils = __webpack_require__(8);
var configUtils = _interopRequireWildcard(_configUtils);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* Responsible for configuration file parsing and implementation
*
* @module analyticsSDK/configuration
*/
var logger = window.Mobify.Debug('analytic-sdk');
/**
* Poll timer
*/
var instrumentTimer = exports.instrumentTimer = null;
var numberOfTimerTrigger = exports.numberOfTimerTrigger = 0;
/**
* Max number of cycles until dropping untilSeen elements
*/
var maxUntilSeen = 20;
/**
* Setup route monitor
*/
var routeTimer = exports.routeTimer = null;
var capturesToAdd = [];
var styleSheetAdded = false;
/**
* Access this list of constants by using `analyticsSDK.c.<name>`
*
* @namespace c
*/
var events = exports.events = {
/**
* A constant for defining AddToCart event
*
* @const
*/
addToCart: 'addToCart',
removeFromCart: 'removeFromCart',
search: 'search',
formInteraction: 'formInteraction'
};
var components = exports.components = {
/**
* Capture the events only in progressive
* we check for `window.Progressive`
* @const
*/
PWA: 'PWA',
/**
* Capture in adaptive
*
* @const
*/
Adaptive: 'Adaptive',
/**
* Capture the events only in astro
* we check for `window.Astro`
* @const
*/
Astro: 'Astro'
};
var lifeCycle = exports.lifeCycle = configUtils.lifeCycle;
var extractorToString = function extractorToString(funcName) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
return JSON.stringify([funcName].concat(args));
};
var c = configUtils.c;
/**
* Various extractor used to extract value
*/
var extractor = exports.extractor = {};
utils.inEach(c.extractor, function (name) {
extractor[name] = function () {
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return extractorToString.apply(undefined, [name].concat(args));
};
});
/**
* Return Boolean expression for defining checks
*/
var checker = exports.checker = {};
utils.inEach(c.checker, function (name) {
checker[name] = function () {
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
args[_key3] = arguments[_key3];
}
return extractorToString.apply(undefined, [name].concat(args));
};
});
/**
* Use target element to determine the dimension
*/
var elementTarget = exports.elementTarget = {};
utils.inEach(c.elementTarget, function (name) {
elementTarget[name] = function () {
for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
args[_key4] = arguments[_key4];
}
return extractorToString.apply(undefined, [name].concat(args));
};
});
/**
* Dimensions using checker
*/
var checkerDimension = exports.checkerDimension = {
funnel_step: 'funnel_step'
/**
* Dimensions using extractor
*/
};var contentDimension = exports.contentDimension = {
product_category: 'product_category',
product_id: 'product_id',
product_name: 'product_name',
product_stock: 'product_stock',
product_price: 'product_price'
};
var addStyleSheet = function addStyleSheet() {
var styleEl = document.createElement('style');
document.head.appendChild(styleEl);
var styleSheet = styleEl.sheet;
styleSheet.insertRule('.mobify-highlight {-webkit-box-shadow: 0px 0px 10px 10px rgba(255,0,0,1) !important; -moz-box-shadow: 0px 0px 10px 10px rgba(255,0,0,1) !important; box-shadow: 0px 0px 10px 10px rgba(255,0,0,1) !important; }', 0); // eslint-disable-line max-len
};
var uninstrument = exports.uninstrument = function uninstrument() {
logger.log('Removing listeners');
utils.forEach(capturesToAdd, function (c) {
if (styleSheetAdded) {
c.applyToElements(function (e) {
return e.classList.remove('mobify-highlight');
});
}
c.uninstrument();
});
eventSender.clearDimensions();
return true;
};
/**
* Clear current instrumentation
*/
var clearInstrumented = exports.clearInstrumented = function clearInstrumented() {
uninstrument();
capturesToAdd = [];
exports.numberOfTimerTrigger = numberOfTimerTrigger = 0;
if (instrumentTimer) {
clearInterval(instrumentTimer);
exports.instrumentTimer = instrumentTimer = null;
}
};
/**
* Clear instrumentation and timers
*/
var clear = exports.clear = function clear() {
clearInstrumented();
exports.numberOfTimerTrigger = numberOfTimerTrigger = 0;
if (routeTimer) {
clearInterval(routeTimer);
exports.routeTimer = routeTimer = null;
}
};
/**
* Highlight available elements
*/
var highlight = exports.highlight = function highlight() {
logger.log('Highlight ' + capturesToAdd.length + ' elements');
if (!styleSheetAdded) {
addStyleSheet();
styleSheetAdded = true;
}
utils.forEach(capturesToAdd, function (c) {
c.applyToElements(function (e) {
return e.classList.add('mobify-highlight');
});
});
return true;
};
var currentConfig = null;
/**
* Exposing current config used
*/
var getConfig = exports.getConfig = function getConfig() {
return currentConfig;
};
/**
* Instrument the page
*
* @param {Object} config - configuration of instrumentation
*/
var parseConfig = exports.parseConfig = function parseConfig(config) {
if (capturesToAdd) {
clearInstrumented();
}
if (!config) {
logger.log('WARNING: no config rule set');
return null;
}
currentConfig = config;
if (config.events) {
logger.log('Initializing config with ' + utils.size(config.events) + ' rules');
utils.inEach(config.events, function (eventConfig, eventType) {
var newCaptures = eventsModule.parseEventConfig(eventConfig, eventType);
if (newCaptures) {
var _capturesToAdd;
(_capturesToAdd = capturesToAdd).push.apply(_capturesToAdd, _toConsumableArray(newCaptures));
}
});
}
if (config.dimensions) {
utils.inEach(config.dimensions, function (dimConfigWithComponents, dimName) {
var newCaptures = [];
if (dimName in checkerDimension) {
newCaptures = dimensionsModule.parseCheckerDimensionConfig(dimConfigWithComponents, dimName, eventSender.setDimension);
} else if (dimName in contentDimension) {
newCaptures = dimensionsModule.parseDimensionConfig(dimConfigWithComponents, dimName, eventSender.setDimension);
} else {
logger.log('Incorrect dimension in ' + dimName);
}
if (newCaptures) {
var _capturesToAdd2;
(_capturesToAdd2 = capturesToAdd).push.apply(_capturesToAdd2, _toConsumableArray(newCaptures));
}
});
}
return capturesToAdd;
};
/**
* Signal that a new pageview is triggered by PWA
*
* @param {Object} dimension to send along with routeChange
* @param {Boolean} noPageviewEvent - if we dont want to send mobile pageview
*/
var registerRouteChange = exports.registerRouteChange = function registerRouteChange(dimensions, noPageviewEvent) {
uninstrument();
// we have to reparse the config since url have changed
parseConfig(currentConfig);
// eslint-disable-next-line no-use-before-define
setupInstrument({
instrumentDCL: false,
mobilePageview: !noPageviewEvent
});
if (!noPageviewEvent) {
// send a virtual pageview, after dimension have been updated
var pageviewEvent = {
action: 'pageview',
location: document.location.href
};
if (dimensions) {
utils.extend(pageviewEvent, dimensions);
}
eventSender.send(pageviewEvent);
}
};
/**
* Setup instrumentation
*
* @param {Object} options - options include config, integrations
* instrumentDCL - if we setup domReady listener
* pollFrequency - frequency of polling function
* SPA - if was use single page app mode (monitor route change)
* firstPageview - if we want to send first pageview
* mobilePageview - if we want to send mobile pageview
*/
var setupInstrument = exports.setupInstrument = function setupInstrument(options) {
var _lifeCycleQueue;
var instrumentDCL = options.instrumentDCL,
pollFrequency = options.pollFrequency,
SPA = options.SPA,
firstPageview = options.firstPageview,
mobilePageview = options.mobilePageview;
var lifeCycleQueue = (_lifeCycleQueue = {}, _defineProperty(_lifeCycleQueue, lifeCycle.ASAP, []), _defineProperty(_lifeCycleQueue, lifeCycle.DCL, []), _defineProperty(_lifeCycleQueue, 'polling', []), _lifeCycleQueue);
utils.forEach(capturesToAdd, function (c) {
if (c.lifeCycle in lifeCycleQueue) {
lifeCycleQueue[c.lifeCycle].push(c);
} else {
lifeCycleQueue.polling.push(c);
}
});
// instrumenting ASAP elements
utils.forEach(lifeCycleQueue[lifeCycle.ASAP], function (c) {
c.instrument();
});
if (lifeCycleQueue[lifeCycle.DCL].length) {
if (instrumentDCL) {
// instrumenting DCL elements in one call
(0, _domready2.default)(function () {
utils.forEach(lifeCycleQueue[lifeCycle.DCL], function (c) {
return c.instrument();
});
});
} else {
utils.forEach(lifeCycleQueue[lifeCycle.DCL], function (c) {
return c.instrument();
});
}
}
// instrument untilSeen elements
if (instrumentTimer) {
clearInterval(instrumentTimer);
}
var pollElements = lifeCycleQueue.polling;
if (pollElements.length) {
exports.instrumentTimer = instrumentTimer = setInterval(function () {
var nextElementSet = [];
exports.numberOfTimerTrigger = numberOfTimerTrigger += 1;
utils.forEach(pollElements, function (c) {
var foundElement = c.instrument();
if (c.lifeCycle === lifeCycle.monitor) {
nextElementSet.push(c);
} else if (!(foundElement && c.hasValue()) && numberOfTimerTrigger < maxUntilSeen) {
nextElementSet.push(c);
}
});
pollElements = nextElementSet;
if (!nextElementSet.length) {
clearInterval(instrumentTimer);
exports.instrumentTimer = instrumentTimer = null;
}
}, pollFrequency || 500);
}
if ((SPA || configUtils.isProgressive()) && !routeTimer) {
var lastRoute = document.location.href;
// NOTE: we can use history.onpush here but we risk interfering with hook
exports.routeTimer = routeTimer = setInterval(function () {
if (document.location.href !== lastRoute) {
logger.log('[Analytics SDK] Route change: ' + lastRoute + ' -> ' + document.location.href);
lastRoute = document.location.href;
registerRouteChange(null, !mobilePageview);
}
}, pollFrequency || 500);
}
if (firstPageview) {
eventSender.send({
action: 'pageview',
location: document.location.href
});
}
if (styleSheetAdded) {
highlight();
}
};
/**
* Show all instrumented elements
*/
var listInstrumented = exports.listInstrumented = function listInstrumented() {
return capturesToAdd;
};
/***/ }),
/* 3 */
/***/ (function(module, exports) {
// reserved words in javascript: debug, debugger, console
(function(window, document) {
"use strict";
// If defined, chill.
if (window.Mobify && window.Mobify.Debug) {
return
}
// FIXME: Make this smarter. Persist whether enabled. Read from cookies.
// Just enough logic to make this work!
var isEnabled = false;
if (/MOBIFY_DEBUG=1/.test(window.location)) {
isEnabled = true
}
var Mobify = window.Mobify = window.Mobify || {};
var Debug = Mobify.Debug = function(namespace) {
return new Debugger(namespace)
};
function Debugger(namespace) {
this.namespace = namespace
}
Debugger.prototype.log = function(callback) {
if (!isEnabled) {
return
}
if (typeof callback === "function") {
callback()
} else {
var args = [].slice.call(arguments);
//push namespace text at the beginning arguments
args.unshift(this.namespace + ": ");
console.log.apply(console, args);
}
};
/**
* is debugger enabled?
* @returns {boolean} - true if enabled
*/
Debugger.prototype.isEnabled = function(){
return isEnabled;
};
if (isEnabled) {
console.log('Mobify Debug Mode is enabled!')
}
})(window, document);
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
* domready (c) Dustin Diaz 2014 - License MIT
*/
!function (name, definition) {
if (typeof exports !== 'undefined' && this.exports !== exports) module.exports = definition(true)
else if (true) !(__WEBPACK_AMD_DEFINE_FACTORY__ = (definition), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.call(exports, __webpack_require__, exports, module)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))
else this[name] = definition()
}('domready', function () {
if (!document.addEventListener) {
var ret = function() {};
ret.nodom = true;
return ret;
}
var fns = [], listener
, doc = document
, hack = doc.documentElement.doScroll
, domContentLoaded = 'DOMContentLoaded'
, loaded = (hack ? /^loaded|^c/ : /^loaded|^i|^c/).test(doc.readyState)
if (!loaded)
doc.addEventListener(domContentLoaded, listener = function () {
doc.removeEventListener(domContentLoaded, listener)
loaded = 1
while (listener = fns.shift()) listener()
})
return function (fn) {
loaded ? setTimeout(fn, 0) : fns.push(fn)
}
})
/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.clearDimensions = exports.getDimensions = exports.setDimension = exports.send = exports.callIntegration = exports.addIntegration = exports.Evt = undefined;
var _utils = __webpack_require__(6);
var utils = _interopRequireWildcard(_utils);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /**
* Send events to related downstream system
*
* @module analyticsSDK/events
*/
var integrations = {};
var dimensions = {};
/**
* Event class which represent event getting sent to downstream integration
* @class
*/
var Evt =
/**
* @param {Object} evt - an object representing the event to be sent
*/
exports.Evt = function Evt(evt) {
_classCallCheck(this, Evt);
this.evt = evt;
};
/**
* Add an integration downstream
*
* @param {String} name - Name of the integration
* @param {Object} integration - An object of the integration
*/
var addIntegration = exports.addIntegration = function addIntegration(name, integration) {
integrations[name] = integration;
};
/**
* Proxy call to integration
*
* @param {String} name - Name of integration
* @param {String} method - Method to call
* @param {Object} optional parameters to pass on
* @return Return from integrations
*/
var callIntegration = exports.callIntegration = function callIntegration(name, method) {
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
return integrations[name][method](args);
};
/**
* Send events to downstream integrations
*
* @param {Evt} - send event to all integrations
*/
var send = exports.send = function send(evt) {
for (var name in integrations) {
if (integrations.hasOwnProperty(name)) {
integrations[name].sendEvent(evt, dimensions);
}
}
};
/**
* Send events to downstream integrations
*
* @param {String} dimension
* @param {String} value
*/
var setDimension = exports.setDimension = function setDimension(dimName, value) {
dimensions[dimName] = value;
for (var name in integrations) {
if (integrations.hasOwnProperty(name)) {
integrations[name].setDimension(dimName, value);
}
}
};
/**
* Send events to downstream integrations
*
* @return Return current dimensions
*/
var getDimensions = exports.getDimensions = function getDimensions() {
return dimensions;
};
/**
* Clear dimensions
*/
var clearDimensions = exports.clearDimensions = function clearDimensions() {
// http://stackoverflow.com/questions/684575/how-to-quickly-clear-a-javascript-object
utils.inEach(dimensions, function (_, k) {
delete dimensions[k];
});
};
/***/ }),
/* 6 */
/***/ (function(module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* Internal helper functions
*
* @module utils
*/
var getElementsFromSelector = exports.getElementsFromSelector = function getElementsFromSelector(selector) {
try {
return document.querySelectorAll(selector);
} catch (e) {
// catch the illegal element exception
return null;
}
};
/**
* Check if value is null or undefined
* http://stackoverflow.com/questions/2559318/how-to-check-for-an-undefined-or-null-variable-in-javascript
*
* @private
*/
var isNullUndef = exports.isNullUndef = function isNullUndef(val) {
return typeof val === 'undefined' || val === null;
};
/**
* Polyfill for element matches
*
* Adapted from version at
* https://developer.mozilla.org/en/docs/Web/API/Element/matches
* @private
*/
var elementMatches = exports.elementMatches = function elementMatches(e, selector) {
if (e.matches) {
// this is the most common way to handle this
return e.matches(selector);
}
var matchAlias = ['matchesSelector', 'mozMatchesSelector', 'msMatchesSelector', 'oMatchesSelector', 'webkitMatchesSelector'];
for (var i = 0; i < matchAlias.length; i++) {
if (matchAlias[i] in e) {
return e[matchAlias[i]](selector);
}
}
// final solution
var matches = document.querySelectorAll(selector);
for (var _i = 0; _i < matches.length; _i++) {
if (matches.item(_i) === e) {
return true;
}
}
return false;
};
/**
* Similar to _.forEach
*
* A util function to avoid polyfill for Symbols
* Since `for (x of array)` syntax will require `Symbol` polyfill
*
* @private
*/
var forEach = exports.forEach = function forEach(array, callback) {
for (var i = 0; i < array.length; i++) {
callback(array[i], i, array);
}
};
/**
* Similar to _.forEach
*
* A util function to avoid polyfill for Symbols
* Since `for (x in array) ...hasOwnProperty` syntax
*
* @private
*/
var inEach = exports.inEach = function inEach(obj, callback) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
callback(obj[key], key, obj);
}
}
};
/**
* Similar to _.filter
* This wraps around so we can use this on generic list,
* instead of just Array (e.g. return from queryAllSelectors)
*
* @private
*/
var filter = exports.filter = function filter(array, callback) {
return Array.prototype.filter.call(array, callback);
};
/**
* Similar to _.extend
*
* @private
*/
var extend = exports.extend = function extend(base) {
for (var _len = arguments.length, objs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
objs[_key - 1] = arguments[_key];
}
forEach(objs, function (o) {
inEach(o, function (v, k) {
base[k] = v;
});
});
};
/**
* Similar to _.size
*
* @private
*/
var size = exports.size = function size(obj) {
var size = 0;
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
size++;
}
}
return size;
};
/**
* Function for handling cross browser implementation for addEventListener
* On IE9 this require different implementation
*
* Reference: http://stackoverflow.com/questions/12783339/how-to-add-a-click-handler-without-using-jquery
*
* @private
*/
var addListener = exports.addListener = function addListener(elem, type, _fn) {
if (elem.addEventListener) {
elem.addEventListener(type, _fn, false);
} else if (elem.attachEvent) {
_fn = function fn() {
return _fn.call(elem, window.event);
};
elem.attachEvent('on' + type, _fn);
}
return _fn;
};
/**
* Function for handling cross browser implementation for removeEventListener
* On IE9 this require different implementation
*
* Reference: http://stackoverflow.com/questions/12783339/how-to-add-a-click-handler-without-using-jquery
*
* @private
*/
var removeListener = exports.removeListener = function removeListener(elem, type, fn) {
if (elem.removeEventListener) {
elem.removeEventListener(type, fn, false);
} else if (elem.detachEvent) {
elem.detachEvent('on' + type, fn);
}
};
/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.parseDimensionConfig = exports.parseCheckerDimensionConfig = undefined;
__webpack_require__(3);
var _utils = __webpack_require__(6);
var utils = _interopRequireWildcard(_utils);
var _configUtils = __webpack_require__(8);
var configUtils = _interopRequireWildcard(_configUtils);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
var logger = window.Mobify.Debug('analytic-sdk');
/**
* Given config of a particular checkerDimension, carry out the parsing
*
* @param eventType {String}
* @param eventConfig {String}
*
* @return a list of Capture to be considered
*/
var parseCheckerDimensionConfig = exports.parseCheckerDimensionConfig = function parseCheckerDimensionConfig(dimConfig, dimName, callback) {
var capturesToAdd = [];
utils.inEach(dimConfig, function (checkerConfig, dimValue) {
var selectorParser = new configUtils.SelectorsParser(checkerConfig, function (config) {
return config;
});
var selectorProcessor = function selectorProcessor(sel) {
var cmdSelector = null;
try {
cmdSelector = JSON.parse(sel.selector);
if (!cmdSelector) {
throw 'No selector given';
}
} catch (e) {
logger.log('Invalid config extractor: ' + sel.selector);
return null;
}
var command = cmdSelector[0];
var selector = cmdSelector.slice(1);
if (command in configUtils.c.checker) {
var captureCallback = function captureCallback(self, elems, param) {
var command = param.command,
args = param.args,
dimName = param.dimName,
dimValue = param.dimValue;
if (command === configUtils.c.checker.hasElement || command === configUtils.c.checker.itemCountBetween && args[0] <= elems.length <= args[1] || command === configUtils.c.checker.itemCountEqual && args[0] === elems.length || command === configUtils.c.checker.itemCountGreaterThan && args[0] < elems.length || command === configUtils.c.checker.itemCountLessThan && args[0] > elems.length) {
callback(dimName, dimValue);
}
return dimValue;
};
var newCapture = new configUtils.Capture(new configUtils.Selector(selector[0], sel.lifeCycle, sel), {
command: command,
args: selector.slice(1),
dimName: dimName,
dimValue: dimValue
}, captureCallback);
capturesToAdd.push(newCapture);
} else if (command in configUtils.c.extractor) {
var _newCapture = configUtils.getCaptureForExtractor(sel, function (value) {
if (!utils.isNullUndef(value)) {
callback(dimName, dimValue);
}
return dimValue;
});
if (_newCapture) {
capturesToAdd.push(_newCapture);
}
} else {
logger.log('Unkown checker type: ' + command);
}
return null;
};
selectorParser.forEachSelector(selectorProcessor);
return null;
});
return capturesToAdd;
};
var parseDimensionConfig = exports.parseDimensionConfig = function parseDimensionConfig(dimConfig, dimName, callback, targetElem) {
var capturesToAdd = [];
var selectorParser = new configUtils.SelectorsParser(dimConfig, function (config) {
return config;
});
selectorParser.forEachSelector(function (sel) {
var newCapture = configUtils.getCaptureForExtractor(sel, function (value) {
if (!utils.isNullUndef(value)) {
callback(dimName, value);
}
return value;
}, targetElem);
if (newCapture) {
capturesToAdd.push(newCapture);
}
});
return capturesToAdd;
};
/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getCaptureForExtractor = exports.extractValueFromElement = exports.SelectorsParser = exports.Selector = exports.Capture = exports.isProgressive = exports.lifeCycle = exports.c = undefined;
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
__webpack_require__(3);
var _utils = __webpack_require__(6);
var utils = _interopRequireWildcard(_utils);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var logger = window.Mobify.Debug('analytic-sdk');
var c = exports.c = {
extractor: {
/**
* Extract a item from an array of text elements
* Useful for breadcrumb parsing
*
* textValueInArray('breadcrump', 2)
* @const
*/
textValueInArray: 'textValueInArray',
/**
* Extract integer value
*
* intValue('div.cartcount', 0) // default as 0
* @const
*/
intValue: 'intValue',
/**
* Extract integer value and add addition value to it
*
* intValue('div.cartcount', 0, 1) // default as 0
* @const
*/
intAddValue: 'intAddValue',
/**
* Extract text value
*
* textValue('input.search', 'default')
* @const
*/
textValue: 'textValue',
/**
* Extract mapping
*
* textValueMap('.stock-count', {
* '/in stock/': 1,
* '/out of stock/': 1
* })
* @const
*/
textValueMap: 'textValueMap',
/**
* Extract from URL regex group
*
* urlRegex('/product/([^/]*)')
* @const
*/
urlRegex: 'urlRegex',
/**
* Extract from title regex group
*
* title('product:(.*)')
* @const
*/
titleRegex: 'titleRegex'
},
checker: {
hasElement: 'hasElement',
itemCountBetween: 'itemCountBetween',
itemCountEqual: 'itemCountEqual',
itemCountGreaterThan: 'itemCountGreaterThan',
itemCountLessThan: 'itemCountLessThan'
},
elementTarget: {
getAttr: 'getAttr'
}
};
var lifeCycle = exports.lifeCycle = {
ASAP: 'ASAP',
DCL: 'DCL',
untilSeen: 'untilSeen',
monitor: 'monitor'
/**
* Tell if we are on PWA
*/
};var isProgressive = exports.isProgressive = function isProgressive() {
return window.Progressive &&
// Ensure we are not in non-PWA mode
!window.Progressive.PWADisabled;
};
/**
* A class representing individual capture to be ran
*/
var Capture = exports.Capture = function () {
/**
* @param {Selector} selector - selector object returned by parser
* @param {Object} param - specify the parameter used in callback
* @param {Function(self, elements, param)} callback - callback function when element is found
* @param {boolean} doDefault - callback function even if element is not found
*/
function Capture(sel, param, callback, doDefault) {
_classCallCheck(this, Capture);
this.selector = sel.selector;
this.lifeCycle = sel.lifeCycle;
this.param = param;
this.callback = callback;
if (!callback) {
throw 'No callback provided';
}
this.elemValue = null;
this.doDefault = doDefault;
this.eventHooks = [];
}
/**
* @return - return false if element is not found (in default case, if callback is called)
*/
Capture.prototype.instrument = function instrument() {
var elems = utils.getElementsFromSelector(this.selector);
if (!(elems && elems.length)) {
// logger.log(`Cannot find element ${this.selector}`)
if (this.doDefault) {
this.callback(this, null, this.param);
return true;
}
return false;
}
// note that we need to explicitly pass in this parameter due to babel
// convert this param in callback into undefined
// http://stackoverflow.com/questions/34419450/self-invoking-argument-changing-this-to-undefined-in-babel-loader
var instrumentedElements = this.allElements();
var newElements = utils.filter(elems, function (e) {
return instrumentedElements.indexOf(e) === -1;
});
this.elemValue = this.callback(this, newElements, this.param);
return true;
};
Capture.prototype.hasValue = function hasValue() {
return !utils.isNullUndef(this.elemValue);
};
Capture.prototype.allElements = function allElements() {
return this.eventHooks.map(function (hook) {
return hook.elem;
});
};
Capture.prototype.applyToElements = function applyToElements(callback) {
utils.forEach(this.eventHooks, function (h) {
return callback(h.elem);
});
};
Capture.prototype.uninstrument = function uninstrument() {
utils.forEach(this.eventHooks, function (h) {
return utils.removeListener(h.elem, h.action, h.listener);
});
this.eventHooks = [];
};
/**
* An internal function for setting the listener for element
* Must be called if the callback set any listener (see events.js)
*/
Capture.prototype.setListener = function setListener(elem, action, eventListener) {
var listener = utils.addListener(elem, action, eventListener);
this.eventHooks.push({ elem: elem, action: action, listener: listener });
};
return Capture;
}();
/**
* A class providing a list of selectors
*/
var Selector = exports.Selector = function Selector(selector, lc, config) {
_classCallCheck(this, Selector);
this.selector = selector;
this.lifeCycle = lc || lifeCycle.DCL;
if (config) {
this.action = config.action;
this.dimensions = config.dimensions;
}
};
/**
* Represent a class structure that is related to the selector
*/
var SelectorsParser = exports.SelectorsParser = function () {
/**
* Given a top level config structure, collapse callback on
* the given url
*
{
default {
},
components : {
PWA : {...}
}
}
*
* @param {Object} config
* @param {function} callback(config) -> list of selectors
* @return list of selectors
*/
function SelectorsParser(config, getSelector) {
_classCallCheck(this, SelectorsParser);
this.selectors = [];
this.config = config;
this.selectedConfig = null;
this.parseConfig(config, getSelector);
}
SelectorsParser.prototype.parseConfig = function parseConfig(config, getSelector) {
var _this = this;
if (!(config && config.default)) {
logger.log('Error in parsing selectors ' + config);
return null;
}
var combinedConfig = this.selectedConfig = this.getSelectorByComponent(config);
if (combinedConfig.disable) {
return null;
}
var selectors = getSelector(combinedConfig);
if (!selectors) {
logger.log('No selector found ' + selectors);
return null;
}
var urlSelectorMatcher = function urlSelectorMatcher(urlSelector) {
var urlToMatch = document.location.pathname;
if (typeof urlSelector === 'string') {
// multiple selectors
// { default: ['selector1', 'selector2']}
_this.selectors.push(new Selector(urlSelector, combinedConfig.lifeCycle, combinedConfig));
} else {
var urlRegex = urlSelector.url;
if (urlRegex && !urlToMatch.match(urlRegex)) {
return null;
}
if (typeof urlSelector.s === 'string') {
// multiple selectors
// { default: [{u: 'url'}, {s: 'selector2'}]})
_this.selectors.push(new Selector(urlSelector.s, urlSelector.l || combinedConfig.lifeCycle, combinedConfig));
} else {
// multiple selectors
// { default: [{u: 'url'}, {s: ['selector1', 'selector2']}]})
utils.forEach(urlSelector.s, function (s) {
_this.selectors.push(new Selector(s, urlSelector.l || combinedConfig.lifeCycle, combinedConfig));
});
}
}
return null;
};
if (typeof selectors === 'string') {
// basic string selector
// { default: 'selector'}
this.selectors = [new Selector(selectors, combinedConfig.lifeCycle, combinedConfig)];
return null;
} else if (Array.isArray(selectors)) {
// given an array of selector
// [{s:...},{s:....}]
utils.forEach(selectors, urlSelectorMatcher);
} else {
// given a single complex selector
// {s:...}
urlSelectorMatcher(selectors);
}
return this.selectors;
};
/**
* This function consume a block of the config file,
* and return the config you want to consider, dependent on components
* you are running
*
* @return config block to consider
*/
SelectorsParser.prototype.getSelectorByComponent = function getSelectorByComponent(config) {
if (!config.components) {
return config.default;
}
var combinedConfig = null;
var chosenConfig = null;
if (isProgressive() && config.components.PWA) {
chosenConfig = config.components.PWA;
} else if (window.Adaptive && config.components.Adaptive) {
chosenConfig = config.components.Adaptive;
} else {
return config.default;
}
if (typeof config.default === 'string') {
combinedConfig = chosenConfig || config.default;
} else {
// If the subsequent config is not disable, override the base disable
if (!chosenConfig.disable) {
chosenConfig.disable = false;
}
combinedConfig = {};
utils.extend(combinedConfig, config.default, chosenConfig);
}
return combinedConfig;
};
SelectorsParser.prototype.forEachSelector = function forEachSelector(callback) {
if (!this.selectors) {
logger.log('No selector found!');
return;
}
utils.forEach(this.selectors, function (s) {
return callback(s);
});
return;
};
SelectorsParser.prototype.getDimensions = function getDimensions() {
return this.selectedConfig.dimensions;
};
return SelectorsParser;
}();
var getRegexMatch = function getRegexMatch(value, regex, defaultValue) {
if (!value) {
return null;
}
var matchedGroups = value.match(regex);
if (matchedGroups) {
return matchedGroups[1] || defaultValue;
}
return null;
};
/**
* Given an extract command, get value from element
* e.g.
* textValue('div.selector')
*
* @return value from element
*/
var extractValueFromElement = exports.extractValueFromElement = function extractValueFromElement(command, elems, args) {
if (command === c.extractor.textValue || command === c.extractor.intValue || command === c.extractor.intAddValue || command === c.extractor.textValueMap) {
var elemValue = '';
if (!elems) {
// no elements - return default
if (command === c.extractor.intAddValue) {
return args[0] + args[1];
} else if (command === c.extractor.textValue || command === c.extractor.textValueMap) {
return args[1];
}
return args[0];
}
if (elems[0].tagName === 'INPUT') {
elemValue = elems[0].value.trim();
} else {
elemValue = elems[0].textContent.trim();
}
if (command === c.extractor.intValue || command === c.extractor.intAddValue) {
elemValue = parseInt(elemValue);
if (isNaN(elemValue)) {
// default return
elemValue = args[0];
}
if (command === c.extractor.intAddValue) {
elemValue += args[1];
}
} else if (command === c.extractor.textValue) {
var regexExtract = args[0];
var defaultValue = args[1];
if (regexExtract) {
elemValue = getRegexMatch(elemValue, regexExtract, defaultValue);
}
} else if (command === c.extractor.textValueMap) {
var regexExtractMap = args[0];
var _defaultValue = args[1];
if (regexExtractMap) {
var valueToMatch = elemValue;
elemValue = _defaultValue;
utils.inEach(regexExtractMap, function (value, regexExtract) {
if (getRegexMatch(valueToMatch, regexExtract, true)) {
elemValue = value;
}
});
} else {
logger.log('Missing valueMap for textValueMap extractor');
elemValue = null;
}
}
return elemValue;
} else if (command === c.extractor.textValueInArray) {
if (!elems) {
// no elements - return default
return args[1];
}
var elem = args[0] >= 0 ? elems[args[0]] : elems[elems.length + args[0]];
if (elem) {
return elem.textContent.trim();
}
} else {
logger.log('Invalid config unknown extractor type: ' + command);
}
return null;
};
/**
* Extract a value given a extractor
* e.g.
*
* ASDK.extractor.intValue('.mini-cart count')
*
* @return new capture
*/
var getCaptureForExtractor = exports.getCaptureForExtractor = function getCaptureForExtractor(sel, setDimCallback, targetElem) {
var cmdSelector = null;
var newCapture = null;
var valueExtractor = sel.selector;
try {
cmdSelector = JSON.parse(valueExtractor);
if (!cmdSelector) {
throw 'No selector given';
}
} catch (e) {
logger.log('Invalid config extractor: ' + valueExtractor);
return null;
}
var command = cmdSelector[0];
var selector = cmdSelector.slice(1);
if (command === c.extractor.urlRegex || command === c.extractor.titleRegex) {
newCapture = new Capture(new Selector('head', lifeCycle.ASAP), {
regex: selector[0],
command: command
}, function (self, es, params) {
var regex = params.regex,
command = params.command;
var text = command === c.extractor.urlRegex ? document.location.pathname : document.title;
var regexResult = getRegexMatch(text, regex, true);
if (regexResult) {
// we set the dimension immediately - no need for captureGroup
setDimCallback(regexResult);
}
return regexResult;
}, true);
} else if (command === c.elementTarget.getAttr) {
// this set of commands parse against element target
if (!targetElem) {
throw 'getAttr must only be used in dimension';
}
var _selector = _slicedToArray(selector, 2),
attrName = _selector[0],
filterSelectors = _selector[1];
if (filterSelectors) {
var foundMatch = false;
utils.forEach(filterSelectors, function (s) {
if (utils.elementMatches(targetElem, s)) {
foundMatch = true;
}
});
if (!foundMatch) {
return null;
}
}
newCapture = new Capture(new Selector('head', lifeCycle.ASAP), {
value: targetElem[attrName]
}, function (self, es, params) {
var value = params.value;
if (value) {
setDimCallback(value);
}
return value;
}, true);
} else {
var captureCallback = function captureCallback(self, elems, param) {
var command = param.command,
args = param.args;
var value = extractValueFromElement(command, elems, args);
setDimCallback(value);
return value;
};
newCapture = new Capture(new Selector(selector[0], sel.lifeCycle), {
command: command,
args: selector.slice(1)
}, captureCallback, true);
}
return newCapture;
};
/***/ }),
/* 9 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.parseEventConfig = undefined;
__webpack_require__(3);
var _utils = __webpack_require__(6);
var utils = _interopRequireWildcard(_utils);
var _events = __webpack_require__(5);
var eventSender = _interopRequireWildcard(_events);
var _dimensions = __webpack_require__(7);
var dimensionsModule = _interopRequireWildcard(_dimensions);
var _configUtils = __webpack_require__(8);
var configUtils = _interopRequireWildcard(_configUtils);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
var logger = window.Mobify.Debug('analytic-sdk');
/**
* Given config of a particular eventType, carry out the parsing
*
* addToCart: {
* default: {....}
* }
*
* @param eventType {String}
* @param eventConfig {String}
*
* @return a list of Capture to be considered
*/
var parseEventConfig = exports.parseEventConfig = function parseEventConfig(eventConfig, eventType) {
// const eventLifeCycle = eventConfig.default.instrumentLifeCycle
var selectorParser = new configUtils.SelectorsParser(eventConfig, function (config) {
return config.selectors;
});
var getEventListener = function getEventListener() {
return function (evt) {
var eventToSend = {
action: eventType
};
try {
if (eventConfig.default.dimensions) {
var componentDimension = selectorParser.getDimensions();
utils.inEach(componentDimension, function (dimConfig, dimName) {
var captures = dimensionsModule.parseDimensionConfig({ default: dimConfig }, dimName, function (d, v) {
eventToSend[d] = v;
}, evt.target);
// acquire the captures related to the dimension and execute
if (captures && !utils.isNullUndef(captures[0])) {
captures[0].instrument();
}
return true;
});
}
eventSender.send(eventToSend);
} catch (e) {
if (console) {
console.error('Error in handling event ' + eventToSend + ': ' + e);
}
}
};
};
var capturesToAdd = [];
selectorParser.forEachSelector(function (eventSelector) {
var capture = new configUtils.Capture(eventSelector, {
eventType: eventType,
eventAction: eventSelector.action
}, function (self, elems, param) {
logger.log('Instrumenting element ' + param.eventType + '.' + param.eventAction + ' with ' + elems.length + ' elements');
utils.forEach(elems, function (elem) {
self.setListener(elem, param.eventAction, getEventListener());
});
return true;
});
capturesToAdd.push(capture);
});
return capturesToAdd;
};
/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.test = exports.assertEventSentOnce = exports.assertDimensionNotNull = exports.assertDimension = exports.assertEventSent = exports.inputSelect = exports.fireEvent = exports.click = exports.setTest = exports.isTest = undefined;
var _promisePolyfill = __webpack_require__(11);
var _promisePolyfill2 = _interopRequireDefault(_promisePolyfill);
var _utils = __webpack_require__(6);
var utils = _interopRequireWildcard(_utils);
var _events = __webpack_require__(5);
var events = _interopRequireWildcard(_events);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var logger = window.Mobify.Debug('analytic-sdk'); /**
* Provide testing interface for AnalyticsSDK
*
* @module analyticsSDK/t
*/
var eventsSent = [];
var result = {};
var logResult = function logResult(msg) {
window.console && console.log(msg);
};
var captureEvents = function captureEvents(evt) {
logger.log('[TEST] Recieved event: ' + evt.actionName);
eventsSent.push(evt);
};
var isTest = exports.isTest = false;
/**
* Set the test mode
*/
var setTest = exports.setTest = function setTest() {
exports.isTest = isTest = true;
};
/**
* Click a specific element
*
* @params {String} selector - selector to click
* @return {DOMElement}
*/
var click = exports.click = function click(selector) {
var elem = utils.getElementsFromSelector(selector);
logger.log('[TEST] Clicking on element ' + selector);
utils.forEach(elem, function (e) {
e.click();
});
return elem;
};
/**
* Fire an event for element
* http://stackoverflow.com/questions/2856513/how-can-i-trigger-an-onchange-event-manually
*/
var fireEvent = exports.fireEvent = function fireEvent(element, evtName) {
if ('createEvent' in document) {
var evt = document.createEvent('HTMLEvents');
evt.initEvent(evtName, false, true);
element.dispatchEvent(evt);
} else {
element.fireEvent('on' + evtName);
}
};
/**
* Interact with and input on specific elemeent
*/
var inputSelect = exports.inputSelect = function inputSelect(selector, value) {
var elem = utils.getElementsFromSelector(selector);
logger.log('[TEST] Selecting on element ' + selector);
utils.forEach(elem, function (e) {
fireEvent(e, 'focus');
if (value) {
e.value = value;
}
fireEvent(e, 'blur');
});
return elem;
};
/**
* Assert that we only receive multiple events
*
* @params {String} actionName - Name of the action
* @params {Object} dimensions - Object of the dimensions
* @params {Integer} expectCount - How many events are we expecting
* @params {Integer} timeout - How long we would measure the double send
*/
var assertEventSent = exports.assertEventSent = function assertEventSent(actionName, dimensions, expectCount, timeout) {
timeout = timeout || 1000;
return new _promisePolyfill2.default(function (resolve) {
setTimeout(function () {
var count = eventsSent.length;
logger.log('[TEST] Seen ' + count + ' ' + actionName + ' events');
if (count !== expectCount) {
logResult('\u274C Seen ' + count + ' ' + actionName + ' events when expecting ' + expectCount);
result.failed += 1;
} else {
logResult('\u2705 Seen ' + count + ' ' + actionName + ' events sent');
result.passed += 1;
}
resolve();
}, timeout);
});
};
/**
* Assert a dimension is set correctly
*
* @params {String} dimensionName - Name of the dimension
* @params {String} expectedValue - Expected value of the dimension
*/
var assertDimension = exports.assertDimension = function assertDimension(dimensionName, expectedValue) {
var dimensions = events.getDimensions();
var valueSeen = dimensions[dimensionName];
return new _promisePolyfill2.default(function (resolve) {
if (valueSeen === expectedValue) {
logResult('\u2705 Dimension ' + dimensionName + ' is correct - ' + valueSeen);
result.passed += 1;
} else {
logResult('\u274C Seen ' + dimensionName + ' as ' + valueSeen + ' when expecting ' + expectedValue);
result.failed += 1;
}
resolve();
});
};
/**
* Assert a dimension is not null or undefined
*
* @params {String} dimensionName - Name of the dimension
*/
var assertDimensionNotNull = exports.assertDimensionNotNull = function assertDimensionNotNull(dimensionName) {
var dimensions = events.getDimensions();
var valueSeen = dimensions[dimensionName];
return new _promisePolyfill2.default(function (resolve) {
if (typeof valueSeen === 'string' || typeof valueSeen === 'number') {
logResult('\u2705 Dimension ' + dimensionName + ' is defined - ' + valueSeen);
result.passed += 1;
} else {
logResult('\u274C Dimension ' + dimensionName + ' is not defined');
result.failed += 1;
}
resolve();
});
};
/**
* Assert that we only receive only one event so far from the test
*
* @params {String} actionName - Name of the action
* @params {Object} dimensions - Object of the dimensions
* @params {Integer} timeout - How long we would measure the double send
*/
var assertEventSentOnce = exports.assertEventSentOnce = function assertEventSentOnce(actionName, dimensions, timeout) {
return assertEventSent(actionName, dimensions, 1, timeout);
};
/**
* Run the test with analytics
*
* @params {Array} testSpecs - array of test objects
* @params {Function} resultCollector - callback which will recieve results on finish - take a done function
* @params {string} url - specify url to test
* @params {string} testToRun - specify eventName to test
*/
var test = exports.test = function test(testSpecs, resultCollector, url, testToRun) {
eventsSent.length = 0;
result = {
testRan: 0,
passed: 0,
failed: 0
};
events.callIntegration('debugger', 'setCallback', captureEvents);
var chainPromise = new _promisePolyfill2.default(function (r) {
return r();
});
utils.forEach(testSpecs, function (spec) {
var urlToMatch = spec.url;
var testName = spec.testName;
var testFunction = spec.test;
if (testToRun && testToRun !== testName || url && url !== urlToMatch) {
logResult('[TEST] Skipping test ' + testName + ' @ ' + urlToMatch + '...');
return;
}
logResult('[TEST] Running test for ' + testName + '...');
result.testRan += 1;
chainPromise = chainPromise.then(function () {
return new _promisePolyfill2.default(function (resolve) {
testFunction(resolve);
});
});
});
chainPromise.then(function () {
if (resultCollector) {
resultCollector(result);
}
});
};
/***/ }),
/* 11 */
/***/ (function(module, exports) {
(function (root) {
// Store setTimeout reference so promise-polyfill will be unaffected by
// other code modifying setTimeout (like sinon.useFakeTimers())
var setTimeoutFunc = setTimeout;
function noop() {}
// Polyfill for Function.prototype.bind
function bind(fn, thisArg) {
return function () {
fn.apply(thisArg, arguments);
};
}
function Promise(fn) {
if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new');
if (typeof fn !== 'function') throw new TypeError('not a function');
this._state = 0;
this._handled = false;
this._value = undefined;
this._deferreds = [];
doResolve(fn, this);
}
function handle(self, deferred) {
while (self._state === 3) {
self = self._value;
}
if (self._state === 0) {
self._deferreds.push(deferred);
return;
}
self._handled = true;
Promise._immediateFn(function () {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
(self._state === 1 ? resolve : reject)(deferred.promise, self._value);
return;
}
var ret;
try {
ret = cb(self._value);
} catch (e) {
reject(deferred.promise, e);
return;
}
resolve(deferred.promise, ret);
});
}
function resolve(self, newValue) {
try {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.');
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (newValue instanceof Promise) {
self._state = 3;
self._value = newValue;
finale(self);
return;
} else if (typeof then === 'function') {
doResolve(bind(then, newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
} catch (e) {
reject(self, e);
}
}
function reject(self, newValue) {
self._state = 2;
self._value = newValue;
finale(self);
}
function finale(self) {
if (self._state === 2 && self._deferreds.length === 0) {
Promise._immediateFn(function() {
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
});
}
for (var i = 0, len = self._deferreds.length; i < len; i++) {
handle(self, self._deferreds[i]);
}
self._deferreds = null;
}
function Handler(onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
}
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
function doResolve(fn, self) {
var done = false;
try {
fn(function (value) {
if (done) return;
done = true;
resolve(self, value);
}, function (reason) {
if (done) return;
done = true;
reject(self, reason);
});
} catch (ex) {
if (done) return;
done = true;
reject(self, ex);
}
}
Promise.prototype['catch'] = function (onRejected) {
return this.then(null, onRejected);
};
Promise.prototype.then = function (onFulfilled, onRejected) {
var prom = new (this.constructor)(noop);
handle(this, new Handler(onFulfilled, onRejected, prom));
return prom;
};
Promise.all = function (arr) {
var args = Array.prototype.slice.call(arr);
return new Promise(function (resolve, reject) {
if (args.length === 0) return resolve([]);
var remaining = args.length;
function res(i, val) {
try {
if (val && (typeof val === 'object' || typeof val === 'function')) {
var then = val.then;
if (typeof then === 'function') {
then.call(val, function (val) {
res(i, val);
}, reject);
return;
}
}
args[i] = val;
if (--remaining === 0) {
resolve(args);
}
} catch (ex) {
reject(ex);
}
}
for (var i = 0; i < args.length; i++) {
res(i, args[i]);
}
});
};
Promise.resolve = function (value) {
if (value && typeof value === 'object' && value.constructor === Promise) {
return value;
}
return new Promise(function (resolve) {
resolve(value);
});
};
Promise.reject = function (value) {
return new Promise(function (resolve, reject) {
reject(value);
});
};
Promise.race = function (values) {
return new Promise(function (resolve, reject) {
for (var i = 0, len = values.length; i < len; i++) {
values[i].then(resolve, reject);
}
});
};
// Use polyfill for setImmediate for performance gains
Promise._immediateFn = (typeof setImmediate === 'function' && function (fn) { setImmediate(fn); }) ||
function (fn) {
setTimeoutFunc(fn, 0);
};
Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
if (typeof console !== 'undefined' && console) {
console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
}
};
/**
* Set the immediate function to execute callbacks
* @param fn {function} Function to execute
* @deprecated
*/
Promise._setImmediateFn = function _setImmediateFn(fn) {
Promise._immediateFn = fn;
};
/**
* Change the function to execute on unhandled rejection
* @param {function} fn Function to execute on unhandled rejection
* @deprecated
*/
Promise._setUnhandledRejectionFn = function _setUnhandledRejectionFn(fn) {
Promise._unhandledRejectionFn = fn;
};
if (typeof module !== 'undefined' && module.exports) {
module.exports = Promise;
} else if (!root.Promise) {
root.Promise = Promise;
}
})(this);
/***/ }),
/* 12 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _utils = __webpack_require__(6);
var utils = _interopRequireWildcard(_utils);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Sandy integration - responsible for sending events to sandy
*
* @module analyticsSDK/integrations/sandy
*/
var logger = window.Mobify.Debug('analytic-sdk');
var SandyIntegration = function () {
function SandyIntegration() {
_classCallCheck(this, SandyIntegration);
}
/**
* Function to send event to sandy
*
* @param {Event} evt - Event to be taken by sandy
* @return {Boolean} - if event is successfully sent
*/
SandyIntegration.prototype.sendEvent = function sendEvent(evt) {
var _this = this;
if (!window.sandy) {
logger.log('WARN: no sandy initialized');
return false;
}
var tracker = window.sandy;
var action = null;
var dimensions = {};
utils.inEach(evt, function (v, k) {
if (k === 'action') {
action = v;
} else {
dimensions[k] = v;
}
});
if (!action) {
logger.log('WARN: no action specified for ' + JSON.stringify(evt));
return false;
}
utils.inEach(dimensions, function (v, k) {
return _this.setDimension(k, v);
});
// sink event to timing table (currently our all in one sink for misc events)
tracker('send', 'event', 'timing', action);
return true;
};
/**
* Set dimension for downstream events
*
* @param {String} dimension
* @param {String} value
* @return {Boolean} - if event is successfully sent
*/
SandyIntegration.prototype.setDimension = function setDimension(dimension, value) {
if (!window.sandy) {
return;
}
window.sandy('set', dimension, value);
};
return SandyIntegration;
}();
exports.default = SandyIntegration;
/***/ }),
/* 13 */
/***/ (function(module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Note we dont use mobify-debug-js here
* Since this is already triggered using `setDebug()` call, this eliminate the
* need to manually add `MOBIFY_DEBUG=1` to the url
*
* @private
*/
var log = function log() {
if (window.console) {
var _window$console;
(_window$console = window.console).log.apply(_window$console, arguments);
}
};
/**
* Debugger integration - provide debugging experience for SDK
*
* @module analyticsSDK/integrations/debugger
*/
var DebuggerIntegration = function () {
function DebuggerIntegration(options) {
_classCallCheck(this, DebuggerIntegration);
this.callback = (options || {}).callback;
this.dimensions = {};
}
/**
* Function to log events sent to Sandy
*
* @param {Event} evt - Event to be taken by sandy
* @return {Boolean} - if event is successfully sent
*/
DebuggerIntegration.prototype.sendEvent = function sendEvent(evt, dimensions) {
log("[EVENTS] \uD83D\uDCA1 Sending event: " + evt.action + " (" + JSON.stringify(evt) + ")\uD83D\uDCA1");
if (this.callback) {
this.callback(evt, dimensions);
}
return true;
};
/**
* Set dimension for downstream events
*
* @param {String} dimension
* @param {String} value
* @return {Boolean} - if event is successfully sent
*/
DebuggerIntegration.prototype.setDimension = function setDimension(dimension, value) {
log("[DIMENSION] \uD83D\uDCA1 Setting dimension: " + JSON.stringify(dimension) + " \uD83D\uDCA1");
this.dimensions[dimension] = value;
return true;
};
/**
* Setting callback to be called on sendEvent
* @param {Function(event)} callback - function to be called
*/
DebuggerIntegration.prototype.setCallback = function setCallback(callbacks) {
// TODO: allow multiple callbacks
var callback = callbacks[0];
log("Setting callback: " + callback);
this.callback = callback;
};
return DebuggerIntegration;
}();
exports.default = DebuggerIntegration;
/***/ })
/******/ ])
});
;