'use strict';
/**
* The `ExperimentManager` service has two main responsibilities: managing
* the state (the 'slide' that is presented to the user) and the
* {@link Angular.TrialData|TrialData} service.
*
* @class Angular.ExperimentManager
* @memberof Angular
*/
angular.module('core').factory('ExperimentManager', [
'TrialData',
'$q',
'$http',
'$state',
'$log',
'rfc4122',
'$rootScope',
'lodash',
function(TrialData, $q, $http, $state, $log, rfc4122, $rootScope, lodash) {
$log.debug('Instantiating ExperimentManager service.');
// Attach an event listener for $rootScope's $stateChangeStart
// events and set stateChanging to true when these events are emitted
function stateChangeStartListener() {
returnObject.stateChanging = true;
}
$rootScope.$on('$stateChangeStart', stateChangeStartListener);
// Attach an event listener for $rootScope's $stateChangeSuccess
// events and set stateChanging to false when these events are emitted
function stateChangeSuccessListener() {
returnObject.stateChanging = false;
}
$rootScope.$on('$stateChangeSuccess', stateChangeSuccessListener);
var returnObject = {
/**
* Advances the state to the next state as defined in the study
* specification document (see the [README](index.html)).
*
* @function advanceSlide
* @instance
* @memberof Angular.ExperimentManager
* @return {undefined}
*/
advanceSlide: function() {
if (!this.stateChanging) {
TrialData.data.state.currentSlideIndex++;
var currentSlideIndex =
TrialData.data.state.currentSlideIndex;
if (currentSlideIndex === TrialData.data.schema.length) {
$state.go('home', {}, {reload: true});
} else {
$state.go(
TrialData.data.schema[currentSlideIndex].name,
{},
{reload: true}
);
}
}
},
/**
* Resets the experiment session.
*
* In resetting the session, after resetting the {@link
* Angular.TrialData|TrialData} service, `masterReset()` does
* the following:
*
* - Generates a new UUID for the session number
* - Fetches a random experiment schema from the backend
* - Gets the custom metadata (as specified in
* `config/custom.js`
*
* The {@link Angular.TrialData|TrialData} service is then updated
* with these new data.
*
* @function masterReset
* @memberof Angular.ExperimentManager
* @instance
* @return {$q.promise} This promise is resolved when the reset
* is complete and rejected if the reset fails.
*/
masterReset: function() {
var deferred = $q.defer();
// Reset TrialData
TrialData.reset();
// Get a new experiment setup from the backend
$http.get('/api/experiment-schemas/random')
.success(function(data) {
// Assign the media property from the ExperimentSchema
// we received as the media property on the TrialData
TrialData.data.media = data.media;
TrialData.data.schema = data.structure;
// Get custom configuration information from the backend
$http.get('/api/config')
.success(function(data) {
// Specify this terminal from custom config
TrialData.data.metadata =
lodash.merge(
TrialData.data.metadata,
data.metadata
);
// Generate new session identifier and store it
// in TrialData
TrialData.data.metadata.session_number =
rfc4122.v4();
// Get default language and set on TrialData
if (data.hasOwnProperty('defaultLanguage') &&
typeof data.defaultLanguage === 'string' &&
data.defaultLanguage.length > 0) {
TrialData.data.metadata.language =
data.defaultLanguage;
}
if (data.hasOwnProperty('exportedProperties') &&
Array.isArray(data.exportedProperties)) {
TrialData.exportedProperties =
data.exportedProperties;
}
deferred.resolve();
})
.error(function() {
deferred.reject('The configuration could not' +
'be fetched from the server.');
});
})
.error(function() {
deferred.reject('An experiment schema could not be ' +
'fetched from the server');
});
return deferred.promise;
},
/**
* Reflects the current state as reported by Angular's
* `$stateProvider`. When the state is changing (a new 'slide'
* is being presented--typically after a call to {@link
* Angular.ExperimentManager#advanceSlide advanceSlide}, this
* will be set to `true`. When the change completes, this will
* be set to `true`.
*
* @var stateChanging
* @memberof Angular.ExperimentManager
* @instance
* @type {boolean}
*/
stateChanging: false,
stateChangeStartListener: stateChangeStartListener,
stateChangeSuccessListener: stateChangeSuccessListener
};
return returnObject;
}
]);