<commit_before>var hangedMan =
"       _______
" +
"      |/      |
" +
"      |      (_)
" +
"      |      \\|/
" +
"      |       |
" +
"      |      / \\
" +
"      |
" +
"___|___
";

exports.commands = {
	hm: 'hangman',
	hangman: function (target, room, user) {
		if (!this.canTalk()) return this.sendReply("You are unable to talk in this room.");
		if (!target) target = 'view';
		var cmd = (~target.indexOf(',') ? target.substr(0, target.indexOf(',')) : target).trim();
		var targetSplit = target.split(',');
		for (var u in targetSplit) targetSplit[u] = targetSplit[u].trim();

		switch (cmd) {
			case 'help':
				if (!this.canBroadcast()) return false;
				this.sendReplyBox(
					"Hangman commands:
" +
					"- /hm command - Short alias for /hangman.
" +
					"- /hangman new/create/start, [word], [topic] - Creates a game of hangman.
" +
					"- /hangman view - Shows the current state of the hangman game.
" +
					"- /hangman topic/changetopic, [newtopic] - Changes the topic of the hangman game.
" +
					"- /hangman word - View the current word.
" +
					"- /hangman guess, [letter] - Guess a letter. Alias: /g
" +
					"- /hangman guessword, [word] - Guess the word. Alias: /gw
" +
					"- /hangman enable/disable - Enables/disables hangman in the current room."
				);
				break;
			case 'new':
			case 'start':
			case 'create':
				if (!user.can('broadcast', null, room)) return this.sendReply("/hangman create - Access denied.");
				if (!room.hangmanEnabled) return this.sendReply("Hangman is disabled in this room.");
				if (!targetSplit || !targetSplit[2]) return this.sendReply("Usage: /hangman [create], [word], [topic]");
				var word = toId(targetSplit[1]);
				word = word.replace(/[^a-z]+/g, '');
				if (word.length > 10) return this.sendReply("Words may not be longer than 10 characters.");
				targetSplit.splice(0, 2);
				var topic = targetSplit.join(' ');
				if (topic.length > 30) return this.sendReply("The topic may not be longer than 30 characters.");

				room.hangman = {};
				room.hangman.word = word;
				room.hangman.topic = topic;
				room.hangman.hangmaner = user.userid;
				room.hangman.guessWord = [];
				room.hangman.guessedLetters = [];
				room.hangman.guessedWords = [];
				room.hangman.guesses = 8;

				for (var i = 0; i < word.length; i++) {
					room.hangman.guessWord.push('_');
				}
				room.add(
					"|raw|

" + Tools.escapeHTML(user.name) +
					" started a game of hangman! The word has " + word.length + " letters.
" + room.hangman.guessWord.join(" ") +
					"
Topic: " + Tools.escapeHTML(room.hangman.topic) + "
" +
					"Use /g letter to guess a letter and /gw word to guess a word

"
				);
				room.update();
				break;
			case 'view':
				if (!room.hangman) return this.sendReply("There's no game of hangman in this room.");
				if (!this.canBroadcast()) return;
				this.sendReplyBox(
					"" + room.hangman.guessWord.join(" ") + "
Guesses left: " + room.hangman.guesses +
					"
Topic: " + Tools.escapeHTML(room.hangman.topic) + ""
				);
				break;
			case 'topic':
			case 'changetopic':
				if (!room.hangman) return this.sendReply("There's no game of hangman in this room.");
				if (user.userid !== room.hangman.hangmaner) return this.sendReply("You can't change the topic if you're not running hangman.");
				if (!targetSplit || !targetSplit[1]) return this.sendReply("Usage: /hangman [topic], [newtopic]");
				targetSplit.splice(0, 1);
				var topic = targetSplit.join(' ');
				if (topic.length > 30) return this.sendReply("The topic may not be longer than 30 characters.");

				room.hangman.topic = topic;
				room.add("
The hangman topic has been changed to '" + Tools.escapeHTML(topic) + "'.");
				room.update();
				break;
			case 'word':
			case 'viewword':
				if (!room.hangman) return this.sendReply("There's no game of hangman in this room.");
				if (user.userid !== room.hangman.hangmaner) return this.sendReply("You're not the user who started this game.");
				this.sendReply("Your word is '" + room.hangman.word + "'.");
				break;
			case 'guess':
				if (!room.hangman) return this.sendReply("There's no game of hangman in this room.");
				if (user.userid === room.hangman.hangmaner) return this.sendReply("You can't guess a letter because you're running hangman.");
				if (!targetSplit || !targetSplit[1]) return this.sendReply("Usage: /hangman [guess], [letter]");
				if (targetSplit[1].length > 1) return this.sendReply("Please specify a letter to guess. To guess the word use /hangman [guessword], [word]");
				var letter = targetSplit[1];
				if (~room.hangman.guessedLetters.indexOf(letter)) return this.sendReply("Someone has already guessed that letter.");
				var found = false;
				room.hangman.guessedLetters.push(letter);

				for (var u in room.hangman.word) {
					if (room.hangman.word[u] === letter) {
						room.hangman.guessWord[u] = room.hangman.word[u];
						found = true;
					}
				}

				room.hangman.guesses--;

				if (found) {
					if (!~room.hangman.guessWord.indexOf('_')) {
						room.add("|raw|
Congratulations! " + Tools.escapeHTML(user.name) + " has guessed the word, which was: " + room.hangman.word + "
");
						room.update();
						delete room.hangman;
						return;
					}

					room.add("|raw|
" + Tools.escapeHTML(user.name) + " guessed the letter '" + letter + "'
Word: " + room.hangman.guessWord.join(' ') + "
");
					room.update();
					return;
				}

				if (room.hangman.guesses < 1) {
					room.add(
						"|raw|
" + Tools.escapeHTML(user.name) + " guessed the letter '" + letter + "', but it was not in the word.
" +
						"You have failed to guess the word, so the man has been hanged.
" +
						"The word was '" + room.hangman.word + "'.
" + hangedMan + "
"
					);
					room.update();
					delete room.hangman;
					return;
				}

				room.add("|raw|
" + Tools.escapeHTML(user.name) + " guessed the letter '" + letter + "' but it was not in the word.
Guesses left: " + room.hangman.guesses + "
");
				room.update();
				break;
			case 'guessword':
				if (!room.hangman) return this.sendReply("There's no game of hangman in this room.");
				if (user.userid === room.hangman.hangmaner) return this.sendReply("You can't guess the word because you're running hangman.");
				if (!targetSplit || !targetSplit[1]) return this.sendReply("Usage: /hangman [guessword], [word]");
				if (targetSplit[1].length !== room.hangman.word.length) return this.sendReply("You can't guess a word that doesn't match the length of the hangman word.");
				var word = targetSplit[1];
				if (~room.hangman.guessedWords.indexOf(word.toLowerCase())) return this.sendReply("That word has already been guessed.");
				room.hangman.guessedWords.push(word.toLowerCase());
				room.hangman.guesses--;

				if (word.toLowerCase() === room.hangman.word.toLowerCase()) {
					room.add("|raw|
Congratulations! " + Tools.escapeHTML(user.name) + " guessed the word, which was: " + room.hangman.word + "
");
					room.update();
					delete room.hangman;
					return;
				}

				if (room.hangman.guesses < 1) {
					room.add(
						"|raw|
" + Tools.escapeHTML(user.name) + " guessed the word '" + Tools.escapeHTML(word) +
						"', but it was not the word. You have failed to guess the word, so the man has been hanged.
" + hangedMan
					);
					room.update();
					delete room.hangman;
					return;
				}

				room.add("|raw|
" + Tools.escapeHTML(user.name) + " guessed the word '" + word + "' but it was not in the word.
Guesses left: " + room.hangman.guesses + "
");
				room.update();
				break;
			case 'on':
			case 'enable':
				if (!user.can('roommod', null, room)) return this.sendReply("/hangman on - Access denied.");
				if (room.hangmanEnabled) return this.sendReply("Hangman is already enabled in this room.");
				room.hangmanEnabled = true;
				room.chatRoomData.hangmanEnabled = true;
				Rooms.global.writeChatRoomData();
				this.sendReply("Hangman has been enabled.");
				break;
			case 'off':
			case 'disable':
				if (!user.can('roommod', null, room)) return this.sendReply("/hangman off - Access denied.");
				if (!room.hangmanEnabled) return this.sendReply("Hangman is already disabled in this room.");
				room.hangmanEnabled = false;
				room.chatRoomData.hangmanEnabled = false;
				Rooms.global.writeChatRoomData();
				this.sendReply("Hangman has been disabled.");
				break;
			case 'end':
				if (!user.can('broadcast', null, room)) return this.sendReply("/hangman end - Access denied.");
				if (!room.hangman) return this.sendReply("There is no hangman game in this room.");
				room.add("|raw|Hangman has been ended by " + Tools.escapeHTML(user.name) + ".");
				room.update();
				delete room.hangman;
				break;
		}
	},

	g: function (target, room, user) {
		this.parse('/hangman guess,' + target);
	},

	gw: function (target, room, user) {
		this.parse('/hangman guessword,' + target);
	}
};
<commit_msg>Change hm alias to hang<commit_after>var hangedMan =
"       _______
" +
"      |/      |
" +
"      |      (_)
" +
"      |      \\|/
" +
"      |       |
" +
"      |      / \\
" +
"      |
" +
"___|___
";

exports.commands = {
	hang: 'hangman',
	hangman: function (target, room, user) {
		if (!this.canTalk()) return this.sendReply("You are unable to talk in this room.");
		if (!target) target = 'view';
		var cmd = (~target.indexOf(',') ? target.substr(0, target.indexOf(',')) : target).trim();
		var targetSplit = target.split(',');
		for (var u in targetSplit) targetSplit[u] = targetSplit[u].trim();

		switch (cmd) {
			case 'help':
				if (!this.canBroadcast()) return false;
				this.sendReplyBox(
					"Hangman commands:
" +
					"- /hang command - Short alias for /hangman.
" +
					"- /hangman new/create/start, [word], [topic] - Creates a game of hangman.
" +
					"- /hangman view - Shows the current state of the hangman game.
" +
					"- /hangman topic/changetopic, [newtopic] - Changes the topic of the hangman game.
" +
					"- /hangman word - View the current word.
" +
					"- /hangman guess, [letter] - Guess a letter. Alias: /g
" +
					"- /hangman guessword, [word] - Guess the word. Alias: /gw
" +
					"- /hangman enable/disable - Enables/disables hangman in the current room."
				);
				break;
			case 'new':
			case 'start':
			case 'create':
				if (!user.can('broadcast', null, room)) return this.sendReply("/hangman create - Access denied.");
				if (!room.hangmanEnabled) return this.sendReply("Hangman is disabled in this room.");
				if (!targetSplit || !targetSplit[2]) return this.sendReply("Usage: /hangman [create], [word], [topic]");
				var word = toId(targetSplit[1]);
				word = word.replace(/[^a-z]+/g, '');
				if (word.length > 10) return this.sendReply("Words may not be longer than 10 characters.");
				targetSplit.splice(0, 2);
				var topic = targetSplit.join(' ');
				if (topic.length > 30) return this.sendReply("The topic may not be longer than 30 characters.");

				room.hangman = {};
				room.hangman.word = word;
				room.hangman.topic = topic;
				room.hangman.hangmaner = user.userid;
				room.hangman.guessWord = [];
				room.hangman.guessedLetters = [];
				room.hangman.guessedWords = [];
				room.hangman.guesses = 8;

				for (var i = 0; i < word.length; i++) {
					room.hangman.guessWord.push('_');
				}
				room.add(
					"|raw|

" + Tools.escapeHTML(user.name) +
					" started a game of hangman! The word has " + word.length + " letters.
" + room.hangman.guessWord.join(" ") +
					"
Topic: " + Tools.escapeHTML(room.hangman.topic) + "
" +
					"Use /g letter to guess a letter and /gw word to guess a word

"
				);
				room.update();
				break;
			case 'view':
				if (!room.hangman) return this.sendReply("There's no game of hangman in this room.");
				if (!this.canBroadcast()) return;
				this.sendReplyBox(
					"" + room.hangman.guessWord.join(" ") + "
Guesses left: " + room.hangman.guesses +
					"
Topic: " + Tools.escapeHTML(room.hangman.topic) + ""
				);
				break;
			case 'topic':
			case 'changetopic':
				if (!room.hangman) return this.sendReply("There's no game of hangman in this room.");
				if (user.userid !== room.hangman.hangmaner) return this.sendReply("You can't change the topic if you're not running hangman.");
				if (!targetSplit || !targetSplit[1]) return this.sendReply("Usage: /hangman [topic], [newtopic]");
				targetSplit.splice(0, 1);
				var topic = targetSplit.join(' ');
				if (topic.length > 30) return this.sendReply("The topic may not be longer than 30 characters.");

				room.hangman.topic = topic;
				room.add("
The hangman topic has been changed to '" + Tools.escapeHTML(topic) + "'.");
				room.update();
				break;
			case 'word':
			case 'viewword':
				if (!room.hangman) return this.sendReply("There's no game of hangman in this room.");
				if (user.userid !== room.hangman.hangmaner) return this.sendReply("You're not the user who started this game.");
				this.sendReply("Your word is '" + room.hangman.word + "'.");
				break;
			case 'guess':
				if (!room.hangman) return this.sendReply("There's no game of hangman in this room.");
				if (user.userid === room.hangman.hangmaner) return this.sendReply("You can't guess a letter because you're running hangman.");
				if (!targetSplit || !targetSplit[1]) return this.sendReply("Usage: /hangman [guess], [letter]");
				if (targetSplit[1].length > 1) return this.sendReply("Please specify a letter to guess. To guess the word use /hangman [guessword], [word]");
				var letter = targetSplit[1];
				if (~room.hangman.guessedLetters.indexOf(letter)) return this.sendReply("Someone has already guessed that letter.");
				var found = false;
				room.hangman.guessedLetters.push(letter);

				for (var u in room.hangman.word) {
					if (room.hangman.word[u] === letter) {
						room.hangman.guessWord[u] = room.hangman.word[u];
						found = true;
					}
				}

				room.hangman.guesses--;

				if (found) {
					if (!~room.hangman.guessWord.indexOf('_')) {
						room.add("|raw|
Congratulations! " + Tools.escapeHTML(user.name) + " has guessed the word, which was: " + room.hangman.word + "
");
						room.update();
						delete room.hangman;
						return;
					}

					room.add("|raw|
" + Tools.escapeHTML(user.name) + " guessed the letter '" + letter + "'
Word: " + room.hangman.guessWord.join(' ') + "
");
					room.update();
					return;
				}

				if (room.hangman.guesses < 1) {
					room.add(
						"|raw|
" + Tools.escapeHTML(user.name) + " guessed the letter '" + letter + "', but it was not in the word.
" +
						"You have failed to guess the word, so the man has been hanged.
" +
						"The word was '" + room.hangman.word + "'.
" + hangedMan + "
"
					);
					room.update();
					delete room.hangman;
					return;
				}

				room.add("|raw|
" + Tools.escapeHTML(user.name) + " guessed the letter '" + letter + "' but it was not in the word.
Guesses left: " + room.hangman.guesses + "
");
				room.update();
				break;
			case 'guessword':
				if (!room.hangman) return this.sendReply("There's no game of hangman in this room.");
				if (user.userid === room.hangman.hangmaner) return this.sendReply("You can't guess the word because you're running hangman.");
				if (!targetSplit || !targetSplit[1]) return this.sendReply("Usage: /hangman [guessword], [word]");
				if (targetSplit[1].length !== room.hangman.word.length) return this.sendReply("You can't guess a word that doesn't match the length of the hangman word.");
				var word = targetSplit[1];
				if (~room.hangman.guessedWords.indexOf(word.toLowerCase())) return this.sendReply("That word has already been guessed.");
				room.hangman.guessedWords.push(word.toLowerCase());
				room.hangman.guesses--;

				if (word.toLowerCase() === room.hangman.word.toLowerCase()) {
					room.add("|raw|
Congratulations! " + Tools.escapeHTML(user.name) + " guessed the word, which was: " + room.hangman.word + "
");
					room.update();
					delete room.hangman;
					return;
				}

				if (room.hangman.guesses < 1) {
					room.add(
						"|raw|
" + Tools.escapeHTML(user.name) + " guessed the word '" + Tools.escapeHTML(word) +
						"', but it was not the word. You have failed to guess the word, so the man has been hanged.
" + hangedMan
					);
					room.update();
					delete room.hangman;
					return;
				}

				room.add("|raw|
" + Tools.escapeHTML(user.name) + " guessed the word '" + word + "' but it was not in the word.
Guesses left: " + room.hangman.guesses + "
");
				room.update();
				break;
			case 'on':
			case 'enable':
				if (!user.can('roommod', null, room)) return this.sendReply("/hangman on - Access denied.");
				if (room.hangmanEnabled) return this.sendReply("Hangman is already enabled in this room.");
				room.hangmanEnabled = true;
				room.chatRoomData.hangmanEnabled = true;
				Rooms.global.writeChatRoomData();
				this.sendReply("Hangman has been enabled.");
				break;
			case 'off':
			case 'disable':
				if (!user.can('roommod', null, room)) return this.sendReply("/hangman off - Access denied.");
				if (!room.hangmanEnabled) return this.sendReply("Hangman is already disabled in this room.");
				room.hangmanEnabled = false;
				room.chatRoomData.hangmanEnabled = false;
				Rooms.global.writeChatRoomData();
				this.sendReply("Hangman has been disabled.");
				break;
			case 'end':
				if (!user.can('broadcast', null, room)) return this.sendReply("/hangman end - Access denied.");
				if (!room.hangman) return this.sendReply("There is no hangman game in this room.");
				room.add("|raw|Hangman has been ended by " + Tools.escapeHTML(user.name) + ".");
				room.update();
				delete room.hangman;
				break;
		}
	},

	g: function (target, room, user) {
		this.parse('/hangman guess,' + target);
	},

	gw: function (target, room, user) {
		this.parse('/hangman guessword,' + target);
	}
};
<|endoftext|>


<commit_before><commit_msg>Fixing bundle<commit_after><|endoftext|>


<commit_before>/**
 * @class
 * @constructor
 * @description The superclass for all maps
 * @param frameworkMap The frameworkspecific layer
 * Map object interface class
 * */
Ext.define("viewer.viewercontroller.controller.Map",{
    extend: "Ext.util.Observable",
    events: [],
    layers: new Array(),
  //
    config :{
        id: "id" ,
        viewerController: new Object()
        //maxExtent : new Object()
    },
    constructor: function(config){
        //this.viewerController = config.viewerController;
        this.initConfig(config);
        this.frameworkMap = this.viewerController.mapComponent.viewerObject;

        this.addEvents(viewer.viewercontroller.controller.Event.ON_ALL_LAYERS_LOADING_COMPLETE,viewer.viewercontroller.controller.Event.ON_CHANGE_EXTENT,viewer.viewercontroller.controller.Event.ON_GET_FEATURE_INFO,viewer.viewercontroller.controller.Event.ON_GET_FEATURE_INFO_DATA,viewer.viewercontroller.controller.Event.ON_FINISHED_CHANGE_EXTENT,viewer.viewercontroller.controller.Event.ON_LAYER_VISIBILITY_CHANGED);
        return this;
    },

    registerEvent : function (event,handler,scope){
        this.addListener(event,handler,scope);
    },

    fire : function (event,options){
        this.fireEvent (event,this,options);
    },

    /**
     * @returns the framework map object.
     */
    getFrameworkMap: function(){
        return this.frameworkMap;
    },

    /**
     *Add a Array of layers(services) to the map
     *@param layers a array of layers
     **/
    addLayers : function(layers){
        for (var i=0; i < layers.length; i++){
            this.addLayer(layers[i]);
        }
    },
    /**
     *Returns all the layers added to this maps.
     */
    getLayers : function(){
        if (this.layers==undefined){
            this.layers=new Array();
        }
        return this.layers;
    },

    /**
     *Get the layer by id
     *@param id the id of the layer you want.
     *@return the layer with the given id or null if the layer does not exists.
     */
    getLayer : function (id){
        for (var i=0; i < this.layers.length; i++){
            if (id==this.layers[i].getId()){
                return this.layers[i];
            }
        }
        return null;
    },

    /**
     *Removes a layer by the given id. Throws a exception when layer with id doesn't exists
     *@param layerId the id of the layer that needs to be removed.
     *Must be implemented by subclass
     */
    removeLayerById : function (layerId){
        this.removeLayer(this.getLayer(layerId));
    },
    /**
     *Remove all the layers
     */
    removeAllLayers:function(){
        for (var i=0; i < this.layers().length; i++){
            removeLayer(this.layers[i]);
        }
    },
    /**
     * Returns the index of the layer.
     * @param layer a Layer
     * @return the index of the layer or -1 if the layer is not found.
     */
    getLayerIndex : function(layer){
        for (var i=0; i < this.getLayers().length; i++){
            if (this.getLayers()[i]==layer){
                return i;
            }
        }
        return -1;
    },

    /*****************Overwrite these functions in the subclass and call this function in the overwrite*****************/

    /**
     *Add a layer(service) to the map
     *@param layer the layer that needs to be added.
     *Must be implemented by subclass to add the layer to the frameworkmap
     **/
    addLayer: function(layer){
        /*  if (!(layer instanceof Layer)){
            throw("Map.addLayer: Given layer is not of type Layer");
        }*/
        this.layers.push(layer);
        layer.map = this;
//        this.viewerController.mapComponent.fire(viewer.viewercontroller.controller.Event.ON_LAYER_ADDED,layer);
    },
    /**
     *Removes a specifice layer from the map.
     *Must be implemented by subclas! The subclass needs to do the remove from the framework!
     **/
    removeLayer:function(layer){
        var index=this.getLayerIndex(layer);
        if (index==-1)
            throw("Map.removeLayer(): Layer not available in map!");
        this.layers.splice(index,1);
    },
    /**
     * Set the layer index of the given layer. The subclass needs to implement
     * the setLayerIndex in the framework.
     * @param layer a Layer object.
     * @param newIndex the new index for this layer
     * @return the old index of this layer
     */
    setLayerIndex : function (layer, newIndex){
        if(!(layer instanceof viewer.viewercontroller.controller.Layer)){
            throw("Given layer not of type Layer");
        }
        var currentIndex=this.getLayerIndex(layer);
        var newLayerArray= new Array();
        var oldLayerArray = this.getLayers();
        var size = oldLayerArray.length;
        var count=0;
        // Delete layer from the old array
        oldLayerArray.splice(currentIndex   ,1);
        for(var i = 0 ; i < size; i++){
            if(newIndex == i ){
                newLayerArray.push(layer);
            }else{
                newLayerArray.push(oldLayerArray[count]);
                count++;
            }
        }
        this.layers=newLayerArray;
        return currentIndex;
    },
        /**
     * Sets a layer visible/invisible
     */
    setLayerVisible : function (layer, visible){
        this.fire(viewer.viewercontroller.controller.Event.ON_LAYER_VISIBILITY_CHANGED,{layer:layer, visible:visible});
    },

    /*****************These functions need to be overwritten*****************/
    /**
     *Gets the id of this object
     *Must be implemented by subclass
     */
    getId : function(){
        throw("Map.getId() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Gets all the wms layers in this map
     */
    getAllWMSLayers : function(){
        throw("Map.getAllWMSLayers() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Gets all the vector layers in this map
     */
    getAllVectorLayers : function(){
        throw("Map.getAllVectorLayers() Not implemented! Must be implemented in sub-class");
    },
    /**
     *Remove this map
     *Must be implemented by subclass
     */
    remove : function(){
        throw("Map.remove() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Move the map to the given extent.
     * @param extent a Extent object
     *Must be implemented by subclass
     */
    zoomToExtent : function (extent){
        throw("Map.moveToExtent() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Moves the viewport to the max extent.
     * Must be implemented by subclass
     */
    zoomToMaxExtent : function(){
        throw("Map.zoomToMaxExtent() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Zooms to the given scale
     *Must be implemented by subclass
     */
    zoomToScale : function(scale){
        throw("Map.zoomToScale() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Zooms to the given resolution
     *Must be implemented by subclass
     */
    zoomToResolution : function(resolution){
        throw("Map.zoomToResolution() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Returns the current extent of the viewport as a extent object.
     *Must be implemented by subclass
     */
    getExtent: function(){
        throw("Map.getExtent() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Sets the full extent of the viewport
     * @param extent (see Extent object)
     * Must be implemented by subclass
     */
    setMaxExtent:function(extent){
        throw("Map.setMaxExtent() Not implemented! Must be implemented in sub-class");
    },
    /**
     *returns the full extent as a extent object
     *Must be implemented by subclass
     *
     */
    getMaxExtent:function(){
        throw("Map.getFullExtent() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Do a identify on a specific coord extent.
     *@param x the x coord
     *@param y the y coord
     *Must be implemented by subclass
     */
    doIdentify : function(x,y){
        throw("Map.doIdentify() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Ipdates the map
     *Must be implemented by subclass
     */
    update : function (){
        throw("Map.update() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Sets a marker on the map
     *@param markerName the name of the marker
     *@param x the x coord
     *@param y the y coord
     *@param type the type marker
     *Must be implemented by subclass
     */
    setMarker : function(markerName,x,y,type){
        throw("Map.setMarker() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Removes the marker with the given markerName
     *@param markerName the name of the marker that needs to be removed.
     *Must be implemented by subclass
     */
    removeMarker : function(markerName){
        throw("Map.removeMarker() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Gets the scale of this map
     * @return The current scale of this map
     */
    getScale : function (){
        throw("Map.getScale() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Gets the resolution of this map
     * @return The current resolution of this map
     */
    getResolution : function (){
        throw("Map.getResolution() Not implemented! Must be implemented in sub-class");
    },

    /**
     * calculates the viewport pixel coordinate from the realworld pixel
     * @param x xcoord
     * @param y ycoord
     * @return a object with object.x the x pixel and object.y the y pixel
     */
    coordinateToPixel : function(x,y){
        throw("Map.coordinateToPixel() Not implemented! Must be implemented in sub-class");
    },

    /**
     * gets the center of this viewport in worldcoordinates
     * @return a object with object.x the .y.
     */
    getCenter : function(){
        throw("Map.getCenter() Not implemented! Must be implemented in sub-class");
    }

});
<commit_msg>Map now fires the ON_LAYER_ADDED event<commit_after>/**
 * @class
 * @constructor
 * @description The superclass for all maps
 * @param frameworkMap The frameworkspecific layer
 * Map object interface class
 * */
Ext.define("viewer.viewercontroller.controller.Map",{
    extend: "Ext.util.Observable",
    events: [],
    layers: new Array(),
  //
    config :{
        id: "id" ,
        viewerController: new Object()
        //maxExtent : new Object()
    },
    constructor: function(config){
        //this.viewerController = config.viewerController;
        this.initConfig(config);
        this.frameworkMap = this.viewerController.mapComponent.viewerObject;

        this.addEvents(viewer.viewercontroller.controller.Event.ON_ALL_LAYERS_LOADING_COMPLETE,viewer.viewercontroller.controller.Event.ON_CHANGE_EXTENT,viewer.viewercontroller.controller.Event.ON_GET_FEATURE_INFO,viewer.viewercontroller.controller.Event.ON_GET_FEATURE_INFO_DATA,viewer.viewercontroller.controller.Event.ON_FINISHED_CHANGE_EXTENT,viewer.viewercontroller.controller.Event.ON_LAYER_VISIBILITY_CHANGED);
        return this;
    },

    registerEvent : function (event,handler,scope){
        this.addListener(event,handler,scope);
    },

    fire : function (event,options){
        this.fireEvent (event,this,options);
    },

    /**
     * @returns the framework map object.
     */
    getFrameworkMap: function(){
        return this.frameworkMap;
    },

    /**
     *Add a Array of layers(services) to the map
     *@param layers a array of layers
     **/
    addLayers : function(layers){
        for (var i=0; i < layers.length; i++){
            this.addLayer(layers[i]);
        }
    },
    /**
     *Returns all the layers added to this maps.
     */
    getLayers : function(){
        if (this.layers==undefined){
            this.layers=new Array();
        }
        return this.layers;
    },

    /**
     *Get the layer by id
     *@param id the id of the layer you want.
     *@return the layer with the given id or null if the layer does not exists.
     */
    getLayer : function (id){
        for (var i=0; i < this.layers.length; i++){
            if (id==this.layers[i].getId()){
                return this.layers[i];
            }
        }
        return null;
    },

    /**
     *Removes a layer by the given id. Throws a exception when layer with id doesn't exists
     *@param layerId the id of the layer that needs to be removed.
     *Must be implemented by subclass
     */
    removeLayerById : function (layerId){
        this.removeLayer(this.getLayer(layerId));
    },
    /**
     *Remove all the layers
     */
    removeAllLayers:function(){
        for (var i=0; i < this.layers().length; i++){
            removeLayer(this.layers[i]);
        }
    },
    /**
     * Returns the index of the layer.
     * @param layer a Layer
     * @return the index of the layer or -1 if the layer is not found.
     */
    getLayerIndex : function(layer){
        for (var i=0; i < this.getLayers().length; i++){
            if (this.getLayers()[i]==layer){
                return i;
            }
        }
        return -1;
    },

    /*****************Overwrite these functions in the subclass and call this function in the overwrite*****************/

    /**
     *Add a layer(service) to the map
     *@param layer the layer that needs to be added.
     *Must be implemented by subclass to add the layer to the frameworkmap
     **/
    addLayer: function(layer){
        /*  if (!(layer instanceof Layer)){
            throw("Map.addLayer: Given layer is not of type Layer");
        }*/
        this.layers.push(layer);
        layer.map = this;
        this.viewerController.mapComponent.fire(viewer.viewercontroller.controller.Event.ON_LAYER_ADDED,layer);
    },
    /**
     *Removes a specifice layer from the map.
     *Must be implemented by subclas! The subclass needs to do the remove from the framework!
     **/
    removeLayer:function(layer){
        var index=this.getLayerIndex(layer);
        if (index==-1)
            throw("Map.removeLayer(): Layer not available in map!");
        this.layers.splice(index,1);
    },
    /**
     * Set the layer index of the given layer. The subclass needs to implement
     * the setLayerIndex in the framework.
     * @param layer a Layer object.
     * @param newIndex the new index for this layer
     * @return the old index of this layer
     */
    setLayerIndex : function (layer, newIndex){
        if(!(layer instanceof viewer.viewercontroller.controller.Layer)){
            throw("Given layer not of type Layer");
        }
        var currentIndex=this.getLayerIndex(layer);
        var newLayerArray= new Array();
        var oldLayerArray = this.getLayers();
        var size = oldLayerArray.length;
        var count=0;
        // Delete layer from the old array
        oldLayerArray.splice(currentIndex   ,1);
        for(var i = 0 ; i < size; i++){
            if(newIndex == i ){
                newLayerArray.push(layer);
            }else{
                newLayerArray.push(oldLayerArray[count]);
                count++;
            }
        }
        this.layers=newLayerArray;
        return currentIndex;
    },
        /**
     * Sets a layer visible/invisible
     */
    setLayerVisible : function (layer, visible){
        this.fire(viewer.viewercontroller.controller.Event.ON_LAYER_VISIBILITY_CHANGED,{layer:layer, visible:visible});
    },

    /*****************These functions need to be overwritten*****************/
    /**
     *Gets the id of this object
     *Must be implemented by subclass
     */
    getId : function(){
        throw("Map.getId() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Gets all the wms layers in this map
     */
    getAllWMSLayers : function(){
        throw("Map.getAllWMSLayers() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Gets all the vector layers in this map
     */
    getAllVectorLayers : function(){
        throw("Map.getAllVectorLayers() Not implemented! Must be implemented in sub-class");
    },
    /**
     *Remove this map
     *Must be implemented by subclass
     */
    remove : function(){
        throw("Map.remove() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Move the map to the given extent.
     * @param extent a Extent object
     *Must be implemented by subclass
     */
    zoomToExtent : function (extent){
        throw("Map.moveToExtent() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Moves the viewport to the max extent.
     * Must be implemented by subclass
     */
    zoomToMaxExtent : function(){
        throw("Map.zoomToMaxExtent() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Zooms to the given scale
     *Must be implemented by subclass
     */
    zoomToScale : function(scale){
        throw("Map.zoomToScale() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Zooms to the given resolution
     *Must be implemented by subclass
     */
    zoomToResolution : function(resolution){
        throw("Map.zoomToResolution() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Returns the current extent of the viewport as a extent object.
     *Must be implemented by subclass
     */
    getExtent: function(){
        throw("Map.getExtent() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Sets the full extent of the viewport
     * @param extent (see Extent object)
     * Must be implemented by subclass
     */
    setMaxExtent:function(extent){
        throw("Map.setMaxExtent() Not implemented! Must be implemented in sub-class");
    },
    /**
     *returns the full extent as a extent object
     *Must be implemented by subclass
     *
     */
    getMaxExtent:function(){
        throw("Map.getFullExtent() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Do a identify on a specific coord extent.
     *@param x the x coord
     *@param y the y coord
     *Must be implemented by subclass
     */
    doIdentify : function(x,y){
        throw("Map.doIdentify() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Ipdates the map
     *Must be implemented by subclass
     */
    update : function (){
        throw("Map.update() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Sets a marker on the map
     *@param markerName the name of the marker
     *@param x the x coord
     *@param y the y coord
     *@param type the type marker
     *Must be implemented by subclass
     */
    setMarker : function(markerName,x,y,type){
        throw("Map.setMarker() Not implemented! Must be implemented in sub-class");
    },

    /**
     *Removes the marker with the given markerName
     *@param markerName the name of the marker that needs to be removed.
     *Must be implemented by subclass
     */
    removeMarker : function(markerName){
        throw("Map.removeMarker() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Gets the scale of this map
     * @return The current scale of this map
     */
    getScale : function (){
        throw("Map.getScale() Not implemented! Must be implemented in sub-class");
    },

    /**
     * Gets the resolution of this map
     * @return The current resolution of this map
     */
    getResolution : function (){
        throw("Map.getResolution() Not implemented! Must be implemented in sub-class");
    },

    /**
     * calculates the viewport pixel coordinate from the realworld pixel
     * @param x xcoord
     * @param y ycoord
     * @return a object with object.x the x pixel and object.y the y pixel
     */
    coordinateToPixel : function(x,y){
        throw("Map.coordinateToPixel() Not implemented! Must be implemented in sub-class");
    },

    /**
     * gets the center of this viewport in worldcoordinates
     * @return a object with object.x the .y.
     */
    getCenter : function(){
        throw("Map.getCenter() Not implemented! Must be implemented in sub-class");
    }

});
<|endoftext|>


<commit_before>'use strict'

const Slapp = require('slapp')
const Context = require('slapp-context-beepboop')
const ConvoStore = require('slapp-convo-beepboop')
const Helper = require('./helper.js')
const Inspire = require('./inspire.js')

const SpaceImageGame = require('./game-space-image.js')
const ColorGame = require('./game-color.js')
const DecodeMessageGame = require('./game-decode-message.js')

module.exports = (server, db) => {
  let app = Slapp({
    verify_token: process.env.SLACK_VERIFY_TOKEN,
    context: Context(),
    convo_store: ConvoStore()
  })

  //*********************************************
  // Middleware
  //*********************************************
  app.use((msg, next) => {
    if (msg.body.event.user)
    {
      // Save message sent to Creature-bot
      db.saveConvo(msg.body.event.user, msg.body, (err, convo) => {
        if (err)
        {
          console.log(err)
        }
      })
      // Increase user experience
      db.increaseExperience(msg.body.event.user, (err) => {
        if (err)
        {
          console.log(err)
        }
      })
    }
    next()
  })

  var HELP_TEXT = `
  I will respond to the following messages:
  \`help\` - to see this message
  \`hi\` - to demonstrate a conversation that tracks state.
  \`thanks\` - to demonstrate a simple response.
  \`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
  \`attachment\` - to see a Slack attachment message.
  `

//*********************************************
// Setup different handlers for messages
//*********************************************

  //*********************************************
  // Begin Inspire Handler
  //*********************************************
  app.message('inspire', ['direct_mention', 'direct_message'], (msg, text) => {
    Inspire.getInspireRoute(db, msg)
  })
  //*********************************************
  // End Inspire Handler
  //*********************************************

  //*********************************************
  // Begin Color Name Game
  //*********************************************
  app.message('color', ['direct_mention', 'direct_message'], (msg, text) => {
    ColorGame.run(db, msg)
  })

  app.route('color_response', (msg, text) => {
    msg.say(['Thanks, I have taken note.', 'Sounds good, I am keeping track of these.', 'Thanks, keep up the good work.'])
  })
  //*********************************************
  // End Color Name Game
  //*********************************************

  //*********************************************
  // Begin Space Image Game
  //*********************************************
  app.message('space', ['direct_mention', 'direct_message'], (msg, text) => {
    // Code used to inject space images and facts.
    // db.saveSpaceImages( {
    //   'title': "Hubble Sees a Cosmic Caterpillar",
    //   'text': 'This light-year-long knot of interstellar gas and dust resembles a caterpillar on its way to a feast.',
    //   'image': 'https://media.stsci.edu/uploads/story/display_image/1005/low_keystone.png',
    // },(err) => {
    //     if (err) {
    //       console.error(err)
    //       msg.say(err)
    //     }
    // })
    SpaceImageGame.run(db, msg)
  })

  app.route('space_response', (msg, state) => {
    msg.say(['Thanks, I have taken note.', 'Sounds good, I am keeping track of these.', 'Thanks, keep up the good work.'])
  })
  //*********************************************
  // End Space Image Game
  //*********************************************

  //*********************************************
  // Begin Decode The Message Game
  //*********************************************
  app.message('decode', ['direct_mention', 'direct_message'], (msg, text) => {
    // db.saveDecodeMessage({
    //   'phrase': 'We See What We Want'
    // },(err) => {
    //   if (err) {
    //     console.error(err)
    //     msg.say(err)
    //   }
    // })
    DecodeMessageGame.run(db, msg)
  })

  app.route('decode_response', (msg, state) => {

  })

  app.message('decode_end', (msg, state) => {

  })
  //*********************************************
  // End Decode The Message Game
  //*********************************************

  app
    .message('motivators', ['direct_mention', 'direct_message'], (msg, text) => {

      var motivators

      db.getMotivations(msg.body.event.user, (err, motivations) => {
        if (err) {
          console.error(err)
        }

        motivators = motivations
        if (motivators instanceof Object)
        {
          msg
            .say('You have motivations set.')
            .say(`Do more of this: `+motivators.motivates+'. Do less of this: '+motivators.discourages)
        }
        else
        {
          msg
            .say(`What is something that motivates you to create?`)
            // sends next event from user to this route, passing along state
            .route('motivates')
        }
      })
    })
  app.route('motivates', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear a response.")
          .say('What is something that motivates you to create?')
          .route('motivates', state)
      }

      // add their response to state
      state.motivates = text

      msg
        .say(`Ok. And what's something that discourages you from creating?`)
        .route('discourages', state)
    })
  app.route('discourages', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear something that discourages you from creating.")
          .route('discourages', state)
      }

      // add their response to state
      state.discourages = text

      // Store the responses
      db.saveMotivations(msg.body.event.user, { 'motivates' : state.motivates , 'discourages' : state.discourages }, (err, convo) => {
        console.log(err)
      })

      msg
        .say('Thanks for sharing.')
        .say(`Do more of this: `+state.motivates+'. Do less of this: '+state.discourages)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // response to the user typing "help"
  app.message('help', ['mention', 'direct_message'], (msg) => {
    msg.say(HELP_TEXT)
  })

  // Test response for boxes
  app.message('yesno', (msg) => {
    msg.say({
      text: '',
      attachments: [
        {
          text: '',
          fallback: 'Yes or No?',
          callback_id: 'yesno_callback',
          actions: [
            { name: 'answer', text: 'Yes', type: 'button', value: 'yes' },
            { name: 'answer', text: 'No',  type: 'button',  value: 'no' }
          ]
        }]
      })
  })

  app.action('yesno_callback', 'answer', (msg, value) => {
    msg.respond(msg.body.response_url, `${value} is a good choice!`)
  })

  // "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(`${text}, how are you?`)
        // sends next event from user to this route, passing along state
        .route('how-are-you', { greeting: text })
    })
    .route('how-are-you', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear how you're doing.")
          .say('How are you?')
          .route('how-are-you', state)
      }

      // add their response to state
      state.status = text

      msg
        .say(`Ok then. What's your favorite color?`)
        .route('color', state)
    })
    .route('color', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear your favorite color.")
          .route('color', state)
      }

      // add their response to state
      state.color = text

      msg
        .say('Thanks for sharing.')
        .say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // Can use a regex as well
  app.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
    // You can provide a list of responses, and a random one will be chosen
    // You can also include slack emoji in your responses
    msg.say([
      "You're welcome :smile:",
      'You bet',
      ':+1: Of course',
      'Anytime :sun_with_face: :full_moon_with_face:'
    ])
  })

  // demonstrate returning an attachment...
  app.message('attachment', ['mention', 'direct_message'], (msg) => {
    msg.say({
      text: 'Check out this amazing attachment! :confetti_ball: ',
      attachments: [{
        text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
        title: 'Slapp Library - Open Source',
        image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
        title_link: 'https://beepboophq.com/',
        color: '#7CD197'
      }]
    })
  })

  // response to the user asking "how did you get here?"
  app.message('how did you get here?', ['mention', 'direct_message'], (msg) => {
    msg.say('Team Rocket! :rocket:')
  })

  // "Creative motivators" flow

  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(text + ', how are you?')
        .route('how-are-you', { text }, 60)
    })
    .route('how-are-you', (msg, state) => {
      var resp = msg.body.event && msg.body.event.text

      if (/good/i.test(resp)) {
        msg
          .say(['Great! Ready?', ':smile: Are you sure?'])
          .route('how-are-you', state, 60)
      } else {
        msg
          .say('Me too', () => msg.say(`Thanks for saying ${state.text}`))
      }
    })
    .message('^(thanks|thank you)', ['mention', 'direct_message'], (msg) => {
      msg.say(["You're welcome", 'Of course'])
    })
    .message('help', ['direct_message', 'direct_mention'], (msg) => {
      let help = `I will respond to the following messages:
\`@slapp hi||hello\` for a greeting.
\`mention me by name in a message\` to demonstrate detecting a mention.
\`@slapp help\` to see this again.`

      msg.say(help)
    })
    .message('fix it', 'ambient', (msg) => {
      msg.say('https://www.youtube.com/watch?v=yo3uxqwTxk0')
    })
    // .message(/.*/, 'mention', (msg) => {
    //   msg.say('You really do care about me. :heart:')
    // })

    app.attachToExpress(server)

  return app
}
<commit_msg>Adds more phrases to database.<commit_after>'use strict'

const Slapp = require('slapp')
const Context = require('slapp-context-beepboop')
const ConvoStore = require('slapp-convo-beepboop')
const Helper = require('./helper.js')
const Inspire = require('./inspire.js')

const SpaceImageGame = require('./game-space-image.js')
const ColorGame = require('./game-color.js')
const DecodeMessageGame = require('./game-decode-message.js')

module.exports = (server, db) => {
  let app = Slapp({
    verify_token: process.env.SLACK_VERIFY_TOKEN,
    context: Context(),
    convo_store: ConvoStore()
  })

  //*********************************************
  // Middleware
  //*********************************************
  app.use((msg, next) => {
    if (msg.body.event.user)
    {
      // Save message sent to Creature-bot
      db.saveConvo(msg.body.event.user, msg.body, (err, convo) => {
        if (err)
        {
          console.log(err)
        }
      })
      // Increase user experience
      db.increaseExperience(msg.body.event.user, (err) => {
        if (err)
        {
          console.log(err)
        }
      })
    }
    next()
  })

  var HELP_TEXT = `
  I will respond to the following messages:
  \`help\` - to see this message
  \`hi\` - to demonstrate a conversation that tracks state.
  \`thanks\` - to demonstrate a simple response.
  \`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
  \`attachment\` - to see a Slack attachment message.
  `

//*********************************************
// Setup different handlers for messages
//*********************************************

  //*********************************************
  // Begin Inspire Handler
  //*********************************************
  app.message('inspire', ['direct_mention', 'direct_message'], (msg, text) => {
    Inspire.getInspireRoute(db, msg)
  })
  //*********************************************
  // End Inspire Handler
  //*********************************************

  //*********************************************
  // Begin Color Name Game
  //*********************************************
  app.message('color', ['direct_mention', 'direct_message'], (msg, text) => {
    ColorGame.run(db, msg)
  })

  app.route('color_response', (msg, text) => {
    msg.say(['Thanks, I have taken note.', 'Sounds good, I am keeping track of these.', 'Thanks, keep up the good work.'])
  })
  //*********************************************
  // End Color Name Game
  //*********************************************

  //*********************************************
  // Begin Space Image Game
  //*********************************************
  app.message('space', ['direct_mention', 'direct_message'], (msg, text) => {
    // Code used to inject space images and facts.
    // db.saveSpaceImages( {
    //   'title': "Hubble Sees a Cosmic Caterpillar",
    //   'text': 'This light-year-long knot of interstellar gas and dust resembles a caterpillar on its way to a feast.',
    //   'image': 'https://media.stsci.edu/uploads/story/display_image/1005/low_keystone.png',
    // },(err) => {
    //     if (err) {
    //       console.error(err)
    //       msg.say(err)
    //     }
    // })
    SpaceImageGame.run(db, msg)
  })

  app.route('space_response', (msg, state) => {
    msg.say(['Thanks, I have taken note.', 'Sounds good, I am keeping track of these.', 'Thanks, keep up the good work.'])
  })
  //*********************************************
  // End Space Image Game
  //*********************************************

  //*********************************************
  // Begin Decode The Message Game
  //*********************************************
  app.message('decode', ['direct_mention', 'direct_message'], (msg, text) => {
    db.saveDecodeMessage({
      'phrase': 'Chance Favors the Prepared Mind'
    },(err) => {
      if (err) {
        console.error(err)
        msg.say(err)
      }
    })
    db.saveDecodeMessage({
      'phrase': 'Failing is not always a Failure'
    },(err) => {
      if (err) {
        console.error(err)
        msg.say(err)
      }
    })
    db.saveDecodeMessage({
      'phrase': 'Everyday is a Second Chance'
    },(err) => {
      if (err) {
        console.error(err)
        msg.say(err)
      }
    })
    db.saveDecodeMessage({
      'phrase': 'You Always Have A Choice'
    },(err) => {
      if (err) {
        console.error(err)
        msg.say(err)
      }
    })
    db.saveDecodeMessage({
      'phrase': 'Wake Up And Be Inspired'
    },(err) => {
      if (err) {
        console.error(err)
        msg.say(err)
      }
    })
    DecodeMessageGame.run(db, msg)
  })

  app.route('decode_response', (msg, state) => {

  })

  app.message('decode_end', (msg, state) => {

  })
  //*********************************************
  // End Decode The Message Game
  //*********************************************

  app
    .message('motivators', ['direct_mention', 'direct_message'], (msg, text) => {

      var motivators

      db.getMotivations(msg.body.event.user, (err, motivations) => {
        if (err) {
          console.error(err)
        }

        motivators = motivations
        if (motivators instanceof Object)
        {
          msg
            .say('You have motivations set.')
            .say(`Do more of this: `+motivators.motivates+'. Do less of this: '+motivators.discourages)
        }
        else
        {
          msg
            .say(`What is something that motivates you to create?`)
            // sends next event from user to this route, passing along state
            .route('motivates')
        }
      })
    })
  app.route('motivates', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear a response.")
          .say('What is something that motivates you to create?')
          .route('motivates', state)
      }

      // add their response to state
      state.motivates = text

      msg
        .say(`Ok. And what's something that discourages you from creating?`)
        .route('discourages', state)
    })
  app.route('discourages', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear something that discourages you from creating.")
          .route('discourages', state)
      }

      // add their response to state
      state.discourages = text

      // Store the responses
      db.saveMotivations(msg.body.event.user, { 'motivates' : state.motivates , 'discourages' : state.discourages }, (err, convo) => {
        console.log(err)
      })

      msg
        .say('Thanks for sharing.')
        .say(`Do more of this: `+state.motivates+'. Do less of this: '+state.discourages)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // response to the user typing "help"
  app.message('help', ['mention', 'direct_message'], (msg) => {
    msg.say(HELP_TEXT)
  })

  // Test response for boxes
  app.message('yesno', (msg) => {
    msg.say({
      text: '',
      attachments: [
        {
          text: '',
          fallback: 'Yes or No?',
          callback_id: 'yesno_callback',
          actions: [
            { name: 'answer', text: 'Yes', type: 'button', value: 'yes' },
            { name: 'answer', text: 'No',  type: 'button',  value: 'no' }
          ]
        }]
      })
  })

  app.action('yesno_callback', 'answer', (msg, value) => {
    msg.respond(msg.body.response_url, `${value} is a good choice!`)
  })

  // "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(`${text}, how are you?`)
        // sends next event from user to this route, passing along state
        .route('how-are-you', { greeting: text })
    })
    .route('how-are-you', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear how you're doing.")
          .say('How are you?')
          .route('how-are-you', state)
      }

      // add their response to state
      state.status = text

      msg
        .say(`Ok then. What's your favorite color?`)
        .route('color', state)
    })
    .route('color', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear your favorite color.")
          .route('color', state)
      }

      // add their response to state
      state.color = text

      msg
        .say('Thanks for sharing.')
        .say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // Can use a regex as well
  app.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
    // You can provide a list of responses, and a random one will be chosen
    // You can also include slack emoji in your responses
    msg.say([
      "You're welcome :smile:",
      'You bet',
      ':+1: Of course',
      'Anytime :sun_with_face: :full_moon_with_face:'
    ])
  })

  // demonstrate returning an attachment...
  app.message('attachment', ['mention', 'direct_message'], (msg) => {
    msg.say({
      text: 'Check out this amazing attachment! :confetti_ball: ',
      attachments: [{
        text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
        title: 'Slapp Library - Open Source',
        image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
        title_link: 'https://beepboophq.com/',
        color: '#7CD197'
      }]
    })
  })

  // response to the user asking "how did you get here?"
  app.message('how did you get here?', ['mention', 'direct_message'], (msg) => {
    msg.say('Team Rocket! :rocket:')
  })

  // "Creative motivators" flow

  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(text + ', how are you?')
        .route('how-are-you', { text }, 60)
    })
    .route('how-are-you', (msg, state) => {
      var resp = msg.body.event && msg.body.event.text

      if (/good/i.test(resp)) {
        msg
          .say(['Great! Ready?', ':smile: Are you sure?'])
          .route('how-are-you', state, 60)
      } else {
        msg
          .say('Me too', () => msg.say(`Thanks for saying ${state.text}`))
      }
    })
    .message('^(thanks|thank you)', ['mention', 'direct_message'], (msg) => {
      msg.say(["You're welcome", 'Of course'])
    })
    .message('help', ['direct_message', 'direct_mention'], (msg) => {
      let help = `I will respond to the following messages:
\`@slapp hi||hello\` for a greeting.
\`mention me by name in a message\` to demonstrate detecting a mention.
\`@slapp help\` to see this again.`

      msg.say(help)
    })
    .message('fix it', 'ambient', (msg) => {
      msg.say('https://www.youtube.com/watch?v=yo3uxqwTxk0')
    })
    // .message(/.*/, 'mention', (msg) => {
    //   msg.say('You really do care about me. :heart:')
    // })

    app.attachToExpress(server)

  return app
}
<|endoftext|>


<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

/*eslint-env mocha*/

import { isFSA } from 'flux-standard-action';

import * as actionSources from '../../../../lib/redux/constants/actionSources';
import * as actionTypes from '../../../../lib/redux/constants/actionTypes';
import * as metrics from '../../../../lib/redux/constants/metrics';

import * as syncActions from '../../../../lib/redux/actions/sync';
import { errorText } from '../../../../lib/redux/utils/errors';

describe('Synchronous Actions', () => {
  describe('hideUnavailableDevices', () => {
    it('should create an action to hide devices unavailable on given operating system', () => {
      const OS = 'test';
      const expectedAction = {
        type: actionTypes.HIDE_UNAVAILABLE_DEVICES,
        payload: {os: OS},
        meta: {source: actionSources[actionTypes.HIDE_UNAVAILABLE_DEVICES]}
      };
      expect(syncActions.hideUnavailableDevices(OS)).to.deep.equal(expectedAction);
      expect(isFSA(syncActions.hideUnavailableDevices(OS))).to.be.true;
    });
  });

  describe('setForgotPasswordUrl', () => {
    it('should create an action to set the forgot password url', () => {
      const URL = 'http://www.acme.com/forgot-password';
      const expectedAction = {
        type: actionTypes.SET_FORGOT_PASSWORD_URL,
        payload: {url: URL},
        meta: {source: actionSources[actionTypes.SET_FORGOT_PASSWORD_URL]}
      };
      expect(syncActions.setForgotPasswordUrl(URL)).to.deep.equal(expectedAction);
      expect(isFSA(syncActions.setForgotPasswordUrl(URL))).to.be.true;
    });
  });

  describe('setSignUpUrl', () => {
    it('should create an action to set the sign-up url', () => {
      const URL = 'http://www.acme.com/sign-up';
      const expectedAction = {
        type: actionTypes.SET_SIGNUP_URL,
        payload: {url: URL},
        meta: {source: actionSources[actionTypes.SET_SIGNUP_URL]}
      };
      expect(syncActions.setSignUpUrl(URL)).to.deep.equal(expectedAction);
      expect(isFSA(syncActions.setSignUpUrl(URL))).to.be.true;
    });
  });

  describe('setOs', () => {
    it('should create an action to set the operating system', () => {
      const OS = 'mac';
      const expectedAction = {
        type: actionTypes.SET_OS,
        payload: {os: OS},
        meta: {source: actionSources[actionTypes.SET_OS]}
      };
      expect(syncActions.setOs(OS)).to.deep.equal(expectedAction);
      expect(isFSA(syncActions.setOs(OS))).to.be.true;
    });
  });

  describe('setPage', () => {
    it('should create an action to set the page', () => {
      const PAGE = 'FOO';
      const expectedAction = {
        type: actionTypes.SET_PAGE,
        payload: {page: PAGE},
        meta: {source: actionSources[actionTypes.SET_PAGE]}
      };
      expect(syncActions.setPage(PAGE)).to.deep.equal(expectedAction);
      expect(isFSA(syncActions.setPage(PAGE))).to.be.true;
    });
  });

  describe('setVersion', () => {
    const VERSION = '0.100.0';
    it('should be an FSA', () => {
      let action = syncActions.setVersion(VERSION);

      expect(isFSA(action)).to.be.true;
    });

    it('should create an action to set the uploader version', () => {
      const expectedAction = {
        type: actionTypes.SET_VERSION,
        payload: {version: VERSION},
        meta: {source: actionSources[actionTypes.SET_VERSION]}
      };
      expect(syncActions.setVersion(VERSION)).to.deep.equal(expectedAction);
    });
  });

  describe('toggleDropdown', () => {
    const DROPDOWN_PREVIOUS_STATE = true;
    it('should be an FSA', () => {
      let action = syncActions.toggleDropdown(DROPDOWN_PREVIOUS_STATE);

      expect(isFSA(action)).to.be.true;
    });

    it('should create an action to toggle the dropdown menu', () => {
      const expectedAction = {
        type: actionTypes.TOGGLE_DROPDOWN,
        payload: {isVisible: false},
        meta: {source: actionSources[actionTypes.TOGGLE_DROPDOWN]}
      };
      expect(syncActions.toggleDropdown(DROPDOWN_PREVIOUS_STATE)).to.deep.equal(expectedAction);
    });
  });

  describe('for doAppInit', () => {
    describe('initRequest', () => {
      it('should be an FSA', () => {
        let action = syncActions.initRequest();

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to record the start of app initialization', () => {
        const expectedAction = {
          type: actionTypes.INIT_APP_REQUEST,
          meta: {source: actionSources[actionTypes.INIT_APP_REQUEST]}
        };
        expect(syncActions.initRequest()).to.deep.equal(expectedAction);
      });
    });

    describe('initSuccess', () => {
      it('should be an FSA', () => {
        let action = syncActions.initSuccess();

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to record the successful completion of app initialization', () => {
        const expectedAction = {
          type: actionTypes.INIT_APP_SUCCESS,
          meta: {source: actionSources[actionTypes.INIT_APP_SUCCESS]}
        };
        expect(syncActions.initSuccess()).to.deep.equal(expectedAction);
      });
    });

    describe('initFailure', () => {
      it('should be an FSA', () => {
        let action = syncActions.initFailure();

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to record early exit from app initialization due to error', () => {
        const expectedAction = {
          type: actionTypes.INIT_APP_FAILURE,
          error: true,
          payload: new Error(errorText.E_INIT),
          meta: {source: actionSources[actionTypes.INIT_APP_FAILURE]}
        };
        expect(syncActions.initFailure()).to.deep.equal(expectedAction);
      });
    });

    describe('setUserInfoFromToken', () => {
      // NB: this is not what these objects actually look like
      // actual shape is irrelevant to testing action creators
      const user = {userid: 'abc123'};
      const profile = {fullName: 'Jane Doe'};
      const memberships = [{userid: 'def456'}, {userid: 'ghi789'}];
      it('should be an FSA', () => {
        let action = syncActions.setUserInfoFromToken({ user, profile, memberships });
        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to set the logged-in user (plus user\'s profile, careteam memberships)', () => {
        const expectedAction = {
          type: actionTypes.SET_USER_INFO_FROM_TOKEN,
          payload: { user, profile, memberships },
          meta: {source: actionSources[actionTypes.SET_USER_INFO_FROM_TOKEN]}
        };
        expect(syncActions.setUserInfoFromToken({ user, profile, memberships })).to.deep.equal(expectedAction);
      });
    });
  });

  describe('for doLogin', () => {
    describe('loginRequest', () => {
      it('should be an FSA', () => {
        let action = syncActions.loginRequest();

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to record the start of user login', () => {
        const expectedAction = {
          type: actionTypes.LOGIN_REQUEST,
          meta: {source: actionSources[actionTypes.LOGIN_REQUEST]}
        };
        expect(syncActions.loginRequest()).to.deep.equal(expectedAction);
      });
    });

    describe('loginSuccess', () => {
      // NB: this is not what these objects actually look like
      // actual shape is irrelevant to testing action creators
      const user = {userid: 'abc123'};
      const profile = {fullName: 'Jane Doe'};
      const memberships = [{userid: 'def456'}, {userid: 'ghi789'}];
      it('should be an FSA', () => {
        expect(isFSA(syncActions.loginSuccess({ user, profile, memberships }))).to.be.true;
      });

      it('should create an action to set the logged-in user (plus user\'s profile, careteam memberships)', () => {
        const expectedAction = {
          type: actionTypes.LOGIN_SUCCESS,
          payload: { user, profile, memberships },
          meta: {
            source: actionSources[actionTypes.LOGIN_SUCCESS],
            metric: metrics.LOGIN_SUCCESS
          }
        };
        expect(syncActions.loginSuccess({ user, profile, memberships })).to.deep.equal(expectedAction);
      });
    });

    describe('loginFailure', () => {
      const err = 'Login error!';
      it('should be an FSA', () => {
        let action = syncActions.loginFailure(err);

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to report a login error', () => {
        syncActions.__Rewire__('getLoginErrorMessage', () => err);
        const expectedAction = {
          type: actionTypes.LOGIN_FAILURE,
          error: true,
          payload: new Error(err),
          meta: {source: actionSources[actionTypes.LOGIN_FAILURE]}
        };
        expect(syncActions.loginFailure(err)).to.deep.equal(expectedAction);
        syncActions.__ResetDependency__('getLoginErrorMessage');
      });
    });
  });

  describe('for retrieveTargetsFromStorage', () => {
    describe('setUsersTargets', () => {
      // NB: this is not what this object actually looks like
      // actual shape is irrelevant to testing action creators
      const targets = {
        a1b2c3: ['foo', 'bar'],
        d4e5f6: ['baz']
      };
      it('should be an FSA', () => {
        let action = syncActions.setUsersTargets();

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to set the users\' target devices', () => {
        const expectedAction = {
          type: actionTypes.SET_USERS_TARGETS,
          payload: { targets },
          meta: {source: actionSources[actionTypes.SET_USERS_TARGETS]}
        };
        expect(syncActions.setUsersTargets(targets)).to.deep.equal(expectedAction);
      });
    });
  });
});<commit_msg>a few straggler tests that hadn't been updated to expect isFSA separately<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

/*eslint-env mocha*/

import { isFSA } from 'flux-standard-action';

import * as actionSources from '../../../../lib/redux/constants/actionSources';
import * as actionTypes from '../../../../lib/redux/constants/actionTypes';
import * as metrics from '../../../../lib/redux/constants/metrics';

import * as syncActions from '../../../../lib/redux/actions/sync';
import { errorText } from '../../../../lib/redux/utils/errors';

describe('Synchronous Actions', () => {
  describe('hideUnavailableDevices', () => {
    const OS = 'test';
    it('should be an FSA', () => {
      let action = syncActions.hideUnavailableDevices(OS);

      expect(isFSA(action)).to.be.true;
    });

    it('should create an action to hide devices unavailable on given operating system', () => {
      const expectedAction = {
        type: actionTypes.HIDE_UNAVAILABLE_DEVICES,
        payload: {os: OS},
        meta: {source: actionSources[actionTypes.HIDE_UNAVAILABLE_DEVICES]}
      };
      expect(syncActions.hideUnavailableDevices(OS)).to.deep.equal(expectedAction);
    });
  });

  describe('setForgotPasswordUrl', () => {
    const URL = 'http://www.acme.com/forgot-password';
    it('should be an FSA', () => {
      let action = syncActions.setForgotPasswordUrl(URL);

      expect(isFSA(action)).to.be.true;
    });

    it('should create an action to set the forgot password url', () => {
      const expectedAction = {
        type: actionTypes.SET_FORGOT_PASSWORD_URL,
        payload: {url: URL},
        meta: {source: actionSources[actionTypes.SET_FORGOT_PASSWORD_URL]}
      };
      expect(syncActions.setForgotPasswordUrl(URL)).to.deep.equal(expectedAction);
    });
  });

  describe('setSignUpUrl', () => {
    const URL = 'http://www.acme.com/sign-up';
    it('should be an FSA', () => {
      let action = syncActions.setSignUpUrl(URL);

      expect(isFSA(action)).to.be.true;
    });

    it('should create an action to set the sign-up url', () => {
      const expectedAction = {
        type: actionTypes.SET_SIGNUP_URL,
        payload: {url: URL},
        meta: {source: actionSources[actionTypes.SET_SIGNUP_URL]}
      };
      expect(syncActions.setSignUpUrl(URL)).to.deep.equal(expectedAction);
    });
  });

  describe('setOs', () => {
    const OS = 'mac';
    it('should be an FSA', () => {
      let action = syncActions.setOs(OS);

      expect(isFSA(action)).to.be.true;
    });

    it('should create an action to set the operating system', () => {
      const expectedAction = {
        type: actionTypes.SET_OS,
        payload: {os: OS},
        meta: {source: actionSources[actionTypes.SET_OS]}
      };
      expect(syncActions.setOs(OS)).to.deep.equal(expectedAction);
    });
  });

  describe('setPage', () => {
    const PAGE = 'FOO';
    it('should be an FSA', () => {
      let action = syncActions.setPage(PAGE);

      expect(isFSA(action)).to.be.true;
    });

    it('should create an action to set the page', () => {
      const expectedAction = {
        type: actionTypes.SET_PAGE,
        payload: {page: PAGE},
        meta: {source: actionSources[actionTypes.SET_PAGE]}
      };
      expect(syncActions.setPage(PAGE)).to.deep.equal(expectedAction);
    });
  });

  describe('setVersion', () => {
    const VERSION = '0.100.0';
    it('should be an FSA', () => {
      let action = syncActions.setVersion(VERSION);

      expect(isFSA(action)).to.be.true;
    });

    it('should create an action to set the uploader version', () => {
      const expectedAction = {
        type: actionTypes.SET_VERSION,
        payload: {version: VERSION},
        meta: {source: actionSources[actionTypes.SET_VERSION]}
      };
      expect(syncActions.setVersion(VERSION)).to.deep.equal(expectedAction);
    });
  });

  describe('toggleDropdown', () => {
    const DROPDOWN_PREVIOUS_STATE = true;
    it('should be an FSA', () => {
      let action = syncActions.toggleDropdown(DROPDOWN_PREVIOUS_STATE);

      expect(isFSA(action)).to.be.true;
    });

    it('should create an action to toggle the dropdown menu', () => {
      const expectedAction = {
        type: actionTypes.TOGGLE_DROPDOWN,
        payload: {isVisible: false},
        meta: {source: actionSources[actionTypes.TOGGLE_DROPDOWN]}
      };
      expect(syncActions.toggleDropdown(DROPDOWN_PREVIOUS_STATE)).to.deep.equal(expectedAction);
    });
  });

  describe('for doAppInit', () => {
    describe('initRequest', () => {
      it('should be an FSA', () => {
        let action = syncActions.initRequest();

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to record the start of app initialization', () => {
        const expectedAction = {
          type: actionTypes.INIT_APP_REQUEST,
          meta: {source: actionSources[actionTypes.INIT_APP_REQUEST]}
        };
        expect(syncActions.initRequest()).to.deep.equal(expectedAction);
      });
    });

    describe('initSuccess', () => {
      it('should be an FSA', () => {
        let action = syncActions.initSuccess();

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to record the successful completion of app initialization', () => {
        const expectedAction = {
          type: actionTypes.INIT_APP_SUCCESS,
          meta: {source: actionSources[actionTypes.INIT_APP_SUCCESS]}
        };
        expect(syncActions.initSuccess()).to.deep.equal(expectedAction);
      });
    });

    describe('initFailure', () => {
      it('should be an FSA', () => {
        let action = syncActions.initFailure();

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to record early exit from app initialization due to error', () => {
        const expectedAction = {
          type: actionTypes.INIT_APP_FAILURE,
          error: true,
          payload: new Error(errorText.E_INIT),
          meta: {source: actionSources[actionTypes.INIT_APP_FAILURE]}
        };
        expect(syncActions.initFailure()).to.deep.equal(expectedAction);
      });
    });

    describe('setUserInfoFromToken', () => {
      // NB: this is not what these objects actually look like
      // actual shape is irrelevant to testing action creators
      const user = {userid: 'abc123'};
      const profile = {fullName: 'Jane Doe'};
      const memberships = [{userid: 'def456'}, {userid: 'ghi789'}];
      it('should be an FSA', () => {
        let action = syncActions.setUserInfoFromToken({ user, profile, memberships });
        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to set the logged-in user (plus user\'s profile, careteam memberships)', () => {
        const expectedAction = {
          type: actionTypes.SET_USER_INFO_FROM_TOKEN,
          payload: { user, profile, memberships },
          meta: {source: actionSources[actionTypes.SET_USER_INFO_FROM_TOKEN]}
        };
        expect(syncActions.setUserInfoFromToken({ user, profile, memberships })).to.deep.equal(expectedAction);
      });
    });
  });

  describe('for doLogin', () => {
    describe('loginRequest', () => {
      it('should be an FSA', () => {
        let action = syncActions.loginRequest();

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to record the start of user login', () => {
        const expectedAction = {
          type: actionTypes.LOGIN_REQUEST,
          meta: {source: actionSources[actionTypes.LOGIN_REQUEST]}
        };
        expect(syncActions.loginRequest()).to.deep.equal(expectedAction);
      });
    });

    describe('loginSuccess', () => {
      // NB: this is not what these objects actually look like
      // actual shape is irrelevant to testing action creators
      const user = {userid: 'abc123'};
      const profile = {fullName: 'Jane Doe'};
      const memberships = [{userid: 'def456'}, {userid: 'ghi789'}];
      it('should be an FSA', () => {
        expect(isFSA(syncActions.loginSuccess({ user, profile, memberships }))).to.be.true;
      });

      it('should create an action to set the logged-in user (plus user\'s profile, careteam memberships)', () => {
        const expectedAction = {
          type: actionTypes.LOGIN_SUCCESS,
          payload: { user, profile, memberships },
          meta: {
            source: actionSources[actionTypes.LOGIN_SUCCESS],
            metric: metrics.LOGIN_SUCCESS
          }
        };
        expect(syncActions.loginSuccess({ user, profile, memberships })).to.deep.equal(expectedAction);
      });
    });

    describe('loginFailure', () => {
      const err = 'Login error!';
      it('should be an FSA', () => {
        let action = syncActions.loginFailure(err);

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to report a login error', () => {
        syncActions.__Rewire__('getLoginErrorMessage', () => err);
        const expectedAction = {
          type: actionTypes.LOGIN_FAILURE,
          error: true,
          payload: new Error(err),
          meta: {source: actionSources[actionTypes.LOGIN_FAILURE]}
        };
        expect(syncActions.loginFailure(err)).to.deep.equal(expectedAction);
        syncActions.__ResetDependency__('getLoginErrorMessage');
      });
    });
  });

  describe('for retrieveTargetsFromStorage', () => {
    describe('setUsersTargets', () => {
      // NB: this is not what this object actually looks like
      // actual shape is irrelevant to testing action creators
      const targets = {
        a1b2c3: ['foo', 'bar'],
        d4e5f6: ['baz']
      };
      it('should be an FSA', () => {
        let action = syncActions.setUsersTargets();

        expect(isFSA(action)).to.be.true;
      });

      it('should create an action to set the users\' target devices', () => {
        const expectedAction = {
          type: actionTypes.SET_USERS_TARGETS,
          payload: { targets },
          meta: {source: actionSources[actionTypes.SET_USERS_TARGETS]}
        };
        expect(syncActions.setUsersTargets(targets)).to.deep.equal(expectedAction);
      });
    });
  });
});<|endoftext|>