Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
# Conflicts:
#	README.md
  • Loading branch information
julesgraus committed Feb 10, 2019
2 parents 7533f00 + 39df267 commit ef50184
Show file tree
Hide file tree
Showing 12 changed files with 331 additions and 257 deletions.
33 changes: 0 additions & 33 deletions dist/forest-guide.css

This file was deleted.

7 changes: 0 additions & 7 deletions dist/forest-guide.css.map

This file was deleted.

186 changes: 1 addition & 185 deletions dist/forestguide.js

Large diffs are not rendered by default.

160 changes: 160 additions & 0 deletions src/js/ActionProcessor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
//Models
import GuideModel from './models/GuideModel';
import ActionModel from './models/ActionModel';

//Other controllers
import Activator from './Activator'

/**
* @class ActionProcessor
*
* The action processor receives a second from the tick method,
* and scans the guide for cues that need to be activated or deactivated.
* Then activates or deactivates cues which results in them triggering actions which
* cause side effects in your browser. For example blink the border on an element.
*
* Activation and deactivation takes place by using the Activator class.
*/
export default class ActionProcessor {
constructor() {
this._guide = null;
this._activeCueIds = []; //Holds the ids of cues wherein the seconds from the tick methods falls in.
this._cueIdsToActivate = []; //Holds the ids of cues that needs to be activated
this._cueIdsToDeactivate = []; //Holds the ids of cues that need to be deactivated
this._activator = new Activator(); //Knows how to create actions
}

/**
* Load a guide to play the actions for
*
* @param {GuideModel} guide
*/
loadGuide(guide) {
if(!guide instanceof GuideModel) {
console.error('The actionprocessor only accepts an instance of the Guide class. Something else was given');
return;
}

this._guide = guide;
}

/**
* Deactivate all cues and their actions
*/
deactivate()
{
self = this;
this._cueIdsToActivate = [];
this._cueIdsToDeactivate = [];
this._activeCueIds.forEach(function(cueId) {
self._cueIdsToDeactivate.push(cueId);
});
this._deactivateCues();
this._activeCueIds = [];
this._cueIdsToDeactivate = [];
}

/**
* Check if one of the guides actions should be executed or stopped,
* depending on what the guide says in combination with the given current
* time in seconds.
*
* @param {number} seconds
*/
tick(seconds) {
if(!this._guide) return;
let self = this;

//Put cues that need to be activated in the _cuesToActivate array.
//And the ones that need to be deactivated in the _cuesToDeactivate array
this._guide.cues.forEach(
/**
* @param {CueModel} cue
* @param {number} index
*/
function (cue, index) {
let activeCuesIdIndex = self._activeCueIds.indexOf(index);
let cuesToActivateIdIndex = self._cueIdsToActivate.indexOf(index);
let cuesToDeactivateIdIndex = self._cueIdsToDeactivate.indexOf(index);

if(seconds >= cue.start && seconds <= cue.end ) {
if(activeCuesIdIndex !== -1 || cuesToActivateIdIndex !== -1 || cuesToDeactivateIdIndex !== -1) return; //Only mark to be activated when the cue was not active, to be activated or deactivated
self._cueIdsToActivate.push(index);
} else if((seconds > cue.end || seconds < cue.start)) {
if(activeCuesIdIndex === -1 || cuesToDeactivateIdIndex !== -1) return; //Only mark the cue to be deactivated when it is active or is not marked as
//Cue is active and outside of the start and end time of the cue.
self._cueIdsToDeactivate.push(index);
}
}
);

this._deactivateCues();
this._activateCues();
}

/**
* Activates cues that need to be activated
* @private
*/
_activateCues() {
let self = this;
let cueId;
while(typeof (cueId = this._cueIdsToActivate.pop()) === "number")
{

let cue = self._guide.cues[cueId];
cue.actions.forEach(
/**
* @param {ActionModel} action
*/
function (action) {
self._activateAction(action);
}
);

//Add the cue to the _activeCueIds
self._activeCueIds.push(cueId);
}
}

/**
* Deactivates cues that need to be deactivated
* @private
*/
_deactivateCues() {
let self = this;
let cueId;
while(typeof (cueId = this._cueIdsToDeactivate.pop()) === "number")
{

let cue = self._guide.cues[cueId];
cue.actions.forEach(
/**
* @param {ActionModel} action
*/
function (action) {
self._deactivateAction(action);
}
);

//Remove the cue id from the _activeCueIds
self._activeCueIds.splice(self._activeCueIds.indexOf(cueId), 1);
}
}

/**
* @param {ActionModel} action
* @private
*/
_activateAction(action) {
this._activator.activate(action);
}

/**
* @param {ActionModel} action
* @private
*/
_deactivateAction(action) {
this._activator.deactivate(action);
}
}
59 changes: 59 additions & 0 deletions src/js/Activator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Blink from './actions/Blink'

