| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- (function(angular) {
- // Just in case...
- if (!Array.prototype.indexOf) {
- Array.prototype.indexOf = function (searchElement, fromIndex) {
- if ( this === undefined || this === null ) {
- throw new TypeError( '"this" is null or not defined' );
- }
- var length = this.length >>> 0; // Hack to convert object.length to a UInt32
- fromIndex = +fromIndex || 0;
- if (Math.abs(fromIndex) === Infinity) {
- fromIndex = 0;
- }
- if (fromIndex < 0) {
- fromIndex += length;
- if (fromIndex < 0) {
- fromIndex = 0;
- }
- }
- for (;fromIndex < length; fromIndex++) {
- if (this[fromIndex] === searchElement) {
- return fromIndex;
- }
- }
- return -1;
- };
- }
- angular.module('mvdSixpack', ['ngCookies'])
- .provider('sixpack', function() {
- var $body
- , _tests = []
- , _choices = {}
- , _opts = {
- baseUrl: '',
- debug: false,
- }
- , sp = window.sixpack;
- this.setOptions = function (options) {
- angular.extend(_opts, options || {});
- }
- this.$get = ['$cookies','$timeout', '$log', function($cookies, $timeout, $log) {
- var _cookiePrefix = 'sixpack-'
- , _session
- , _clientId;
- var _getOrInitSession = function () {
- if (!_session) {
- if (_clientId = $cookies[_cookiePrefix + 'clientId']) {
- _session = new sp.Session({client_id:_clientId, base_url:_opts.baseUrl});
- } else {
- _session = new sp.Session({client_id:_opts.client_id, base_url:_opts.baseUrl});
- $cookies[_cookiePrefix + 'clientId'] = _clientId = _session.client_id;
- }
- if (_opts.debug) {
- $log.debug('[sixpack] Initialized session with clientId', _clientId, 'and base url', _opts.baseUrl);
- };
- };
- return _session;
- }
- var methods = {
- participate : function (testName, variations, callback) {
- if (_tests.indexOf(testName) < 0) {
- _tests.push(testName);
- } else if (angular.isDefined(_choices[testName])) {
- var res = _choices[testName];
- if (res === false) {
- // Still loading
- $timeout(function () {
- methods.participate(testName, variations, callback);
- }, 50);
- return;
- };
- if (_opts.debug) {
- $log.info('[sixpack] Using already chosen variation for test', testName, res);
- };
- $timeout(function () {
- callback(res.alternative.name, res);
- });
- return;
- }
- _choices[testName] = false;
- var session = _getOrInitSession();
- if (_opts.debug) {
- $log.info('[sixpack] Getting choice for', testName, 'out of', variations);
- };
- session.participate(testName, variations, function (err, res) {
- if (err) {
- if (_opts.debug) {
- $log.warn('[sixpack] Received error', err);
- };
- $timeout(function () {
- callback(false);
- });
- delete _choices[testName];
- return;
- };
- _choices[testName] = res;
- var choice = res.alternative.name;
- if (_opts.debug) {
- $log.info('[sixpack] Alternative chosen:', choice);
- $log.debug('[sixpack] Full response', res);
- };
- if (!$body) {
- $body = angular.element(document).find('body');
- };
- $body.addClass('sixpack-'+testName+' sixpack-'+testName+'-'+choice);
- $timeout(function() {
- callback(choice, res);
- });
- });
- },
- // Register a 'conversion'. If no testName, will call for all active tests
- // Takes an optional callback that receives the raw response from sixpack (or undefined on error)
- convert : function (testName, kpi, callback) {
- if (typeof kpi === 'function') {
- callback = kpi;
- kpi = null;
- }
- var session = _getOrInitSession();
- if (!testName) {
- if (_opts.debug) {
- $log.info("[sixpack] Recording conversion for all tests", _tests);
- };
- for (var i = 0, ii = _tests.length; i < ii; i++) {
- var test = _tests[i]
- , results = [];
- session.convert(test, kpi, function (err, res) {
- results.push(res);
- if (err && _opts.debug) {
- $log.warn("[sixpack] Error recording conversion for", test, err);
- };
- if (results.length == ii) {
- if (_opts.debug) {
- $log.debug('[sixpack] All results:', results);
- };
- if (callback) {
- $timeout(function () {
- callback(results);
- });
- }
- };
- });
- }
- } else {
- if (_opts.debug) {
- $log.info("[sixpack] Recording conversion for", testName);
- };
- session.convert(testName, kpi, function (err, res) {
- if (err && _opts.debug) {
- $log.warn('[sixpack] Error recording conversion:', err);
- } else if (_opts.debug) {
- $log.debug('[sixpack] Conversion result:', res);
- };
- if (callback) {
- $timeout(function () {
- callback(res);
- });
- }
- });
- }
- }
- }
- return methods;
- }];
- })
- .directive('sixpackSwitch', ['sixpack', function(sixpack) {
- return {
- controller : ['$element', function($element) {
- var ctrl = this
- , _testName
- // Map of variation names to transclude fns
- , _variations = {};
- var _processChoice = function (choice) {
- // Triggered if for some reason we get an error from sixpack,
- // or optionally if a user is excluded from this test via configuration
- if (!choice) {
- _setContent(_variations['default']);
- } else {
- _setContent(_variations[choice]);
- }
- }
- var _setContent = function (fn) {
- if (!fn) {
- return;
- };
- fn(function(clone) {
- $element.replaceWith(clone);
- });
- }
- // Pseudo-shim for '.keys' method
- // Additionally, if obj has a 'default' property, sets that as the first element
- // so sixpack will use it as the control
- var _keys = function (obj) {
- var keys = []
- , prop;
- for (prop in obj) {
- if (!obj.hasOwnProperty(prop)) {
- continue;
- };
- if (prop == 'default') {
- keys.unshift(prop);
- } else {
- keys.push(prop);
- }
- }
- return keys;
- }
- ctrl.registerSwitch = function (name) {
- _testName = name;
- sixpack.participate(_testName, _keys(_variations), _processChoice);
- }
- ctrl.registerVariation = function (variation, fn) {
- _variations[variation] = fn;
- }
- return ctrl;
- }],
- require: 'sixpackSwitch',
- link : function ($scope, $element, $attrs, ctrl) {
- ctrl.registerSwitch($attrs.sixpackSwitch);
- }
- }
- }])
- // Register a variation for a test
- .directive('sixpackWhen', ['$log', function ($log) {
- return {
- require: '^sixpackSwitch',
- transclude: 'element',
- link: function($scope, $element, $attrs, ctrl, transcludeFn) {
- if ($attrs.sixpackWhen) {
- ctrl.registerVariation($attrs.sixpackWhen, transcludeFn);
- } else {
- $log.debug('[sixpack] When directive initialized without a name, ignoring');
- }
- }
- }
- }])
- // Register the 'default view, registered as the control variation, and
- // always used if sixpack errors out or if user is excluded via configuration
- .directive('sixpackDefault', function () {
- return {
- require: '^sixpackSwitch',
- transclude: 'element',
- link: function($scope, $element, $attrs, ctrl, transcludeFn) {
- ctrl.registerVariation('default', transcludeFn);
- }
- }
- })
- .directive('sixpackConvert', ['sixpack', function (sixpack) {
- return {
- link : function ($scope, $element, $attrs) {
- var test = $attrs.sixpackConvert || undefined
- , eventType = $attrs.on || 'click';
- $element.on(eventType, function () {
- sixpack.convert(test);
- });
- }
- }
- }]);
- })(window.angular, window.sixpack);
|