mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
added six pack to web
This commit is contained in:
parent
5e19d710ba
commit
8f1d09beea
6 changed files with 704 additions and 2 deletions
|
@ -56,7 +56,8 @@ html(itemscope, itemtype='http://schema.org/Product')
|
||||||
script.
|
script.
|
||||||
window.sharelatex = {
|
window.sharelatex = {
|
||||||
siteUrl: '#{settings.siteUrl}',
|
siteUrl: '#{settings.siteUrl}',
|
||||||
jsPath: '#{jsPath}'
|
jsPath: '#{jsPath}',
|
||||||
|
sixpackDomain: '#{settings.sixpackDomain}'
|
||||||
};
|
};
|
||||||
window.systemMessages = !{JSON.stringify(systemMessages).replace(/\//g, '\\/')};
|
window.systemMessages = !{JSON.stringify(systemMessages).replace(/\//g, '\\/')};
|
||||||
window.ab = {}
|
window.ab = {}
|
||||||
|
|
|
@ -13,8 +13,13 @@ define [
|
||||||
"underscore"
|
"underscore"
|
||||||
"ngSanitize"
|
"ngSanitize"
|
||||||
"ipCookie"
|
"ipCookie"
|
||||||
|
"mvdSixpack"
|
||||||
"ErrorCatcher"
|
"ErrorCatcher"
|
||||||
"localStorage"
|
"localStorage"
|
||||||
])
|
]).config (sixpackProvider)->
|
||||||
|
sixpackProvider.setOptions({
|
||||||
|
debug: true
|
||||||
|
baseUrl: window.sharelatex.sixpackDomain
|
||||||
|
})
|
||||||
|
|
||||||
return App
|
return App
|
||||||
|
|
|
@ -9,5 +9,8 @@ define [
|
||||||
"libs/fineuploader"
|
"libs/fineuploader"
|
||||||
"libs/angular-sanitize-1.2.17"
|
"libs/angular-sanitize-1.2.17"
|
||||||
"libs/angular-cookie"
|
"libs/angular-cookie"
|
||||||
|
"libs/angular-cookies"
|
||||||
"libs/passfield"
|
"libs/passfield"
|
||||||
|
"libs/sixpack"
|
||||||
|
"libs/angular-sixpack"
|
||||||
], () ->
|
], () ->
|
||||||
|
|
206
services/web/public/js/libs/angular-cookies.js
vendored
Normal file
206
services/web/public/js/libs/angular-cookies.js
vendored
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
/**
|
||||||
|
* @license AngularJS v1.3.15
|
||||||
|
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||||
|
* License: MIT
|
||||||
|
*/
|
||||||
|
(function(window, angular, undefined) {'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc module
|
||||||
|
* @name ngCookies
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* # ngCookies
|
||||||
|
*
|
||||||
|
* The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <div doc-module-components="ngCookies"></div>
|
||||||
|
*
|
||||||
|
* See {@link ngCookies.$cookies `$cookies`} and
|
||||||
|
* {@link ngCookies.$cookieStore `$cookieStore`} for usage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
angular.module('ngCookies', ['ng']).
|
||||||
|
/**
|
||||||
|
* @ngdoc service
|
||||||
|
* @name $cookies
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Provides read/write access to browser's cookies.
|
||||||
|
*
|
||||||
|
* Only a simple Object is exposed and by adding or removing properties to/from this object, new
|
||||||
|
* cookies are created/deleted at the end of current $eval.
|
||||||
|
* The object's properties can only be strings.
|
||||||
|
*
|
||||||
|
* Requires the {@link ngCookies `ngCookies`} module to be installed.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* angular.module('cookiesExample', ['ngCookies'])
|
||||||
|
* .controller('ExampleController', ['$cookies', function($cookies) {
|
||||||
|
* // Retrieving a cookie
|
||||||
|
* var favoriteCookie = $cookies.myFavorite;
|
||||||
|
* // Setting a cookie
|
||||||
|
* $cookies.myFavorite = 'oatmeal';
|
||||||
|
* }]);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
factory('$cookies', ['$rootScope', '$browser', function($rootScope, $browser) {
|
||||||
|
var cookies = {},
|
||||||
|
lastCookies = {},
|
||||||
|
lastBrowserCookies,
|
||||||
|
runEval = false,
|
||||||
|
copy = angular.copy,
|
||||||
|
isUndefined = angular.isUndefined;
|
||||||
|
|
||||||
|
//creates a poller fn that copies all cookies from the $browser to service & inits the service
|
||||||
|
$browser.addPollFn(function() {
|
||||||
|
var currentCookies = $browser.cookies();
|
||||||
|
if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
|
||||||
|
lastBrowserCookies = currentCookies;
|
||||||
|
copy(currentCookies, lastCookies);
|
||||||
|
copy(currentCookies, cookies);
|
||||||
|
if (runEval) $rootScope.$apply();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
runEval = true;
|
||||||
|
|
||||||
|
//at the end of each eval, push cookies
|
||||||
|
//TODO: this should happen before the "delayed" watches fire, because if some cookies are not
|
||||||
|
// strings or browser refuses to store some cookies, we update the model in the push fn.
|
||||||
|
$rootScope.$watch(push);
|
||||||
|
|
||||||
|
return cookies;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes all the cookies from the service to the browser and verifies if all cookies were
|
||||||
|
* stored.
|
||||||
|
*/
|
||||||
|
function push() {
|
||||||
|
var name,
|
||||||
|
value,
|
||||||
|
browserCookies,
|
||||||
|
updated;
|
||||||
|
|
||||||
|
//delete any cookies deleted in $cookies
|
||||||
|
for (name in lastCookies) {
|
||||||
|
if (isUndefined(cookies[name])) {
|
||||||
|
$browser.cookies(name, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//update all cookies updated in $cookies
|
||||||
|
for (name in cookies) {
|
||||||
|
value = cookies[name];
|
||||||
|
if (!angular.isString(value)) {
|
||||||
|
value = '' + value;
|
||||||
|
cookies[name] = value;
|
||||||
|
}
|
||||||
|
if (value !== lastCookies[name]) {
|
||||||
|
$browser.cookies(name, value);
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//verify what was actually stored
|
||||||
|
if (updated) {
|
||||||
|
updated = false;
|
||||||
|
browserCookies = $browser.cookies();
|
||||||
|
|
||||||
|
for (name in cookies) {
|
||||||
|
if (cookies[name] !== browserCookies[name]) {
|
||||||
|
//delete or reset all cookies that the browser dropped from $cookies
|
||||||
|
if (isUndefined(browserCookies[name])) {
|
||||||
|
delete cookies[name];
|
||||||
|
} else {
|
||||||
|
cookies[name] = browserCookies[name];
|
||||||
|
}
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]).
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc service
|
||||||
|
* @name $cookieStore
|
||||||
|
* @requires $cookies
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Provides a key-value (string-object) storage, that is backed by session cookies.
|
||||||
|
* Objects put or retrieved from this storage are automatically serialized or
|
||||||
|
* deserialized by angular's toJson/fromJson.
|
||||||
|
*
|
||||||
|
* Requires the {@link ngCookies `ngCookies`} module to be installed.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* angular.module('cookieStoreExample', ['ngCookies'])
|
||||||
|
* .controller('ExampleController', ['$cookieStore', function($cookieStore) {
|
||||||
|
* // Put cookie
|
||||||
|
* $cookieStore.put('myFavorite','oatmeal');
|
||||||
|
* // Get cookie
|
||||||
|
* var favoriteCookie = $cookieStore.get('myFavorite');
|
||||||
|
* // Removing a cookie
|
||||||
|
* $cookieStore.remove('myFavorite');
|
||||||
|
* }]);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
factory('$cookieStore', ['$cookies', function($cookies) {
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name $cookieStore#get
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Returns the value of given cookie key
|
||||||
|
*
|
||||||
|
* @param {string} key Id to use for lookup.
|
||||||
|
* @returns {Object} Deserialized cookie value.
|
||||||
|
*/
|
||||||
|
get: function(key) {
|
||||||
|
var value = $cookies[key];
|
||||||
|
return value ? angular.fromJson(value) : value;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name $cookieStore#put
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Sets a value for given cookie key
|
||||||
|
*
|
||||||
|
* @param {string} key Id for the `value`.
|
||||||
|
* @param {Object} value Value to be stored.
|
||||||
|
*/
|
||||||
|
put: function(key, value) {
|
||||||
|
$cookies[key] = angular.toJson(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name $cookieStore#remove
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Remove given cookie
|
||||||
|
*
|
||||||
|
* @param {string} key Id of the key-value pair to delete.
|
||||||
|
*/
|
||||||
|
remove: function(key) {
|
||||||
|
delete $cookies[key];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}]);
|
||||||
|
|
||||||
|
|
||||||
|
})(window, window.angular);
|
274
services/web/public/js/libs/angular-sixpack.js
vendored
Normal file
274
services/web/public/js/libs/angular-sixpack.js
vendored
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
(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:undefined, 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, callback) {
|
||||||
|
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, 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, 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);
|
||||||
|
|
||||||
|
|
||||||
|
|
213
services/web/public/js/libs/sixpack.js
Normal file
213
services/web/public/js/libs/sixpack.js
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
(function () {
|
||||||
|
// Object.assign polyfill from https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill
|
||||||
|
Object.assign||Object.defineProperty(Object,"assign",{enumerable:!1,configurable:!0,writable:!0,value:function(e){"use strict";if(void 0===e||null===e)throw new TypeError("Cannot convert first argument to object");for(var r=Object(e),t=1;t<arguments.length;t++){var n=arguments[t];if(void 0!==n&&null!==n){n=Object(n);for(var o=Object.keys(Object(n)),a=0,c=o.length;c>a;a++){var i=o[a],b=Object.getOwnPropertyDescriptor(n,i);void 0!==b&&b.enumerable&&(r[i]=n[i])}}}return r}});
|
||||||
|
|
||||||
|
var sixpack = {base_url: "http://localhost:5000", ip_address: null, user_agent: null, timeout: 1000};
|
||||||
|
|
||||||
|
// check for node module loader
|
||||||
|
var on_node = false;
|
||||||
|
if (typeof module !== "undefined" && typeof require !== "undefined") {
|
||||||
|
on_node = true;
|
||||||
|
module.exports = sixpack;
|
||||||
|
} else {
|
||||||
|
window["sixpack"] = sixpack;
|
||||||
|
}
|
||||||
|
|
||||||
|
sixpack.generate_client_id = function () {
|
||||||
|
// from http://stackoverflow.com/questions/105034
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
|
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
sixpack.Session = function (options) {
|
||||||
|
Object.assign(this, sixpack, options);
|
||||||
|
console.log("creating new session", options)
|
||||||
|
if (!this.client_id) {
|
||||||
|
this.client_id = this.generate_client_id();
|
||||||
|
}
|
||||||
|
if (!on_node) {
|
||||||
|
this.user_agent = this.user_agent || (window && window.navigator && window.navigator.userAgent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sixpack.Session.prototype = {
|
||||||
|
participate: function(experiment_name, alternatives, traffic_fraction, force, callback) {
|
||||||
|
console.log("processing new participate", this.base_url)
|
||||||
|
if (typeof traffic_fraction === "function") {
|
||||||
|
callback = traffic_fraction;
|
||||||
|
traffic_fraction = null;
|
||||||
|
force = null;
|
||||||
|
}
|
||||||
|
else if (typeof traffic_fraction === "string") {
|
||||||
|
callback = force;
|
||||||
|
force = traffic_fraction;
|
||||||
|
traffic_fraction = null;
|
||||||
|
}
|
||||||
|
if (typeof force === "function") {
|
||||||
|
callback = force;
|
||||||
|
force = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(/^[a-z0-9][a-z0-9\-_ ]*$/).test(experiment_name)) {
|
||||||
|
return callback(new Error("Bad experiment_name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alternatives.length < 2) {
|
||||||
|
return callback(new Error("Must specify at least 2 alternatives"));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < alternatives.length; i += 1) {
|
||||||
|
if (!(/^[a-z0-9][a-z0-9\-_ ]*$/).test(alternatives[i])) {
|
||||||
|
return callback(new Error("Bad alternative name: " + alternatives[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var params = {client_id: this.client_id,
|
||||||
|
experiment: experiment_name,
|
||||||
|
alternatives: alternatives};
|
||||||
|
if (!on_node && force == null) {
|
||||||
|
var regex = new RegExp("[\\?&]sixpack-force-" + experiment_name + "=([^&#]*)");
|
||||||
|
var results = regex.exec(window.location.search);
|
||||||
|
if(results != null) {
|
||||||
|
force = decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (traffic_fraction !== null && !isNaN(traffic_fraction)) {
|
||||||
|
params.traffic_fraction = traffic_fraction;
|
||||||
|
}
|
||||||
|
if (force != null && _in_array(alternatives, force)) {
|
||||||
|
return callback(null, {"status": "ok", "alternative": {"name": force}, "experiment": {"version": 0, "name": experiment_name}, "client_id": this.client_id});
|
||||||
|
}
|
||||||
|
if (this.ip_address) {
|
||||||
|
params.ip_address = this.ip_address;
|
||||||
|
}
|
||||||
|
if (this.user_agent) {
|
||||||
|
params.user_agent = this.user_agent;
|
||||||
|
}
|
||||||
|
console.log(this.base_url, "participate")
|
||||||
|
return _request(this.base_url + "/participate", params, this.timeout, function(err, res) {
|
||||||
|
if (err) {
|
||||||
|
res = {status: "failed",
|
||||||
|
error: err,
|
||||||
|
alternative: {name: alternatives[0]}};
|
||||||
|
}
|
||||||
|
return callback(null, res);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
convert: function(experiment_name, kpi, callback) {
|
||||||
|
if (typeof kpi === 'function') {
|
||||||
|
callback = kpi;
|
||||||
|
kpi = null;
|
||||||
|
}
|
||||||
|
if (!(/^[a-z0-9][a-z0-9\-_ ]*$/).test(experiment_name)) {
|
||||||
|
return callback(new Error("Bad experiment_name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var params = {client_id: this.client_id,
|
||||||
|
experiment: experiment_name};
|
||||||
|
if (this.ip_address) {
|
||||||
|
params.ip_address = this.ip_address;
|
||||||
|
}
|
||||||
|
if (this.user_agent) {
|
||||||
|
params.user_agent = this.user_agent;
|
||||||
|
}
|
||||||
|
if (kpi) {
|
||||||
|
params.kpi = kpi;
|
||||||
|
}
|
||||||
|
return _request(this.base_url + "/convert", params, this.timeout, function(err, res) {
|
||||||
|
console.log(res)
|
||||||
|
if (err) {
|
||||||
|
res = {status: "failed",
|
||||||
|
error: err,};
|
||||||
|
}
|
||||||
|
return callback(null, res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var counter = 0;
|
||||||
|
|
||||||
|
var _request = function(uri, params, timeout, callback) {
|
||||||
|
var timed_out = false;
|
||||||
|
var timeout_handle = setTimeout(function () {
|
||||||
|
timed_out = true;
|
||||||
|
return callback(new Error("request timed out"));
|
||||||
|
}, timeout);
|
||||||
|
|
||||||
|
if (!on_node) {
|
||||||
|
var cb = "callback" + (++counter);
|
||||||
|
params.callback = "sixpack." + cb
|
||||||
|
sixpack[cb] = function (res) {
|
||||||
|
if (!timed_out) {
|
||||||
|
clearTimeout(timeout_handle);
|
||||||
|
return callback(null, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var url = _request_uri(uri, params);
|
||||||
|
console.log(url)
|
||||||
|
if (!on_node) {
|
||||||
|
script = document.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.src = url;
|
||||||
|
script.async = true;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
} else {
|
||||||
|
var http = require('http');
|
||||||
|
var req = http.get(url, function(res) {
|
||||||
|
var body = "";
|
||||||
|
res.on('data', function(chunk) {
|
||||||
|
return body += chunk;
|
||||||
|
});
|
||||||
|
return res.on('end', function() {
|
||||||
|
var data;
|
||||||
|
if (res.statusCode == 500) {
|
||||||
|
data = {status: "failed", response: body};
|
||||||
|
} else {
|
||||||
|
data = JSON.parse(body);
|
||||||
|
}
|
||||||
|
if (!timed_out) {
|
||||||
|
clearTimeout(timeout_handle);
|
||||||
|
return callback(null, data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
req.on('error', function(err) {
|
||||||
|
if (!timed_out) {
|
||||||
|
clearTimeout(timeout_handle);
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var _request_uri = function(endpoint, params) {
|
||||||
|
var query_string = [];
|
||||||
|
var e = encodeURIComponent;
|
||||||
|
for (var key in params) {
|
||||||
|
if (params.hasOwnProperty(key)) {
|
||||||
|
var vals = params[key];
|
||||||
|
if (Object.prototype.toString.call(vals) !== '[object Array]') {
|
||||||
|
vals = [vals];
|
||||||
|
}
|
||||||
|
for (var i = 0; i < vals.length; i += 1) {
|
||||||
|
query_string.push(e(key) + '=' + e(vals[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (query_string.length) {
|
||||||
|
endpoint += '?' + query_string.join('&');
|
||||||
|
}
|
||||||
|
return endpoint;
|
||||||
|
};
|
||||||
|
|
||||||
|
var _in_array = function(a, v) {
|
||||||
|
for(var i = 0; i < a.length; i++) {
|
||||||
|
if(a[i] === v) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
})();
|
Loading…
Reference in a new issue