/**
* Activator. Knows how to activate and deactivate actions
*/
export default class Activator {
/**
* Activates or deactivates an action
*
* @param {string} name
* @param {object} options
* @param {string} method either activate or deactivate.
* @private
*/
_do(name, options, method) {
let action;
switch (name.toLowerCase()) {
case 'blink':
if(!this._verifyAction(Blink, 'blink')) break;
Blink[method](options);
break;
}
}

/**
* @param {ActionModel} action
*/
activate(action) {
this._do(action.name, action.options, 'activate');
}

/**
* @param {ActionModel} action
*/
deactivate(action) {
this._do(action.name, action.options, 'deactivate');
}

/**
* Verifies that an so called action really is one.
* If it is, it returns true. false otherwise.
*
* @param action
* @param {string} name
* @private
*/
_verifyAction(action, name)
{
if( !action.hasOwnProperty('activate') ||
!action.hasOwnProperty('deactivate') ||
typeof action.activate !== 'function' ||
typeof action.deactivate !== 'function'
) {
console.error('ActionFactory: The action called "'+name+'" was not really an action because it did not have both the activate and deactivate methods.');
return false;
}
return action;
}
}
21 changes: 13 additions & 8 deletions src/js/ForestGuide.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import AudioPlayer from './AudioPlayer';
import Config from './models/Config';
import ConfigModel from './models/ConfigModel';
import DataRetriever from './DataRetriever';
import Guide from './models/Guide';
import GuideModel from './models/GuideModel';
import ActionProcessor from './ActionProcessor';

/**
* @class ForestGuide. Guides users through your app.
* ForestGuide. Guides users through your app.
*/
export default class ForestGuide {
/**
Expand All @@ -15,8 +16,9 @@ export default class ForestGuide {
constructor(config) {
//Initialize dependencies
this._dataRetriever = new DataRetriever();
this._config = new Config(config);
this._config = new ConfigModel(config);
this._audioPlayer = new AudioPlayer();
this._actionProcessor = new ActionProcessor();

//initialize scalar variables
this._guideDataAttributeName = 'forest-guide';
Expand Down Expand Up @@ -72,20 +74,21 @@ export default class ForestGuide {
return;
}

let guideModel = Guide.fromJson(json);
let guideModel = GuideModel.fromJson(json);
self._startOrStopGuidance(guideModel, button);
}).catch(function(reason) {
console.error('ForestGuide: Could not retrieve guide "'+guideName+'" because of an error: '+reason);
})
}

/**
* @param {Guide} guide
* @param {GuideModel} guide
* @param button The guide button that was clicked and eventually triggered this method
* @private
*/
_startOrStopGuidance(guide, button) {
let self = this;
self._actionProcessor.loadGuide(guide);
if(this._audioPlayer.isPlaying() === false) {
this._audioPlayer.onLoading(function () {
button.classList.add(self._config.loadingClass);
Expand All @@ -96,20 +99,22 @@ export default class ForestGuide {
}).onPause(function () {
button.classList.remove(self._config.loadingClass);
button.classList.remove(self._config.playingClass);
self._actionProcessor.deactivate();
}).onPlayProgress(function () {

self._actionProcessor.tick(self._audioPlayer.getCurrentTime());
}).onFinish(function () {
button.classList.remove(self._config.loadingClass);
button.classList.remove(self._config.playingClass);
}).onStopped(function () {
button.classList.remove(self._config.loadingClass);
button.classList.remove(self._config.playingClass);
self._actionProcessor.deactivate();
});
this._audioPlayer.load(this._config.rootUrl + guide.soundFile);
this._audioPlayer.play();
} else {
this._audioPlayer.stop();
this._audioPlayer = new AudioPlayer();
// this._audioPlayer.pause(); //Also a possibility to use.
}
}
}
Expand Down
Loading

0 comments on commit ef50184

Please sign in to comment.