<commit_before>import alertsData from '../lib/alerts/index.jsx';
import {AlertTypes, AlertLevels} from '../lib/alerts/index.jsx';
import extensionData from '../lib/libraries/extensions/index.jsx';

const SHOW_STANDARD_ALERT = 'scratch-gui/alerts/SHOW_STANDARD_ALERT';
const SHOW_EXTENSION_ALERT = 'scratch-gui/alerts/SHOW_EXTENSION_ALERT';
const CLOSE_ALERT = 'scratch-gui/alerts/CLOSE_ALERT';
const CLOSE_ALERTS_WITH_ID = 'scratch-gui/alerts/CLOSE_ALERTS_WITH_ID';

const initialState = {
    visible: true,
    // list of alerts, each with properties:
    // * alert type (required): one of AlertTypes
    // * closeButton (optional): bool indicating that we should show close button
    // * content (optional): react element (a <FormattedMessage />)
    // * extentionId (optional): id string that identifies the extension
    // * iconURL (optional): string
    // * level (required): string, one of AlertLevels
<<<<<<< HEAD
    // * message (optional): string
    // * showReconnect (optional): bool
=======
>>>>>>> Removing extraneous message prop from alerts.
    alertsList: []
};

const reducer = function (state, action) {
    if (typeof state === 'undefined') state = initialState;
    switch (action.type) {
    case SHOW_STANDARD_ALERT: { // also will show inline alerts
        const alertId = action.alertId;
        if (alertId) {
            const newAlert = {
                alertId: alertId,
                level: AlertLevels.WARN // default level
            };
            const alertData = alertsData.find(thisAlertData => thisAlertData.alertId === alertId);
            if (alertData) {
                const newList = state.alertsList.filter(curAlert => (
                    !alertData.clearList || alertData.clearList.indexOf(curAlert.alertId) === -1
                ));
                if (action.data && action.data.message) {
                    newAlert.message = action.data.message;
                }

                newAlert.alertType = alertData.alertType || AlertTypes.STANDARD;
                newAlert.closeButton = alertData.closeButton;
                newAlert.content = alertData.content;
                newAlert.iconURL = alertData.iconURL;
                newAlert.iconSpinner = alertData.iconSpinner;
                newAlert.level = alertData.level;

                newList.push(newAlert);
                return Object.assign({}, state, {
                    alertsList: newList
                });
            }
        }
        return state; // if alert not found, show nothing
    }
    case SHOW_EXTENSION_ALERT: {
        const newList = state.alertsList.slice();
        const newAlert = {
            alertType: AlertTypes.EXTENSION,
            message: action.data.message,
            level: AlertLevels.WARN
        };
        const extensionId = action.data.extensionId;
        newAlert.showReconnect = false;
        newAlert.extensionId = extensionId;
        if (extensionId) { // if it's an extension
            const extension = extensionData.find(ext => ext.extensionId === extensionId);
            if (extension) {
                newAlert.showReconnect = true;
                if (extension.name) {
                    // TODO: make translate-friendly
                    newAlert.content = `${action.data.message} ${extension.name}`;
                }
                if (extension.smallPeripheralImage) {
                    newAlert.iconURL = extension.smallPeripheralImage;
                }
                newAlert.closeButton = true;
            }
        }
        newList.push(newAlert);
        return Object.assign({}, state, {
            alertsList: newList
        });
    }
    case CLOSE_ALERT: {
        const newList = state.alertsList.slice();
        newList.splice(action.index, 1);
        return Object.assign({}, state, {
            alertsList: newList
        });
    }
    case CLOSE_ALERTS_WITH_ID: {
        return Object.assign({}, state, {
            alertsList: state.alertsList.filter(curAlert => (
                curAlert.alertId !== action.alertId
            ))
        });
    }
    default:
        return state;
    }
};

/**
 * Action creator to close an alert with the given index.
 *
 * @param {object} index - the index of the alert to close.
 * @return {object} - an object to be passed to the reducer.
 */
const closeAlert = function (index) {
    return {
        type: CLOSE_ALERT,
        index
    };
};

/**
 * Action creator to close all alerts with a given ID.
 *
 * @param {string} alertId - id string of the alert to close
 * @return {object} - an object to be passed to the reducer.
 */
const closeAlertsWithId = function (alertId) {
    return {
        type: CLOSE_ALERTS_WITH_ID,
        alertId
    };
};

/**
 * Action creator to show an alert with the given alertId.
 *
 * @param {string} alertId - id string of the alert to show
 * @return {object} - an object to be passed to the reducer.
 */
const showStandardAlert = function (alertId) {
    return {
        type: SHOW_STANDARD_ALERT,
        alertId
    };
};

/**
 * Action creator to show an alert with the given input data.
 *
 * @param {object} data - data for the alert
 * @param {string} data.message - message for the alert
 * @param {string} data.extensionId - extension ID for the alert
 * @return {object} - an object to be passed to the reducer.
 */
const showExtensionAlert = function (data) {
    return {
        type: SHOW_EXTENSION_ALERT,
        data
    };
};

/**
 * Function to dispatch showing an alert, with optional
 * timeout to make it close/go away.
 *
 * @param {object} dispatch - dispatch function
 * @param {string} alertId - the ID of the alert
 */
const showAlertWithTimeout = function (dispatch, alertId) {
    const alertData = alertsData.find(thisAlertData => thisAlertData.alertId === alertId);
    if (alertData) {
        dispatch(showStandardAlert(alertId));
        if (alertData.maxDisplaySecs) {
            setTimeout(() => {
                dispatch(closeAlertsWithId(alertId));
            }, alertData.maxDisplaySecs * 1000);
        }
    }
};

export {
    reducer as default,
    initialState as alertsInitialState,
    closeAlert,
    showAlertWithTimeout,
    showExtensionAlert,
    showStandardAlert
};
<commit_msg>Minor cleanup in alerts reducer.<commit_after>import alertsData from '../lib/alerts/index.jsx';
import {AlertTypes, AlertLevels} from '../lib/alerts/index.jsx';
import extensionData from '../lib/libraries/extensions/index.jsx';

const SHOW_STANDARD_ALERT = 'scratch-gui/alerts/SHOW_STANDARD_ALERT';
const SHOW_EXTENSION_ALERT = 'scratch-gui/alerts/SHOW_EXTENSION_ALERT';
const CLOSE_ALERT = 'scratch-gui/alerts/CLOSE_ALERT';
const CLOSE_ALERTS_WITH_ID = 'scratch-gui/alerts/CLOSE_ALERTS_WITH_ID';

const initialState = {
    visible: true,
    // list of alerts, each with properties:
    // * alert type (required): one of AlertTypes
    // * closeButton (optional): bool indicating that we should show close button
    // * content (optional): react element (a <FormattedMessage />)
    // * extentionId (optional): id string that identifies the extension
    // * iconURL (optional): string
    // * level (required): string, one of AlertLevels
<<<<<<< HEAD
    // * message (optional): string
    // * showReconnect (optional): bool
=======
>>>>>>> Removing extraneous message prop from alerts.
    alertsList: []
};

const reducer = function (state, action) {
    if (typeof state === 'undefined') state = initialState;
    switch (action.type) {
    case SHOW_STANDARD_ALERT: { // also will show inline alerts
        const alertId = action.alertId;
        if (alertId) {
            const newAlert = {
                alertId: alertId,
                level: AlertLevels.WARN // default level
            };
            const alertData = alertsData.find(thisAlertData => thisAlertData.alertId === alertId);
            if (alertData) {
                const newList = state.alertsList.filter(curAlert => (
                    !alertData.clearList || alertData.clearList.indexOf(curAlert.alertId) === -1
                ));
                if (action.data && action.data.message) {
                    newAlert.message = action.data.message;
                }

                newAlert.alertType = alertData.alertType || AlertTypes.STANDARD;
                newAlert.closeButton = alertData.closeButton;
                newAlert.content = alertData.content;
                newAlert.iconURL = alertData.iconURL;
                newAlert.iconSpinner = alertData.iconSpinner;
                newAlert.level = alertData.level;

                newList.push(newAlert);
                return Object.assign({}, state, {
                    alertsList: newList
                });
            }
        }
        return state; // if alert not found, show nothing
    }
    case SHOW_EXTENSION_ALERT: {
        const newList = state.alertsList.slice();
        const newAlert = {
            alertType: AlertTypes.EXTENSION,
            message: action.data.message,
            level: AlertLevels.WARN
        };
        const extensionId = action.data.extensionId;
        if (extensionId) {
            const newList = state.alertsList.slice();
            const newAlert = {
                level: AlertLevels.WARN,
                extensionId: extensionId,
                showReconnect: true
            };
            const extension = extensionData.find(ext => ext.extensionId === extensionId);
            if (extension) {
                if (extension.name) {
                    // TODO: make translate-friendly
                    newAlert.content = `${action.data.message} ${extension.name}`;
                }
                if (extension.smallPeripheralImage) {
                    newAlert.iconURL = extension.smallPeripheralImage;
                }
                newAlert.closeButton = true;
            }
            newList.push(newAlert);
            return Object.assign({}, state, {
                alertsList: newList
            });
        }
        return state; // if extension not found, show nothing
    }
    case CLOSE_ALERT: {
        const newList = state.alertsList.slice();
        newList.splice(action.index, 1);
        return Object.assign({}, state, {
            alertsList: newList
        });
    }
    case CLOSE_ALERTS_WITH_ID: {
        return Object.assign({}, state, {
            alertsList: state.alertsList.filter(curAlert => (
                curAlert.alertId !== action.alertId
            ))
        });
    }
    default:
        return state;
    }
};

/**
 * Action creator to close an alert with the given index.
 *
 * @param {object} index - the index of the alert to close.
 * @return {object} - an object to be passed to the reducer.
 */
const closeAlert = function (index) {
    return {
        type: CLOSE_ALERT,
        index
    };
};

/**
 * Action creator to close all alerts with a given ID.
 *
 * @param {string} alertId - id string of the alert to close
 * @return {object} - an object to be passed to the reducer.
 */
const closeAlertsWithId = function (alertId) {
    return {
        type: CLOSE_ALERTS_WITH_ID,
        alertId
    };
};

/**
 * Action creator to show an alert with the given alertId.
 *
 * @param {string} alertId - id string of the alert to show
 * @return {object} - an object to be passed to the reducer.
 */
const showStandardAlert = function (alertId) {
    return {
        type: SHOW_STANDARD_ALERT,
        alertId
    };
};

/**
 * Action creator to show an alert with the given input data.
 *
 * @param {object} data - data for the alert
 * @param {string} data.message - message for the alert
 * @param {string} data.extensionId - extension ID for the alert
 * @return {object} - an object to be passed to the reducer.
 */
const showExtensionAlert = function (data) {
    return {
        type: SHOW_EXTENSION_ALERT,
        data
    };
};

/**
 * Function to dispatch showing an alert, with optional
 * timeout to make it close/go away.
 *
 * @param {object} dispatch - dispatch function
 * @param {string} alertId - the ID of the alert
 */
const showAlertWithTimeout = function (dispatch, alertId) {
    const alertData = alertsData.find(thisAlertData => thisAlertData.alertId === alertId);
    if (alertData) {
        dispatch(showStandardAlert(alertId));
        if (alertData.maxDisplaySecs) {
            setTimeout(() => {
                dispatch(closeAlertsWithId(alertId));
            }, alertData.maxDisplaySecs * 1000);
        }
    }
};

export {
    reducer as default,
    initialState as alertsInitialState,
    closeAlert,
    showAlertWithTimeout,
    showExtensionAlert,
    showStandardAlert
};
<|endoftext|>


<commit_before>import { combineReducers } from 'redux'

import {
  FETCH_CLAUDY_INFOS
} from '../actions'

import {
  FETCH_CLAUDY_INFOS_SUCCESS,
  FETCH_CLAUDY_INFOS_FAILURE,
  FETCH_DEVICES_SUCCESS
} from '../actions/services'

export const actions = (state = [], action) => {
  switch (action.type) {
    case FETCH_CLAUDY_INFOS_SUCCESS:
      return action.claudyActions
    default:
      return state
  }
}

export const apps = (state = [], action) => {
  switch (action.type) {
    case FETCH_CLAUDY_INFOS_SUCCESS:
      return action.apps
    default:
      return state
  }
}

export const collectAccounts = (state = [], action) => {
  switch (action.type) {
    case FETCH_CLAUDY_INFOS_SUCCESS:
      return action.collectAccounts
    default:
      return state
  }
}

export const devices = (state = [], action) => {
  switch (action.type) {
    case FETCH_DEVICES_SUCCESS:
      return action.devices
    default:
      return state
  }
}

export const error = (state = null, action) => {
  switch (action.type) {
    case FETCH_CLAUDY_INFOS_FAILURE:
      return action.error
    default:
      return state
  }
}

export const isFetching = (state = false, action) => {
  switch (action.type) {
    case FETCH_CLAUDY_INFOS:
      return true
    default:
      return state
  }
}

const claudy = combineReducers({
  actions,
  apps,
  collectAccounts,
  devices,
  error,
  isFetching
})

export default claudy
<commit_msg>fix: typo in constants import :ambulance:<commit_after>import { combineReducers } from 'redux'

import {
  FETCH_DEVICES_SUCCESS
} from '../actions'

import {
  FETCH_CLAUDY_INFOS,
  FETCH_CLAUDY_INFOS_SUCCESS,
  FETCH_CLAUDY_INFOS_FAILURE
} from '../actions/services'

export const actions = (state = [], action) => {
  switch (action.type) {
    case FETCH_CLAUDY_INFOS_SUCCESS:
      return action.claudyActions
    default:
      return state
  }
}

export const apps = (state = [], action) => {
  switch (action.type) {
    case FETCH_CLAUDY_INFOS_SUCCESS:
      return action.apps
    default:
      return state
  }
}

export const collectAccounts = (state = [], action) => {
  switch (action.type) {
    case FETCH_CLAUDY_INFOS_SUCCESS:
      return action.collectAccounts
    default:
      return state
  }
}

export const devices = (state = [], action) => {
  switch (action.type) {
    case FETCH_DEVICES_SUCCESS:
      return action.devices
    default:
      return state
  }
}

export const error = (state = null, action) => {
  switch (action.type) {
    case FETCH_CLAUDY_INFOS_FAILURE:
      return action.error
    default:
      return state
  }
}

export const isFetching = (state = false, action) => {
  switch (action.type) {
    case FETCH_CLAUDY_INFOS:
      return true
    default:
      return state
  }
}

const claudy = combineReducers({
  actions,
  apps,
  collectAccounts,
  devices,
  error,
  isFetching
})

export default claudy
<|endoftext|>


<commit_before>Werld.Views.Base.Creature = Backbone.View.extend({
  initialize: function() {
    this.container = new Container();
    var image = new Image();
    var SPRITE = this.model.get('SPRITE');
    image.src = SPRITE.SRC;
    image.onload = _.bind(function() {
      var spriteSheet = new SpriteSheet({
        images: [image],
        frames: {
          width: SPRITE.DIMENSIONS[0],
          height: SPRITE.DIMENSIONS[1]
        },
        animations: {
          walkDown:  [0, 3],
          walkLeft:  [4, 7],
          walkRight: [8, 11],
          walkUp:    [12, 15]
        }
      });

      this.bitmapAnimation = new BitmapAnimation(spriteSheet);
      this.bitmapAnimation.currentFrame = 0;
      this.bitmapAnimation.mouseEnabled = true;
      this.bitmapAnimation.paused = true;
      this.bitmapAnimation.onMouseOver = function() {
        this.parent.getStage().canvas.style.cursor = 'pointer';
      };
      this.bitmapAnimation.onMouseOut = function() {
        this.parent.getStage().canvas.style.cursor = '';
      };
      var self = this;
      this.bitmapAnimation.onPress = function(event) {
        if (self.model.alive()) {
          Werld.character.attack(self.model);
        } else {
          self.showLoot(self.model.loot());
        }
      };
      this.bitmapAnimation.tick = _.bind(this.bitmapAnimationTick, this);

      this.characterNameText = new Text();
      this.characterNameText.tick = _.bind(this.characterNameTextTick, this);

      this.messagesContainer = new Container();
      this.messagesContainer.tick = _.bind(this.messagesContainerTick, this);

      this.container.tick = _.bind(this.tick, this);

      this.container.addChild(this.bitmapAnimation);
      this.container.addChild(this.characterNameText);
      this.container.addChild(this.messagesContainer);
    }, this);
  },
  showLoot: function() {
  },
  bitmapAnimationTick: function() {
    var modelCoordinates = this.model.get('coordinates');
    var modelDestinationCoordinates = this.model.get('destination');
    var gotoThrottle = Ticker.getTicks() % 4;
    var advanceThrottle = Ticker.getTicks() % 8;
    var walking = [];

    if (gotoThrottle === 0) {
      if (modelCoordinates[0] > modelDestinationCoordinates[0]) {
        this.bitmapAnimation.gotoAndStop('walkLeft');
        walking[0] = true;
      } else if (modelCoordinates[0] < modelDestinationCoordinates[0]) {
        this.bitmapAnimation.gotoAndStop('walkRight');
        walking[0] = true;
      } else {
        walking[0] = false;
      }

      if (modelCoordinates[1] > modelDestinationCoordinates[1]) {
        this.bitmapAnimation.gotoAndStop('walkUp');
        walking[1] = true;
      } else if (modelCoordinates[1] < modelDestinationCoordinates[1]) {
        this.bitmapAnimation.gotoAndStop('walkDown');
        walking[1] = true;
      } else {
        walking[1] = false;
      }
    }

    if ((walking[0] || walking[1]) && advanceThrottle === 0) {
      this.bitmapAnimation.advance();
    }
  },
  characterNameTextTick: function() {
    this.characterNameText.text = this._characterNameText();

    this.characterNameText.textBaseline = 'top';
    this.characterNameText.textAlign = 'center';
    this.characterNameText.x = 20;
    this.characterNameText.y = -30;
  },
  messagesContainerTick: function() {
    var temporaryCreatureScreenCoordinates = [0, 0];
    var self = this;

    this.messagesContainer.removeAllChildren();
    _(this.model.get('messages')).each(function(message) {
      if (message.content !== '') {
        self.messageText = new Text();
        self.messagesContainer.addChild(self.messageText);

        self.messageText.text = message.content;
        self.messageText.textAlign = 'center';

        if (message.type === 'speech') {
          self.messageText.color = '#cccccc';
          self.messageText.font = '16px "PowellAntique" serif';
        } else if (message.type === 'hit') {
          self.messageText.text = Math.ceil(self.messageText.text);
          self.messageText.shadow = new Shadow('black', 1, 1, 1);
          self.messageText.color = '#ff3300';
          self.messageText.font = '14px "PowellAntique" serif';
        } else {
          self.messageText.color = '#cccccc';
          self.messageText.font = '16px "PowellAntique" serif';
        }

        temporaryCreatureScreenCoordinates[1] -= 30;
        self.messageText.x = 20;
        self.messageText.y = temporaryCreatureScreenCoordinates[1];
      }
    });
  },
  tick: function() {
    var modelCoordinates = this.model.get('coordinates');
    var fixedModelCoordinates = this.model.get('fixedCoordinates');
    var modelDestinationCoordinates = this.model.get('destination');
    var screenCoordinates = Werld.screen.get('coordinates');
    var mapDimensions = Werld.map.get('dimensions');
    var creatureScreenCoordinates;

    if (modelCoordinates[0] >= 0 &&
          modelCoordinates[0] < Werld.util.tileToPixel(mapDimensions[0]) &&
          modelCoordinates[1] >= 0 &&
          modelCoordinates[1] < Werld.util.tileToPixel(mapDimensions[1])) {
      if (this.model.get('fixed')) {
        creatureScreenCoordinates = [
          fixedModelCoordinates[0],
          fixedModelCoordinates[1]
        ];
      } else {
        creatureScreenCoordinates = [
          modelCoordinates[0] - screenCoordinates[0],
          modelCoordinates[1] - screenCoordinates[1]
        ];
      }
    } else {
      return;
    }

    this.container.x = creatureScreenCoordinates[0];
    this.container.y = creatureScreenCoordinates[1];
  }
});
<commit_msg>Fix bitmap animation.<commit_after>Werld.Views.Base.Creature = Backbone.View.extend({
  initialize: function() {
    this.container = new Container();
    this.walking = {};

    var image = new Image();
    var SPRITE = this.model.get('SPRITE');
    image.src = SPRITE.SRC;
    image.onload = _.bind(function() {
      this.spriteSheet = new SpriteSheet({
        images: [image],
        frames: {
          width: SPRITE.DIMENSIONS[0],
          height: SPRITE.DIMENSIONS[1],
          regX: (SPRITE.DIMENSIONS[0] - Werld.Config.PIXELS_PER_TILE) / 2,
          regY: (1.7 * SPRITE.DIMENSIONS[1] - Werld.Config.PIXELS_PER_TILE) / 2
        },
        animations: {
          walkDown:  [0, 3, true, 4],
          walkLeft:  [4, 7, true, 4],
          walkRight: [8, 11, true, 4],
          walkUp:    [12, 15, true, 4]
        }
      });

      this.bitmapAnimation = new BitmapAnimation(this.spriteSheet);
      this.bitmapAnimation.currentFrame = 0;
      this.bitmapAnimation.mouseEnabled = true;
      this.bitmapAnimation.onMouseOver = function() {
        this.parent.getStage().canvas.style.cursor = 'pointer';
      };
      this.bitmapAnimation.onMouseOut = function() {
        this.parent.getStage().canvas.style.cursor = '';
      };
      var self = this;
      this.bitmapAnimation.onPress = function(event) {
        if (self.model.alive()) {
          Werld.character.attack(self.model);
        } else {
          self.showLoot(self.model.loot());
        }
      };
      this.bitmapAnimation.tick = _.bind(this.bitmapAnimationTick, this);

      this.characterNameText = new Text();
      this.characterNameText.tick = _.bind(this.characterNameTextTick, this);

      this.messagesContainer = new Container();
      this.messagesContainer.tick = _.bind(this.messagesContainerTick, this);

      this.container.tick = _.bind(this.tick, this);

      this.container.addChild(this.bitmapAnimation);
      this.container.addChild(this.characterNameText);
      this.container.addChild(this.messagesContainer);
    }, this);
  },
  showLoot: function() {
  },
  bitmapAnimationTick: function() {
    var modelCoordinates = this.model.get('coordinates');
    var modelDestinationCoordinates = this.model.get('destination');

    if (modelCoordinates[1] > modelDestinationCoordinates[1]) {
      if (!this.walking.up) {
        this.bitmapAnimation.gotoAndPlay('walkUp');
        this.walking.up = true;
        this.walking.down = false;
        this.walking.left = false;
        this.walking.right = false;
      }
    } else if (modelCoordinates[1] < modelDestinationCoordinates[1]) {
      if (!this.walking.down) {
        this.bitmapAnimation.gotoAndPlay('walkDown');
        this.walking.up = false;
        this.walking.down = true;
        this.walking.left = false;
        this.walking.right = false;
      }
    } else {
      this.walking.up = false;
      this.walking.down = false;

      if (modelCoordinates[0] > modelDestinationCoordinates[0]) {
        if (!this.walking.left) {
          this.bitmapAnimation.gotoAndPlay('walkLeft');
          this.walking.left = true;
          this.walking.right = false;
        }
      } else if (modelCoordinates[0] < modelDestinationCoordinates[0]) {
        if (!this.walking.right) {
          this.bitmapAnimation.gotoAndPlay('walkRight');
          this.walking.left = false;
          this.walking.right = true;
        }
      } else {
        this.walking.left = false;
        this.walking.right = false;
        this.bitmapAnimation.currentAnimationFrame = 0;
        this.bitmapAnimation.paused = true;
      }
    }
  },
  characterNameTextTick: function() {
    this.characterNameText.text = this._characterNameText();

    this.characterNameText.textBaseline = 'top';
    this.characterNameText.textAlign = 'center';
    this.characterNameText.x = 20;
    this.characterNameText.y = -30;
  },
  messagesContainerTick: function() {
    var temporaryCreatureScreenCoordinates = [0, 0];
    var self = this;

    this.messagesContainer.removeAllChildren();
    _(this.model.get('messages')).each(function(message) {
      if (message.content !== '') {
        self.messageText = new Text();
        self.messagesContainer.addChild(self.messageText);

        self.messageText.text = message.content;
        self.messageText.textAlign = 'center';

        if (message.type === 'speech') {
          self.messageText.color = '#cccccc';
          self.messageText.font = '16px "PowellAntique" serif';
        } else if (message.type === 'hit') {
          self.messageText.text = Math.ceil(self.messageText.text);
          self.messageText.shadow = new Shadow('black', 1, 1, 1);
          self.messageText.color = '#ff3300';
          self.messageText.font = '14px "PowellAntique" serif';
        } else {
          self.messageText.color = '#cccccc';
          self.messageText.font = '16px "PowellAntique" serif';
        }

        temporaryCreatureScreenCoordinates[1] -= 30;
        self.messageText.x = 20;
        self.messageText.y = temporaryCreatureScreenCoordinates[1];
      }
    });
  },
  tick: function() {
    var modelCoordinates = this.model.get('coordinates');
    var fixedModelCoordinates = this.model.get('fixedCoordinates');
    var modelDestinationCoordinates = this.model.get('destination');
    var screenCoordinates = Werld.screen.get('coordinates');
    var mapDimensions = Werld.map.get('dimensions');
    var creatureScreenCoordinates;

    if (modelCoordinates[0] >= 0 &&
          modelCoordinates[0] < Werld.util.tileToPixel(mapDimensions[0]) &&
          modelCoordinates[1] >= 0 &&
          modelCoordinates[1] < Werld.util.tileToPixel(mapDimensions[1])) {
      if (this.model.get('fixed')) {
        creatureScreenCoordinates = [
          fixedModelCoordinates[0],
          fixedModelCoordinates[1]
        ];
      } else {
        creatureScreenCoordinates = [
          modelCoordinates[0] - screenCoordinates[0],
          modelCoordinates[1] - screenCoordinates[1]
        ];
      }
    } else {
      return;
    }

    this.container.x = creatureScreenCoordinates[0];
    this.container.y = creatureScreenCoordinates[1];
  }
});
<|endoftext|>


<commit_before>(function() {
  var storage = new Storage();
  storage.load();

  var graph = new Graph();
  var editor = new Editor();
  editor.setRawText(storage.getEditorText());
  editor.setListener(draw);

  var config = new Config();
  config.apply(storage.getConfigObj());
  config.setListener(draw);

  draw();

  function draw() {
    var commands = editor.getCommands();
    graph.draw(commands);

    storage.save(editor, config);
  }

  function Graph() {
    var _target = document.getElementById('target');

    this.draw = function(commands) {
      _refreshCanvas();

      var git = new GitGraphWrapperExtention({
        template: _createTemplate(),
        reverseArrow: config.reverseArrow,
        orientation: config.orientation,
        mode: config.mode,
        author: config.author
      });

      for (var i=0; i<commands.length; i++) {
        var command = commands[i];
        command.invoke(git);
      }
    };

    function _createTemplate() {
      var template = new GitGraph.Template().get(config.template);

      if (config.colors !== '') {
        var colors = config.colors.split(',');
        template.colors = colors;
      }

      // arrow
      if (config.arrowColor) {
        template.arrow.color = config.arrowColor;
      }
      if (config.arrowSize) {
        template.arrow.size = config.arrowSize;
      }
      if (config.arrowOffset) {
        template.arrow.offset = config.arrowOffset;
      }

      // branch
      if (config.branchColor) {
        template.branch.color = config.branchColor;
      }
      if (config.branchLineWidth) {
        template.branch.lineWidth = config.branchLineWidth;
      }
      if (config.branchMergeStyle) {
        template.branch.mergeStyle = config.branchMergeStyle;
      }
      if (config.branchSpacingX) {
        template.branch.spacingX = config.branchSpacingX;
      }
      if (config.branchSpacingY) {
        template.branch.spacingY = config.branchSpacingY;
      }

      // commit
      if (config.commitSpacingX) {
        template.commit.spacingX = config.commitSpacingX;
      }
      if (config.commitSpacingY) {
        template.commit.spacingY = config.commitSpacingY;
      }
      if (config.commitWidthExtension) {
        template.commit.widthExtension = config.commitWidthExtension;
      }
      if (config.commitColor) {
        template.commit.color = config.commitColor;
      }
      if (config.commitShouldDisplayTooltipsInCompactMode) {
        template.commit.shouldDisplayTooltipsInCompactMode = config.commitShouldDisplayTooltipsInCompactMode === 'true';
      }

      // commit.dot
      if (config.commitDotColor) {
        template.commit.dot.color = config.commitDotColor;
      }
      if (config.commitDotSize) {
        template.commit.dot.size = config.commitDotSize;
      }
      if (config.commitDotStrokeWidth) {
        template.commit.dot.strokeWidth = config.commitDotStrokeWidth;
      }
      if (config.commitDotStrokeColor) {
        template.commit.dot.strokeColor = config.commitDotStrokeColor;
      }

      // commit.message
      if (config.commitMessageColor) {
        template.commit.message.color = config.commitMessageColor;
      }
      if (config.commitMessageDisplay) {
        template.commit.message.display = config.commitMessageDisplay === 'true';
      }
      if (config.commitMessageDisplayAuthor) {
        template.commit.message.displayAuthor = config.commitMessageDisplayAuthor === 'true';
      }
      if (config.commitMessageDisplayBranch) {
        template.commit.message.displayBranch = config.commitMessageDisplayBranch === 'true';
      }
      if (config.commitMessageDisplayHash) {
        template.commit.message.displayHash = config.commitMessageDisplayHash === 'true';
      }
      if (config.commitMessageFont) {
        template.commit.message.font = config.commitMessageFont;
      }

      return template;
    }

    function _refreshCanvas() {
      _removeCurrentCanvas();
      var canvas = _createCanvas();
      _target.appendChild(canvas);
    }

    function _removeCurrentCanvas() {
      if (_target.childNodes.length !== 0) {
        _target.removeChild(_target.firstChild);
      }
    }

    function _createCanvas() {
      var canvas = document.createElement('canvas');
      canvas.id = 'gitGraph';
      return canvas;
    }
  }

  function Command(line) {
    var _elements = _normalize(line);
    var _method = _elements[0];
    var _arguments = _elements.slice(1);

    this.invoke = function(git) {
      if (!(typeof git[_method] === 'function')) {
        return;
      }

      try {
        git[_method].apply(git, _arguments);
      } catch (e) {
        console.warn(e);
      }
    };

    function _normalize(line) {
      var elements = line.split(' ');
      var result = [];

      for (var i=0; i<elements.length; i++) {
        var element = elements[i]

        // remove empty strings
        if (element !== '') {
          result.push(element);
        }
      }

      return result;
    }
  }

  function Editor() {
    var _editor = document.getElementById('editor');
    var _timeoutKey;

    this.setListener = function(listener) {
      _editor.addEventListener('keyup', function() {
        if (_timeoutKey) {
          clearTimeout(_timeoutKey);
        }

        _timeoutKey = setTimeout(function() {
          try {
            listener();
          } finally {
            _timeoutKey = null;
          }
        }, 500);
      });
    };

    this.getCommands = function() {
      var commands = [];

      var lines = _editor.value.split('\n');
      for (var i=0; i<lines.length; i++) {
        var line = lines[i].trim();

        if (line.length !== 0) {
          commands.push(new Command(line));
        }
      }

      return commands;
    };

    this.getRawText = function() {
      return _editor.value;
    };

    this.setRawText = function(value) {
      _editor.value = value;
    };
  }

  function Storage() {
    var KEY = "gitgraph.storage";
    var _editor;
    var _config;

    this.save = function(editor, config) {
      var obj = {
        editor: editor.getRawText(),
        config: config
      };

      localStorage.setItem(KEY, JSON.stringify(obj));
    };

    this.load = function() {
      var json = localStorage.getItem(KEY);
      if (typeof json === 'string') {
        var obj = JSON.parse(json);
        _editor = obj.editor;
        _config = obj.config;
      } else {
        _editor = '';
        _config = {};
      }
    };

    this.getEditorText = function() {
      return _editor;
    };

    this.getConfigObj = function() {
      return _config;
    };

    this.remove = function() {
      localStorage.removeItem(KEY);
    };
  }

  function Config() {
    var _defauls = {
      template: 'metro',
      orientation: 'vertical',
      mode: '',
      author: '',
      reverseArrow: false,
      colors: '',
      arrowColor: '',
      arrowSize: '',
      arrowOffset: '',
      branchColor: '',
      branchLineWidth: '',
      branchMergeStyle: '',
      branchSpacingX: '',
      branchSpacingY: '',
      commitSpacingX: '',
      commitSpacingY: '',
      commitWidthExtension: '',
      commitColor: '',
      commitDotColor: '',
      commitDotSize: '',
      commitDotStrokeWidth: '',
      commitMessageColor: '',
      commitMessageDisplay: '',
      commitMessageDisplayAuthor: '',
      commitMessageDisplayBranch: '',
      commitMessageDisplayHash: '',
      commitMessageFont: '',
      commitShouldDisplayTooltipsInCompactMode: ''
    };

    var _elements = {};
    for (var key in _defauls) {
      _elements[key] = new Element(document.getElementById(key));
    }

    var _listener = function() {};
    var _self = this;

    for (var key in _defauls) {
      this[key] = _elements[key].getValue();
    }

    this.apply = function(obj) {
      for (var key in _defauls) {
        this[key] = obj[key] || _defauls[key];
        _elements[key].setValue(this[key]);
      }
    };

    this.reset = function() {
      this.apply(_defauls);
    };

    this.setListener = function(listener) {
      _listener = listener;
    };

    for (var key in _defauls) {
      _elements[key].addEventListener(_onChange);
    }

    function _onChange() {
      for (var key in _defauls) {
        _self[key] = _elements[key].getValue();
      }

      _listener();
    }

    this.reset();
  }

  function Element(e) {
    this.getValue = function() {
      if (_isCheckbox()) {
        return e.checked;
      } else if (e.type === 'number') {
        return Number(e.value);
      } else {
        return e.value;
      }
    };

    this.setValue = function(value) {
      if (_isCheckbox()) {
        e.checked = value;
      } else {
        e.value = value;
      }
    };

    this.addEventListener = function(listener) {
      e.addEventListener('change', listener);
    };

    function _isCheckbox() {
      return e.type === 'checkbox';
    }
  }

  var bottomArea = document.getElementById('bottom-area');
  var expand = document.getElementById('expand');
  expand.addEventListener('click', function() {
    if (bottomArea.style.height === '') {
      bottomArea.style.height = '80%';
      expand.innerText = 'expand↓';
    } else {
      bottomArea.style.height = '';
      expand.innerText = 'expand↑';
    }
  });
})();
<commit_msg>default author if empty<commit_after>(function() {
  var storage = new Storage();
  storage.load();

  var graph = new Graph();
  var editor = new Editor();
  editor.setRawText(storage.getEditorText());
  editor.setListener(draw);

  var config = new Config();
  config.apply(storage.getConfigObj());
  config.setListener(draw);

  draw();

  function draw() {
    var commands = editor.getCommands();
    graph.draw(commands);

    storage.save(editor, config);
  }

  function Graph() {
    var _target = document.getElementById('target');

    this.draw = function(commands) {
      _refreshCanvas();

      var git = new GitGraphWrapperExtention({
        template: _createTemplate(),
        reverseArrow: config.reverseArrow,
        orientation: config.orientation,
        mode: config.mode,
        author: config.author === '' ? null : config.author
      });

      for (var i=0; i<commands.length; i++) {
        var command = commands[i];
        command.invoke(git);
      }
    };

    function _createTemplate() {
      var template = new GitGraph.Template().get(config.template);

      if (config.colors !== '') {
        var colors = config.colors.split(',');
        template.colors = colors;
      }

      // arrow
      if (config.arrowColor) {
        template.arrow.color = config.arrowColor;
      }
      if (config.arrowSize) {
        template.arrow.size = config.arrowSize;
      }
      if (config.arrowOffset) {
        template.arrow.offset = config.arrowOffset;
      }

      // branch
      if (config.branchColor) {
        template.branch.color = config.branchColor;
      }
      if (config.branchLineWidth) {
        template.branch.lineWidth = config.branchLineWidth;
      }
      if (config.branchMergeStyle) {
        template.branch.mergeStyle = config.branchMergeStyle;
      }
      if (config.branchSpacingX) {
        template.branch.spacingX = config.branchSpacingX;
      }
      if (config.branchSpacingY) {
        template.branch.spacingY = config.branchSpacingY;
      }

      // commit
      if (config.commitSpacingX) {
        template.commit.spacingX = config.commitSpacingX;
      }
      if (config.commitSpacingY) {
        template.commit.spacingY = config.commitSpacingY;
      }
      if (config.commitWidthExtension) {
        template.commit.widthExtension = config.commitWidthExtension;
      }
      if (config.commitColor) {
        template.commit.color = config.commitColor;
      }
      if (config.commitShouldDisplayTooltipsInCompactMode) {
        template.commit.shouldDisplayTooltipsInCompactMode = config.commitShouldDisplayTooltipsInCompactMode === 'true';
      }

      // commit.dot
      if (config.commitDotColor) {
        template.commit.dot.color = config.commitDotColor;
      }
      if (config.commitDotSize) {
        template.commit.dot.size = config.commitDotSize;
      }
      if (config.commitDotStrokeWidth) {
        template.commit.dot.strokeWidth = config.commitDotStrokeWidth;
      }
      if (config.commitDotStrokeColor) {
        template.commit.dot.strokeColor = config.commitDotStrokeColor;
      }

      // commit.message
      if (config.commitMessageColor) {
        template.commit.message.color = config.commitMessageColor;
      }
      if (config.commitMessageDisplay) {
        template.commit.message.display = config.commitMessageDisplay === 'true';
      }
      if (config.commitMessageDisplayAuthor) {
        template.commit.message.displayAuthor = config.commitMessageDisplayAuthor === 'true';
      }
      if (config.commitMessageDisplayBranch) {
        template.commit.message.displayBranch = config.commitMessageDisplayBranch === 'true';
      }
      if (config.commitMessageDisplayHash) {
        template.commit.message.displayHash = config.commitMessageDisplayHash === 'true';
      }
      if (config.commitMessageFont) {
        template.commit.message.font = config.commitMessageFont;
      }

      return template;
    }

    function _refreshCanvas() {
      _removeCurrentCanvas();
      var canvas = _createCanvas();
      _target.appendChild(canvas);
    }

    function _removeCurrentCanvas() {
      if (_target.childNodes.length !== 0) {
        _target.removeChild(_target.firstChild);
      }
    }

    function _createCanvas() {
      var canvas = document.createElement('canvas');
      canvas.id = 'gitGraph';
      return canvas;
    }
  }

  function Command(line) {
    var _elements = _normalize(line);
    var _method = _elements[0];
    var _arguments = _elements.slice(1);

    this.invoke = function(git) {
      if (!(typeof git[_method] === 'function')) {
        return;
      }

      try {
        git[_method].apply(git, _arguments);
      } catch (e) {
        console.warn(e);
      }
    };

    function _normalize(line) {
      var elements = line.split(' ');
      var result = [];

      for (var i=0; i<elements.length; i++) {
        var element = elements[i]

        // remove empty strings
        if (element !== '') {
          result.push(element);
        }
      }

      return result;
    }
  }

  function Editor() {
    var _editor = document.getElementById('editor');
    var _timeoutKey;

    this.setListener = function(listener) {
      _editor.addEventListener('keyup', function() {
        if (_timeoutKey) {
          clearTimeout(_timeoutKey);
        }

        _timeoutKey = setTimeout(function() {
          try {
            listener();
          } finally {
            _timeoutKey = null;
          }
        }, 500);
      });
    };

    this.getCommands = function() {
      var commands = [];

      var lines = _editor.value.split('\n');
      for (var i=0; i<lines.length; i++) {
        var line = lines[i].trim();

        if (line.length !== 0) {
          commands.push(new Command(line));
        }
      }

      return commands;
    };

    this.getRawText = function() {
      return _editor.value;
    };

    this.setRawText = function(value) {
      _editor.value = value;
    };
  }

  function Storage() {
    var KEY = "gitgraph.storage";
    var _editor;
    var _config;

    this.save = function(editor, config) {
      var obj = {
        editor: editor.getRawText(),
        config: config
      };

      localStorage.setItem(KEY, JSON.stringify(obj));
    };

    this.load = function() {
      var json = localStorage.getItem(KEY);
      if (typeof json === 'string') {
        var obj = JSON.parse(json);
        _editor = obj.editor;
        _config = obj.config;
      } else {
        _editor = '';
        _config = {};
      }
    };

    this.getEditorText = function() {
      return _editor;
    };

    this.getConfigObj = function() {
      return _config;
    };

    this.remove = function() {
      localStorage.removeItem(KEY);
    };
  }

  function Config() {
    var _defauls = {
      template: 'metro',
      orientation: 'vertical',
      mode: '',
      author: '',
      reverseArrow: false,
      colors: '',
      arrowColor: '',
      arrowSize: '',
      arrowOffset: '',
      branchColor: '',
      branchLineWidth: '',
      branchMergeStyle: '',
      branchSpacingX: '',
      branchSpacingY: '',
      commitSpacingX: '',
      commitSpacingY: '',
      commitWidthExtension: '',
      commitColor: '',
      commitDotColor: '',
      commitDotSize: '',
      commitDotStrokeWidth: '',
      commitMessageColor: '',
      commitMessageDisplay: '',
      commitMessageDisplayAuthor: '',
      commitMessageDisplayBranch: '',
      commitMessageDisplayHash: '',
      commitMessageFont: '',
      commitShouldDisplayTooltipsInCompactMode: ''
    };

    var _elements = {};
    for (var key in _defauls) {
      _elements[key] = new Element(document.getElementById(key));
    }

    var _listener = function() {};
    var _self = this;

    for (var key in _defauls) {
      this[key] = _elements[key].getValue();
    }

    this.apply = function(obj) {
      for (var key in _defauls) {
        this[key] = obj[key] || _defauls[key];
        _elements[key].setValue(this[key]);
      }
    };

    this.reset = function() {
      this.apply(_defauls);
    };

    this.setListener = function(listener) {
      _listener = listener;
    };

    for (var key in _defauls) {
      _elements[key].addEventListener(_onChange);
    }

    function _onChange() {
      for (var key in _defauls) {
        _self[key] = _elements[key].getValue();
      }

      _listener();
    }

    this.reset();
  }

  function Element(e) {
    this.getValue = function() {
      if (_isCheckbox()) {
        return e.checked;
      } else if (e.type === 'number') {
        return Number(e.value);
      } else {
        return e.value;
      }
    };

    this.setValue = function(value) {
      if (_isCheckbox()) {
        e.checked = value;
      } else {
        e.value = value;
      }
    };

    this.addEventListener = function(listener) {
      e.addEventListener('change', listener);
    };

    function _isCheckbox() {
      return e.type === 'checkbox';
    }
  }

  var bottomArea = document.getElementById('bottom-area');
  var expand = document.getElementById('expand');
  expand.addEventListener('click', function() {
    if (bottomArea.style.height === '') {
      bottomArea.style.height = '80%';
      expand.innerText = 'expand↓';
    } else {
      bottomArea.style.height = '';
      expand.innerText = 'expand↑';
    }
  });
})();
<|endoftext|>


<commit_before>import isPromise from 'is-promise';

import createElements from './createElements';
import {
  checkResolved, getComponents, getRouteMatches, getRouteValues, isResolved,
} from './ResolverUtils';

export default async function* resolveElements(match) {
  const routeMatches = getRouteMatches(match);

  const Components = getComponents(routeMatches);
  const data = getRouteValues(
    routeMatches,
    route => route.getData,
    route => route.data,
  );

  const earlyComponents = Components.some(isPromise) ?
    await Promise.all(Components.map(checkResolved)) : Components;
  const earlyData = data.some(isPromise) ?
    await Promise.all(data.map(checkResolved)) : data;

  let resolvedComponents;
  let resolvedData;

  if (!earlyComponents.every(isResolved) || !earlyData.every(isResolved)) {
    const pendingElements =
      createElements(routeMatches, earlyComponents, earlyData);
    yield pendingElements.every(element => element !== undefined) ?
      pendingElements : undefined;

    resolvedComponents = await Promise.all(Components);
    resolvedData = await Promise.all(data);
  } else {
    resolvedComponents = earlyComponents;
    resolvedData = earlyData;
  }

  yield createElements(routeMatches, resolvedComponents, resolvedData);
}
<commit_msg>Rename internal variables<commit_after>import isPromise from 'is-promise';

import createElements from './createElements';
import {
  checkResolved, getComponents, getRouteMatches, getRouteValues, isResolved,
} from './ResolverUtils';

export default async function* resolveElements(match) {
  const routeMatches = getRouteMatches(match);

  const Components = getComponents(routeMatches);
  const data = getRouteValues(
    routeMatches,
    route => route.getData,
    route => route.data,
  );

  const earlyComponents = Components.some(isPromise) ?
    await Promise.all(Components.map(checkResolved)) : Components;
  const earlyData = data.some(isPromise) ?
    await Promise.all(data.map(checkResolved)) : data;

  let fetchedComponents;
  let fetchedData;

  if (!earlyComponents.every(isResolved) || !earlyData.every(isResolved)) {
    const pendingElements =
      createElements(routeMatches, earlyComponents, earlyData);
    yield pendingElements.every(element => element !== undefined) ?
      pendingElements : undefined;

    fetchedComponents = await Promise.all(Components);
    fetchedData = await Promise.all(data);
  } else {
    fetchedComponents = earlyComponents;
    fetchedData = earlyData;
  }

  yield createElements(routeMatches, fetchedComponents, fetchedData);
}
<|endoftext|>


<commit_before>import Mail from '../classes/Mail'
import { Session, User, db } from '../db'
import crypto from 'crypto'
import { NotFoundAPIError } from '../classes/APIError'
import { Context } from '../classes/Context'
import API, {
  GET,
  parameters
} from '../classes/API'
import GeoIP from '../classes/GeoIP'
import UAParser from 'ua-parser-js'

const mail = new Mail()
const sessionTokenLength = 64

/**
 * Class managing user session endpoints
 */
export default class Sessions extends API {
  /**
   * @inheritdoc
   */
  get type () {
    return 'sessions'
  }

  /**
   * Verify a session token
   * @endpoint
   */
  @GET('/session/:token')
  @parameters('token')
  async verify (ctx) {
    const session = await Session.findOne({
      where: {
        code: ctx.params.token,
        verified: false
      }
    })

    if (!session) {
      throw new NotFoundAPIError({ parameter: 'token' })
    }

    await session.update({
      verified: true,
      lastAccess: Date.now()
    })

    return true
  }

  /**
   * Create a user session verification
   * @param {Context} ctx request context
   * @param {User} user the user the session belongs to
   * @returns {Promise<void>} completes a promise when email is sent
   */
  static async createSession (ctx, user) {
    const session = await Session.create({
      ip: ctx.request.ip,
      userAgent: ctx.state.userAgent,
      code: crypto.randomBytes(sessionTokenLength / 2).toString('hex'),
      userId: user.id
    })

    return Sessions.sendSessionMail(user.email, user.preferredRat.name, session.code, ctx)
  }

  /**
   * Create a verified user session
   * @param {Context} ctx request context
   * @param {User} user the user the session belongs to
   * @param {db.Transaction} transaction Sequelize transaction
   * @returns {Session} user session
   */
  static createVerifiedSession (ctx, user, transaction = undefined) {
    return Session.create({
      ip: ctx.request.ip,
      userAgent: ctx.state.userAgent,
      code: crypto.randomBytes(sessionTokenLength / 2).toString('hex'),
      userId: user.id,
      verified: true
    }, { transaction })
  }

  /**
   * Send a user session verification email
   * @param {string} email the user's email
   * @param {string} name the user's display name
   * @param {string} code verification code
   * @param {Context} ctx request context
   * @returns {Promise<void>} completes a promise when successful
   */
  static sendSessionMail (email, name, code, ctx) {
    const ipAddress = ctx.request.ip

    const geoip = GeoIP.lookup(ipAddress)
    const locationString = `${geoip.city.names.en}, ${geoip.postal.code} ${geoip.country.names.en}`

    return mail.send({
      to: email,
      subject: 'Fuel Rats: Login from a new location',
      body: {
        name,
        intro: 'An attempt was made to login to your Fuel Rats account from a new location.',
        action: {
          instructions: 'Click the button below to authorise the login:',
          button: {
            color: '#d65050',
            text: 'Authorise login',
            link:  Sessions.getVerifyLink(code)
          }
        },
        goToAction: {
          text: 'Authorise login',
          link: Sessions.getVerifyLink(code),
          description: 'Click to authorise the login from a new location'
        },
        dictionary: {
          'Device': Sessions.generateDeviceDescription(ctx.state.userAgent),
          'Location': locationString,
          'IP Address': ipAddress
        },
        outro: 'If this login was not by you then please change your password immediately and contact administrators!',
        signature: 'Sincerely'
      }
    })
  }

  /**
   * Generate a short device description based on a user agent
   * @param {object} userAgent parsed user agent
   * @returns {string} short device description
   */
  static generateDeviceDescription (userAgent) {
    const ua = new UAParser(userAgent)
    if (!ua.getBrowser().name) {
      return 'Unknown device'
    }
    return `${ua.getBrowser().name} ${ua.getBrowser().version} on ${ua.getOS().name}`
  }

  /**
   * Get a verification link
   * @param {string} verifyToken the verification token
   * @returns {string} a verification link
   */
  static getVerifyLink (verifyToken) {
    return `https://fuelrats.com/verify?type=session&t=${verifyToken}`
  }
}
<commit_msg>Session endpoint fix<commit_after>import Mail from '../classes/Mail'
import { Session, User, db } from '../db'
import crypto from 'crypto'
import { NotFoundAPIError } from '../classes/APIError'
import { Context } from '../classes/Context'
import API, {
  GET,
  parameters
} from '../classes/API'
import GeoIP from '../classes/GeoIP'
import UAParser from 'ua-parser-js'

const mail = new Mail()
const sessionTokenLength = 64

/**
 * Class managing user session endpoints
 */
export default class Sessions extends API {
  /**
   * @inheritdoc
   */
  get type () {
    return 'sessions'
  }

  /**
   * Verify a session token
   * @endpoint
   */
  @GET('/sessions/:token')
  @parameters('token')
  async verify (ctx) {
    const session = await Session.findOne({
      where: {
        code: ctx.params.token,
        verified: false
      }
    })

    if (!session) {
      throw new NotFoundAPIError({ parameter: 'token' })
    }

    await session.update({
      verified: true,
      lastAccess: Date.now()
    })

    return true
  }

  /**
   * Create a user session verification
   * @param {Context} ctx request context
   * @param {User} user the user the session belongs to
   * @returns {Promise<void>} completes a promise when email is sent
   */
  static async createSession (ctx, user) {
    const session = await Session.create({
      ip: ctx.request.ip,
      userAgent: ctx.state.userAgent,
      code: crypto.randomBytes(sessionTokenLength / 2).toString('hex'),
      userId: user.id
    })

    return Sessions.sendSessionMail(user.email, user.preferredRat.name, session.code, ctx)
  }

  /**
   * Create a verified user session
   * @param {Context} ctx request context
   * @param {User} user the user the session belongs to
   * @param {db.Transaction} transaction Sequelize transaction
   * @returns {Session} user session
   */
  static createVerifiedSession (ctx, user, transaction = undefined) {
    return Session.create({
      ip: ctx.request.ip,
      userAgent: ctx.state.userAgent,
      code: crypto.randomBytes(sessionTokenLength / 2).toString('hex'),
      userId: user.id,
      verified: true
    }, { transaction })
  }

  /**
   * Send a user session verification email
   * @param {string} email the user's email
   * @param {string} name the user's display name
   * @param {string} code verification code
   * @param {Context} ctx request context
   * @returns {Promise<void>} completes a promise when successful
   */
  static sendSessionMail (email, name, code, ctx) {
    const ipAddress = ctx.request.ip

    const geoip = GeoIP.lookup(ipAddress)
    const locationString = `${geoip.city.names.en}, ${geoip.postal.code} ${geoip.country.names.en}`

    return mail.send({
      to: email,
      subject: 'Fuel Rats: Login from a new location',
      body: {
        name,
        intro: 'An attempt was made to login to your Fuel Rats account from a new location.',
        action: {
          instructions: 'Click the button below to authorise the login:',
          button: {
            color: '#d65050',
            text: 'Authorise login',
            link:  Sessions.getVerifyLink(code)
          }
        },
        goToAction: {
          text: 'Authorise login',
          link: Sessions.getVerifyLink(code),
          description: 'Click to authorise the login from a new location'
        },
        dictionary: {
          'Device': Sessions.generateDeviceDescription(ctx.state.userAgent),
          'Location': locationString,
          'IP Address': ipAddress
        },
        outro: 'If this login was not by you then please change your password immediately and contact administrators!',
        signature: 'Sincerely'
      }
    })
  }

  /**
   * Generate a short device description based on a user agent
   * @param {object} userAgent parsed user agent
   * @returns {string} short device description
   */
  static generateDeviceDescription (userAgent) {
    const ua = new UAParser(userAgent)
    if (!ua.getBrowser().name) {
      return 'Unknown device'
    }
    return `${ua.getBrowser().name} ${ua.getBrowser().version} on ${ua.getOS().name}`
  }

  /**
   * Get a verification link
   * @param {string} verifyToken the verification token
   * @returns {string} a verification link
   */
  static getVerifyLink (verifyToken) {
    return `https://fuelrats.com/verify?type=session&t=${verifyToken}`
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import { Text, StyleSheet } from 'react-native';
import { DARKER_GREY, APP_FONT_FAMILY, COLD_BREACH_BLUE, DANGER_RED } from '../globalStyles';

import {
  selectAverageTemperature,
  selectColdCumulativeBreach,
  selectNumberOfColdConsecutiveBreaches,
  selectNumberOfHotConsecutiveBreaches,
  selectHotCumulativeBreach,
} from '../selectors/fridge';
import { ColdBreachIcon, HotBreachIcon, Paper } from './index';
import { BreachManHappy } from './BreachManHappy';
import { vaccineStrings } from '../localization/index';

const BreachCardComponent = ({ config }) => {
  const { type, breachCount, headerText, message } = config;
  let icon = null;
  let textStyle = {};
  let text = breachCount?.toString();

  switch (type) {
    case 'cold':
      icon = <ColdBreachIcon color={COLD_BREACH_BLUE} />;
      textStyle = localStyles.coldText;
      break;
    case 'hot':
      icon = <HotBreachIcon color={DANGER_RED} />;
      textStyle = localStyles.hotText;
      break;
    case 'text':
      textStyle = [localStyles.hotText, { color: DARKER_GREY }];
      text = message;
      break;
    default:
      break;
  }

  if (!breachCount && type !== 'text') {
    icon = <BreachManHappy />;
    text = vaccineStrings.no_breaches;
    textStyle = localStyles.noBreachText;
  }

  return (
    <Paper
      headerText={headerText}
      style={localStyles.card}
      contentContainerStyle={{ alignItems: 'center', paddingBottom: 10 }}
    >
      <Text style={textStyle}>{text}</Text>
      {icon}
    </Paper>
  );
};

BreachCardComponent.defaultProps = {
  breachCount: 0,
  headerText: '',
  message: '',
};
BreachCardComponent.propTypes = {
  breachCount: PropTypes.number,
  config: PropTypes.shape({
    type: PropTypes.string.isRequired,
    breachCount: PropTypes.number,
    headerText: PropTypes.string,
    message: PropTypes.string,
  }).isRequired,
  headerText: PropTypes.string,
  message: PropTypes.string,
  type: PropTypes.string.isRequired,
};

const localStyles = StyleSheet.create({
  hotText: {
    color: DANGER_RED,
    fontSize: 50,
    fontFamily: APP_FONT_FAMILY,
  },
  coldText: {
    color: COLD_BREACH_BLUE,
    fontSize: 50,
    fontFamily: APP_FONT_FAMILY,
  },
  noBreachText: {
    fontSize: 12,
    color: DARKER_GREY,
    fontFamily: APP_FONT_FAMILY,
  },
});

const stateToProps = (state, props) => {
  const { type } = props;

  switch (type) {
    case 'AVERAGE_TEMPERATURE':
      return {
        config: {
          headerText: vaccineStrings.average_temperature,
          message: selectAverageTemperature(state),
          type: 'text',
        },
      };
    case 'COLD_CONSECUTIVE':
      return {
        config: {
          breachCount: selectNumberOfColdConsecutiveBreaches(state),
          headerText: vaccineStrings.consecutive_breach,
          type: 'cold',
        },
      };
    case 'COLD_CUMULATIVE':
      return {
        config: {
          type: 'cold',
          breachCount: selectColdCumulativeBreach(state),
          headerText: vaccineStrings.cumulative_breach,
        },
      };
    case 'HOT_CONSECUTIVE':
      return {
        config: {
          breachCount: selectNumberOfHotConsecutiveBreaches(state),
          headerText: vaccineStrings.consecutive_breach,
          type: 'hot',
        },
      };
    case 'HOT_CUMULATIVE':
      return {
        config: {
          breachCount: selectHotCumulativeBreach(state),
          headerText: vaccineStrings.cumulative_breach,
          type: 'hot',
        },
      };
    default:
      return {
        config: {
          type: 'unknown',
        },
      };
  }
};

export const BreachCard = connect(stateToProps)(BreachCardComponent);
<commit_msg>cumulative breaches are strings<commit_after>import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import { Text, StyleSheet } from 'react-native';
import { DARKER_GREY, APP_FONT_FAMILY, COLD_BREACH_BLUE, DANGER_RED } from '../globalStyles';

import {
  selectAverageTemperature,
  selectColdCumulativeBreach,
  selectNumberOfColdConsecutiveBreaches,
  selectNumberOfHotConsecutiveBreaches,
  selectHotCumulativeBreach,
} from '../selectors/fridge';
import { ColdBreachIcon, HotBreachIcon, Paper } from './index';
import { BreachManHappy } from './BreachManHappy';
import { vaccineStrings } from '../localization/index';

const BreachCardComponent = ({ config }) => {
  const { type, breachCount, headerText, message } = config;
  let icon = null;
  let textStyle = {};
  let text = breachCount?.toString();

  switch (type) {
    case 'cold':
      icon = <ColdBreachIcon color={COLD_BREACH_BLUE} />;
      textStyle = localStyles.coldText;
      break;
    case 'hot':
      icon = <HotBreachIcon color={DANGER_RED} />;
      textStyle = localStyles.hotText;
      break;
    case 'text':
      textStyle = [localStyles.hotText, { color: DARKER_GREY }];
      text = message;
      break;
    default:
      break;
  }

  if (!breachCount && type !== 'text') {
    icon = <BreachManHappy />;
    text = vaccineStrings.no_breaches;
    textStyle = localStyles.noBreachText;
  }

  return (
    <Paper
      headerText={headerText}
      style={localStyles.card}
      contentContainerStyle={{ alignItems: 'center', paddingBottom: 10 }}
    >
      <Text style={textStyle}>{text}</Text>
      {icon}
    </Paper>
  );
};

BreachCardComponent.defaultProps = {
  breachCount: 0,
  headerText: '',
  message: '',
};
BreachCardComponent.propTypes = {
  breachCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  config: PropTypes.shape({
    type: PropTypes.string.isRequired,
    breachCount: PropTypes.number,
    headerText: PropTypes.string,
    message: PropTypes.string,
  }).isRequired,
  headerText: PropTypes.string,
  message: PropTypes.string,
  type: PropTypes.string.isRequired,
};

const localStyles = StyleSheet.create({
  hotText: {
    color: DANGER_RED,
    fontSize: 50,
    fontFamily: APP_FONT_FAMILY,
  },
  coldText: {
    color: COLD_BREACH_BLUE,
    fontSize: 50,
    fontFamily: APP_FONT_FAMILY,
  },
  noBreachText: {
    fontSize: 12,
    color: DARKER_GREY,
    fontFamily: APP_FONT_FAMILY,
  },
});

const stateToProps = (state, props) => {
  const { type } = props;

  switch (type) {
    case 'AVERAGE_TEMPERATURE':
      return {
        config: {
          headerText: vaccineStrings.average_temperature,
          message: selectAverageTemperature(state),
          type: 'text',
        },
      };
    case 'COLD_CONSECUTIVE':
      return {
        config: {
          breachCount: selectNumberOfColdConsecutiveBreaches(state),
          headerText: vaccineStrings.consecutive_breach,
          type: 'cold',
        },
      };
    case 'COLD_CUMULATIVE':
      return {
        config: {
          type: 'cold',
          breachCount: selectColdCumulativeBreach(state),
          headerText: vaccineStrings.cumulative_breach,
        },
      };
    case 'HOT_CONSECUTIVE':
      return {
        config: {
          breachCount: selectNumberOfHotConsecutiveBreaches(state),
          headerText: vaccineStrings.consecutive_breach,
          type: 'hot',
        },
      };
    case 'HOT_CUMULATIVE':
      return {
        config: {
          breachCount: selectHotCumulativeBreach(state),
          headerText: vaccineStrings.cumulative_breach,
          type: 'hot',
        },
      };
    default:
      return {
        config: {
          type: 'unknown',
        },
      };
  }
};

export const BreachCard = connect(stateToProps)(BreachCardComponent);
<|endoftext|>


<commit_before>import .Size;

module.exports = Class(function () {

  this.init = function (opts) {
    this._opts = opts;
  }

  this.getId = function () { return this._opts.id; }

  this.getName = function () { return this._opts.name; }

  this.getTarget = function () { return this._opts.target; }

  this.isNativeTarget = function () { return /^native/.test(this._opts.target); }

  this.centerSimulatorX = function () {
    return 'centerX' in this._opts ? this._opts.centerX : true;
  }

  this.centerSimulatorY = function () {
    return 'centerY' in this._opts ? this._opts.centerY : true;
  }

  this.getScreenSize = function (isRotated) {
    var opts = this._opts;
    var size = opts && opts
             ? new Size(opts.width, opts.height)
             : new Size();

    return isRotated ? size.rotate() : size;
  }

  this.getChromeSize = function (isRotated) {
    var opts = this._opts;
    var size = opts && opts.background
             ? new Size(opts.background.width, opts.background.height)
             : new Size();

    return isRotated ? size.rotate() : size;
  }

  // some devices have browser chrome, so the renderable viewport is smaller
  // than the reported screen size
  this.getViewportSize = function (rotation) {
    var opts = this._opts;
    if (opts.viewportSize) {
      return Array.isArray(opts.viewportSize)
               ? new Size(opts.viewportSize[rotation % opts.viewportSize.length])
               : new Size(opts.viewportSize);
    } else {
      return this.getScreenSize(rotation % 2);
    }
  }

  this.getCustomStyle = function () {
    return this._opts.style;
  }

  this.getBackground = function (rotation) {
    var background = this._opts.background;
    if (Array.isArray(background)) {
      return background[rotation % background.length];
    }

    return background;
  }

  this.getDevicePixelRatio = function () {
    return this._opts.devicePixelRatio || 1;
  }

  this.canRotate = function () {
    return 'canRotate' in this._opts ? !!this._opts.canRotate : true;
  }

  this.canDrag = function () {
    return 'canDrag' in this._opts ? !!this._opts.canDrag : true;
  }
});

module.exports.get = function(id, opts) {
  var info = module.exports.allInfo[id];
  if (info) { return new module.exports(info); }

  if (!opts) { opts = {}; }

  var match;
  function matchResolution (id) {
    var info = module.exports.defaults[id];
    if (opts.screen.width == info.width && opts.screen.height == info.height) {
      match = copy(info);
    } else if (opts.screen.width == info.height && opts.screen.height == info.width) {
      match = copy(info);
      match.rotation = 1;
    }
  }

  var fallback = 'mobile';
  if (opts.screen) {
    if (/-ios$/.test(id)) {
      ['iphone', 'iphone4', 'iphone5', 'ipad', 'ipad3'].forEach(bind(this, matchResolution));
    } else if (/-android$/.test(id)) {
      ['nexus', 'galaxy-nexus'].forEach(bind(this, matchResolution));
    }
  }

  if (!match) {
    match = copy(module.exports.defaults[fallback]);
    if (opts.width) { match.width = opts.width; }
    if (opts.height) { match.height = opts.height; }
    if (/^browser-/.test(id)) {
      opts.target = 'browser-mobile';
    }
  }

  return new module.exports(match);
};

module.exports.setInfo = function (allInfo) {
  for (var id in allInfo) {
    allInfo[id].id = id;
  }
  module.exports.allInfo = allInfo;
};
<commit_msg>add missing copy function<commit_after>import .Size;

module.exports = Class(function () {

  this.init = function (opts) {
    this._opts = opts;
  }

  this.getId = function () { return this._opts.id; }

  this.getName = function () { return this._opts.name; }

  this.getTarget = function () { return this._opts.target; }

  this.isNativeTarget = function () { return /^native/.test(this._opts.target); }

  this.centerSimulatorX = function () {
    return 'centerX' in this._opts ? this._opts.centerX : true;
  }

  this.centerSimulatorY = function () {
    return 'centerY' in this._opts ? this._opts.centerY : true;
  }

  this.getScreenSize = function (isRotated) {
    var opts = this._opts;
    var size = opts && opts
             ? new Size(opts.width, opts.height)
             : new Size();

    return isRotated ? size.rotate() : size;
  }

  this.getChromeSize = function (isRotated) {
    var opts = this._opts;
    var size = opts && opts.background
             ? new Size(opts.background.width, opts.background.height)
             : new Size();

    return isRotated ? size.rotate() : size;
  }

  // some devices have browser chrome, so the renderable viewport is smaller
  // than the reported screen size
  this.getViewportSize = function (rotation) {
    var opts = this._opts;
    if (opts.viewportSize) {
      return Array.isArray(opts.viewportSize)
               ? new Size(opts.viewportSize[rotation % opts.viewportSize.length])
               : new Size(opts.viewportSize);
    } else {
      return this.getScreenSize(rotation % 2);
    }
  }

  this.getCustomStyle = function () {
    return this._opts.style;
  }

  this.getBackground = function (rotation) {
    var background = this._opts.background;
    if (Array.isArray(background)) {
      return background[rotation % background.length];
    }

    return background;
  }

  this.getDevicePixelRatio = function () {
    return this._opts.devicePixelRatio || 1;
  }

  this.canRotate = function () {
    return 'canRotate' in this._opts ? !!this._opts.canRotate : true;
  }

  this.canDrag = function () {
    return 'canDrag' in this._opts ? !!this._opts.canDrag : true;
  }
});

function copy(obj) {
  return JSON.parse(JSON.stringify(obj));
}

module.exports.get = function(id, opts) {
  var info = module.exports.allInfo[id];
  if (info) { return new module.exports(info); }

  if (!opts) { opts = {}; }

  var match;
  function matchResolution (id) {
    var info = module.exports.defaults[id];
    if (opts.screen.width == info.width && opts.screen.height == info.height) {
      match = copy(info);
    } else if (opts.screen.width == info.height && opts.screen.height == info.width) {
      match = copy(info);
      match.rotation = 1;
    }
  }

  var fallback = 'mobile';
  if (opts.screen) {
    if (/-ios$/.test(id)) {
      ['iphone', 'iphone4', 'iphone5', 'ipad', 'ipad3'].forEach(bind(this, matchResolution));
    } else if (/-android$/.test(id)) {
      ['nexus', 'galaxy-nexus'].forEach(bind(this, matchResolution));
    }
  }

  if (!match) {
    match = copy(module.exports.defaults[fallback]);
    if (opts.width) { match.width = opts.width; }
    if (opts.height) { match.height = opts.height; }
    if (/^browser-/.test(id)) {
      opts.target = 'browser-mobile';
    }
  }

  return new module.exports(match);
};

module.exports.setInfo = function (allInfo) {
  for (var id in allInfo) {
    allInfo[id].id = id;
  }
  module.exports.allInfo = allInfo;
};
<|endoftext|>


<commit_before>/*
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apache License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://opensource.org/licenses/Apache-2.0/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 */

var appData   = Windows.Storage.ApplicationData.current,
    BADGE_KEY = 'cordova_badge_number';

/**
 * Clear the badge number.
 *
 * @param [ Function ] success Success callback
 * @param [ Function ] error   Error callback
 *
 * @return [ Void ]
 */
exports.clear = function (success, error) {
    exports.set(success, error, [0]);
};

/**
 * Get the badge number.
 *
 * @param [ Function ] success Success callback
 * @param [ Function ] error   Error callback
 *
 * @return [ Void ]
 */
exports.get = function (success, error) {
    var badge = appData.localSettings.values[BADGE_KEY];

    success(badge || 0);
};

/**
 * Set the badge number.
 *
 * @param [ Function ] success Success callback
 * @param [ Function ] error   Error callback
 * @param [ Int ]      badge   The badge number
 *
 * @return [ Void ]
 */
exports.set = function (success, error, args) {
    var notifications = Windows.UI.Notifications,
        badge         = args[0],
        type          = notifications.BadgeTemplateType.badgeNumber,
        xml           = notifications.BadgeUpdateManager.getTemplateContent(type),
        attrs         = xml.getElementsByTagName('badge'),
        notification  = new notifications.BadgeNotification(xml);

    attrs[0].setAttribute('value', badge);

    notifications.BadgeUpdateManager
        .createBadgeUpdaterForApplication()
        .update(notification);

    exports.saveBadge(badge);

    success(badge);
};

/**
 * Persist the badge of the app icon so that `getBadge` is able to return the
 * badge number back to the client.
 *
 * @param [ Int ] badge The badge number
 *
 * @return [ Void ]
 */
exports.saveBadge = function (badge) {
    appData.localSettings.values[BADGE_KEY] = badge;
};

cordova.commandProxy.add('Badge', exports);
<commit_msg>Persist configs and load on app start (Windows)<commit_after>/*
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apache License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://opensource.org/licenses/Apache-2.0/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 */

var appData    = Windows.Storage.ApplicationData.current,
    CONFIG_KEY = 'APPBadgeConfigKey',
    BADGE_KEY  = 'cordova_badge_number';

/**
 * Clear the badge number.
 *
 * @param [ Function ] success Success callback
 * @param [ Function ] error   Error callback
 *
 * @return [ Void ]
 */
exports.clear = function (success, error) {
    exports.set(success, error, [0]);
};

/**
 * Get the badge number.
 *
 * @param [ Function ] success Success callback
 * @param [ Function ] error   Error callback
 *
 * @return [ Void ]
 */
exports.get = function (success, error) {
    var badge = appData.localSettings.values[BADGE_KEY];

    success(badge || 0);
};

/**
 * Set the badge number.
 *
 * @param [ Function ] success Success callback
 * @param [ Function ] error   Error callback
 * @param [ Int ]      badge   The badge number
 *
 * @return [ Void ]
 */
exports.set = function (success, error, args) {
    var notifications = Windows.UI.Notifications,
        badge         = args[0],
        type          = notifications.BadgeTemplateType.badgeNumber,
        xml           = notifications.BadgeUpdateManager.getTemplateContent(type),
        attrs         = xml.getElementsByTagName('badge'),
        notification  = new notifications.BadgeNotification(xml);

    attrs[0].setAttribute('value', badge);

    notifications.BadgeUpdateManager
        .createBadgeUpdaterForApplication()
        .update(notification);

    exports.saveBadge(badge);

    success(badge);
};

/**
 * Save the badge config.
 *
 * @param [ Function ] success Success callback
 * @param [ Function ] error   Error callback
 * @param [ Int ]      config  The config map
 *
 * @return [ Void ]
 */
exports.save = function (success, error, args) {
    var config = args[0],
        json   = JSON.stringify(config);

    appData.localSettings.values[CONFIG_KEY] = json;
};

/**
 * Load the badge config.
 *
 * @param [ Function ] success Success callback
 * @param [ Function ] error   Error callback
 *
 * @return [ Void ]
 */
exports.load = function (success, error) {
    var json   = appData.localSettings.values[CONFIG_KEY],
        config = JSON.parse(json);

    success(config);
};

/**
 * Persist the badge of the app icon so that `getBadge` is able to return the
 * badge number back to the client.
 *
 * @param [ Int ] badge The badge number
 *
 * @return [ Void ]
 */
exports.saveBadge = function (badge) {
    appData.localSettings.values[BADGE_KEY] = badge;
};

cordova.commandProxy.add('Badge', exports);
<|endoftext|>


<commit_before>/* eslint-disable no-unused-expressions */
/* eslint-disable react/forbid-prop-types */
import React, { useCallback } from 'react';
import { View, ScrollView, StyleSheet } from 'react-native';

import PropTypes from 'prop-types';
import { connect } from 'react-redux';

import { PageButton } from './PageButton';

import { FORM_INPUT_TYPES } from '../utilities/formInputConfigs';
import { FormTextInput } from './FormInputs/FromTextInput';
import { FormDateInput } from './FormInputs/FormDateInput';
import { FormToggle } from './FormInputs/FormToggle';
import { FormDropdown } from './FormInputs/FormDropdown';
import { FormSlider } from './FormInputs/FormSlider';

import globalStyles, { SUSSOL_ORANGE, WHITE } from '../globalStyles';
import { modalStrings, generalStrings } from '../localization';
import { selectForm, selectCanSaveForm, selectCompletedForm } from '../selectors/form';
import { FormActions } from '../actions/FormActions';

/**
 * Component which will manage and control a set of user inputs of a form.
 * The configuration for the inputs comes from `formInputConfigs.js` where
 * the configs are defined for common inputs to create a form semi-declaritively.
 *
 * Uses the `Form` object within redux state to help manage state.
 *
 * @param {func}  onCancel        Callback passed from a parent to control the cancel button.
 * @param {func}  onUpdateForm    Takes the field/value and updates state within redux.
 * @param {func}  onCancelPressed Resets the form state in redux and calls the onClose callback.
 * @param {bool}  canSaveForm     Indicator that all fields are valid and required fields are filled
 * @param {func}  completedForm   Object containing key:value pairs of valid inputs from the form.
 * @param {func}  initialiseForm  Dispatcher to initialise the redux state with the correct config.
 * @param {func}  onSave          Callback to invoke on saving the form, passing back completedForm.
 * @param {bool}  isDisabled      Indicator whether this Form is disabled.
 * @param {Array} inputConfig     Configuration array of input config objects.
 *                                See { getFormInputConfig } from src/utilities/formInputConfigs.
 */
const FormControlComponent = ({
  onUpdateForm,
  onCancelPressed,
  canSaveForm,
  completedForm,
  form,
  initialiseForm,
  onSave,
  inputConfig,
  isDisabled,
  isSearchForm,
}) => {
  const [refs, setRefs] = React.useState([]);

  React.useEffect(() => {
    initialiseForm(inputConfig);
    setRefs({ length: inputConfig.length });
  }, []);

  const onSaveCompletedForm = useCallback(() => onSave(completedForm), [onSave, completedForm]);

  const nextFocus = (index, key) => value => {
    onUpdateForm(key, value);
    refs[index + 1]?.current?.focus?.();
  };

  const formInputs = () =>
    inputConfig.map(
      (
        {
          type,
          key,
          isRequired,
          validator,
          initialValue,
          label,
          invalidMessage,
          isEditable,
          ...rest
        },
        index
      ) => {
        refs[index] = React.useRef();
        switch (type) {
          case FORM_INPUT_TYPES.TEXT: {
            return (
              <FormTextInput
                ref={refs[index]}
                form={form}
                formKey={key}
                onSubmit={nextFocus(index, key)}
                key={key}
                value={initialValue}
                isRequired={isRequired}
                onValidate={validator}
                onChangeText={value => onUpdateForm(key, value)}
                label={label}
                invalidMessage={invalidMessage}
                isDisabled={!isEditable || isDisabled}
              />
            );
          }
          case FORM_INPUT_TYPES.DATE: {
            return (
              <FormDateInput
                ref={refs[index]}
                key={key}
                isRequired={isRequired}
                label={label}
                value={initialValue}
                onChangeDate={value => onUpdateForm(key, value)}
                onValidate={validator}
                invalidMessage={invalidMessage}
                onSubmit={nextFocus(index, key)}
                isDisabled={isDisabled}
              />
            );
          }
          case FORM_INPUT_TYPES.DROPDOWN: {
            const { options, optionKey } = rest;
            return (
              <FormDropdown
                key={key}
                isRequired={isRequired}
                label={label}
                value={completedForm?.[key] ?? initialValue}
                onValueChange={value => onUpdateForm(key, value)}
                options={options}
                optionKey={optionKey}
                isDisabled={!isEditable || isDisabled}
              />
            );
          }
          case FORM_INPUT_TYPES.TOGGLE: {
            const { options, optionLabels } = rest;
            return (
              <FormToggle
                options={options}
                optionLabels={optionLabels}
                value={completedForm?.[key] ?? initialValue}
                onValueChange={value => onUpdateForm(key, value)}
                key={key}
                label={label}
                isRequired={isRequired}
                isDisabled={!isEditable || isDisabled}
              />
            );
          }
          case FORM_INPUT_TYPES.SLIDER: {
            const { maximumValue, minimumValue, step } = rest;
            return (
              <FormSlider
                ref={refs[index]}
                maxmimumValue={maximumValue}
                minimumValue={minimumValue}
                step={step}
                value={completedForm?.[key] ?? initialValue}
                onValueChange={value => onUpdateForm(key, value)}
                key={key}
                label={label}
                isRequired={isRequired}
                isDisabled={isDisabled}
              />
            );
          }
          default:
            return null;
        }
      }
    );

  const Buttons = useCallback(
    () =>
      isSearchForm ? (
        <PageButton
          onPress={onSaveCompletedForm}
          style={{ ...localStyles.saveButton, width: 300 }}
          isDisabled={!canSaveForm || isDisabled}
          textStyle={localStyles.saveButtonTextStyle}
          text={generalStrings.search}
        />
      ) : (
        <>
          <PageButton
            onPress={onSaveCompletedForm}
            style={localStyles.saveButton}
            isDisabled={!canSaveForm || isDisabled}
            textStyle={localStyles.saveButtonTextStyle}
            text={generalStrings.save}
          />
          <PageButton
            style={localStyles.cancelButton}
            onPress={onCancelPressed}
            textStyle={localStyles.cancelButtonTextStyle}
            text={modalStrings.cancel}
          />
        </>
      ),
    [isSearchForm, onSaveCompletedForm, canSaveForm, isDisabled, onCancelPressed]
  );

  return (
    <View style={localStyles.flexOne}>
      <ScrollView style={localStyles.whiteBackground}>
        <View style={localStyles.flexRow}>
          <View style={localStyles.flexOne} />
          <View style={localStyles.flexTen}>{formInputs()}</View>
          <View style={localStyles.flexOne} />
        </View>
      </ScrollView>
      <View style={localStyles.buttonsRow}>
        <Buttons />
      </View>
    </View>
  );
};

const mapDispatchToProps = (dispatch, ownProps) => {
  const { onCancel } = ownProps;

  const initialiseForm = config => dispatch(FormActions.initialiseForm(config));
  const onUpdateForm = (key, value) => dispatch(FormActions.updateForm(key, value));
  const onCancelPressed = () => {
    dispatch(FormActions.resetForm());
    onCancel();
  };
  return { initialiseForm, onUpdateForm, onCancelPressed };
};

const mapStateToProps = state => {
  const form = selectForm(state);
  const canSaveForm = selectCanSaveForm(state);
  const completedForm = selectCompletedForm(state);
  return { form, canSaveForm, completedForm };
};

FormControlComponent.defaultProps = {
  completedForm: null,
  isDisabled: false,
  isSearchForm: false,
  form: {},
};

FormControlComponent.propTypes = {
  canSaveForm: PropTypes.bool.isRequired,
  completedForm: PropTypes.object,
  form: PropTypes.object,
  initialiseForm: PropTypes.func.isRequired,
  onSave: PropTypes.func.isRequired,
  onUpdateForm: PropTypes.func.isRequired,
  inputConfig: PropTypes.array.isRequired,
  onCancelPressed: PropTypes.func.isRequired,
  isDisabled: PropTypes.bool,
  isSearchForm: PropTypes.bool,
};

export const FormControl = connect(mapStateToProps, mapDispatchToProps)(FormControlComponent);

const localStyles = StyleSheet.create({
  saveButton: {
    ...globalStyles.button,
    backgroundColor: SUSSOL_ORANGE,
    alignSelf: 'center',
  },
  saveButtonTextStyle: {
    ...globalStyles.buttonText,
    color: 'white',
    fontSize: 14,
  },
  cancelButton: {
    ...globalStyles.button,

    alignSelf: 'center',
  },
  cancelButtonTextStyle: {
    ...globalStyles.buttonText,
    color: SUSSOL_ORANGE,
    fontSize: 14,
  },
  flexOne: { flex: 1 },
  flexTen: { flex: 10 },
  flexRow: { flex: 1, flexDirection: 'row' },
  buttonsRow: { marginTop: 10, flexDirection: 'row-reverse' },
  whiteBackground: { backgroundColor: WHITE },
});
<commit_msg>Update form control component props<commit_after>/* eslint-disable no-unused-expressions */
/* eslint-disable react/forbid-prop-types */
import React, { useCallback } from 'react';
import { View, ScrollView, StyleSheet } from 'react-native';

import PropTypes from 'prop-types';
import { connect } from 'react-redux';

import { PageButton } from './PageButton';

import { FORM_INPUT_TYPES } from '../utilities/formInputConfigs';
import { FormTextInput } from './FormInputs/FromTextInput';
import { FormDateInput } from './FormInputs/FormDateInput';
import { FormToggle } from './FormInputs/FormToggle';
import { FormDropdown } from './FormInputs/FormDropdown';
import { FormSlider } from './FormInputs/FormSlider';

import globalStyles, { SUSSOL_ORANGE, WHITE } from '../globalStyles';
import { modalStrings, generalStrings } from '../localization';
import { selectForm, selectCanSaveForm, selectCompletedForm } from '../selectors/form';
import { FormActions } from '../actions/FormActions';

/**
 * Component which will manage and control a set of user inputs of a form.
 * The configuration for the inputs comes from `formInputConfigs.js` where
 * the configs are defined for common inputs to create a form semi-declaritively.
 *
 * Uses the `Form` object within redux state to help manage state.
 *
 * @param {func}  onCancel        Callback passed from a parent to control the cancel button.
 * @param {func}  onUpdateForm    Takes the field/value and updates state within redux.
 * @param {func}  onCancelPressed Resets the form state in redux and calls the onClose callback.
 * @param {bool}  canSaveForm     Indicator that all fields are valid and required fields are filled
 * @param {func}  completedForm   Object containing key:value pairs of valid inputs from the form.
 * @param {func}  initialiseForm  Dispatcher to initialise the redux state with the correct config.
 * @param {func}  onSave          Callback to invoke on saving the form, passing back completedForm.
 * @param {bool}  isDisabled      Indicator whether this Form is disabled.
 * @param {Array} inputConfig     Configuration array of input config objects.
 *                                See { getFormInputConfig } from src/utilities/formInputConfigs.
 */
const FormControlComponent = ({
  // Form state
  form,
  completedForm,
  inputConfig,
  isDisabled,
  // Save button state
  canSaveForm,
  saveButtonText,
  // Cancel button state
  showCancelButton,
  cancelButtonText,
  // Form callbacks
  initialiseForm,
  onUpdateForm,
  // Save button callbacks
  onSave,
  // Cancel button callbacks
  onCancel,
}) => {
  const [refs, setRefs] = React.useState([]);

  React.useEffect(() => {
    initialiseForm(inputConfig);
    setRefs({ length: inputConfig.length });
  }, []);

  const nextFocus = (index, key) => value => {
    onUpdateForm(key, value);
    refs[index + 1]?.current?.focus?.();
  };

  const formInputs = () =>
    inputConfig.map(
      (
        {
          type,
          key,
          isRequired,
          validator,
          initialValue,
          label,
          invalidMessage,
          isEditable,
          ...rest
        },
        index
      ) => {
        refs[index] = React.useRef();
        switch (type) {
          case FORM_INPUT_TYPES.TEXT: {
            return (
              <FormTextInput
                ref={refs[index]}
                form={form}
                formKey={key}
                onSubmit={nextFocus(index, key)}
                key={key}
                value={initialValue}
                isRequired={isRequired}
                onValidate={validator}
                onChangeText={value => onUpdateForm(key, value)}
                label={label}
                invalidMessage={invalidMessage}
                isDisabled={!isEditable || isDisabled}
              />
            );
          }
          case FORM_INPUT_TYPES.DATE: {
            return (
              <FormDateInput
                ref={refs[index]}
                key={key}
                isRequired={isRequired}
                label={label}
                value={initialValue}
                onChangeDate={value => onUpdateForm(key, value)}
                onValidate={validator}
                invalidMessage={invalidMessage}
                onSubmit={nextFocus(index, key)}
                isDisabled={isDisabled}
              />
            );
          }
          case FORM_INPUT_TYPES.DROPDOWN: {
            const { options, optionKey } = rest;
            return (
              <FormDropdown
                key={key}
                isRequired={isRequired}
                label={label}
                value={completedForm?.[key] ?? initialValue}
                onValueChange={value => onUpdateForm(key, value)}
                options={options}
                optionKey={optionKey}
                isDisabled={!isEditable || isDisabled}
              />
            );
          }
          case FORM_INPUT_TYPES.TOGGLE: {
            const { options, optionLabels } = rest;
            return (
              <FormToggle
                options={options}
                optionLabels={optionLabels}
                value={completedForm?.[key] ?? initialValue}
                onValueChange={value => onUpdateForm(key, value)}
                key={key}
                label={label}
                isRequired={isRequired}
                isDisabled={!isEditable || isDisabled}
              />
            );
          }
          case FORM_INPUT_TYPES.SLIDER: {
            const { maximumValue, minimumValue, step } = rest;
            return (
              <FormSlider
                ref={refs[index]}
                maxmimumValue={maximumValue}
                minimumValue={minimumValue}
                step={step}
                value={completedForm?.[key] ?? initialValue}
                onValueChange={value => onUpdateForm(key, value)}
                key={key}
                label={label}
                isRequired={isRequired}
                isDisabled={isDisabled}
              />
            );
          }
          default:
            return null;
        }
      }
    );

  const onSaveCompletedForm = useCallback(() => onSave(completedForm), [onSave, completedForm]);
  const SaveButton = useCallback(
    () => (
      <PageButton
        onPress={onSaveCompletedForm}
        style={localStyles.saveButton}
        isDisabled={!canSaveForm || isDisabled}
        textStyle={localStyles.saveButtonTextStyle}
        text={saveButtonText}
      />
    ),
    [isDisabled, showCancelButton, canSaveForm, saveButtonText, onSaveCompletedForm]
  );
  const CancelButton = useCallback(
    () =>
      showCancelButton ? (
        <PageButton
          onPress={onCancel}
          style={localStyles.cancelButton}
          textStyle={localStyles.cancelButtonTextStyle}
          text={cancelButtonText}
        />
      ) : null,
    [showCancelButton, cancelButtonText, onCancel]
  );

  const Buttons = useCallback(
    () => (
      <View style={localStyles.buttonsRow}>
        <SaveButton />
        <CancelButton />
      </View>
    ),
    [SaveButton, CancelButton]
  );

  return (
    <View style={localStyles.flexOne}>
      <ScrollView style={localStyles.whiteBackground}>
        <View style={localStyles.flexRow}>
          <View style={localStyles.flexOne} />
          <View style={localStyles.flexTen}>{formInputs()}</View>
          <View style={localStyles.flexOne} />
        </View>
      </ScrollView>
      <Buttons />
    </View>
  );
};

const mapDispatchToProps = (dispatch, ownProps) => {
  const initialiseForm = config => dispatch(FormActions.initialiseForm(config));
  const onUpdateForm = (key, value) => dispatch(FormActions.updateForm(key, value));
  const onCancel = () => {
    dispatch(FormActions.resetForm());
    ownProps.onCancel();
  };
  return { initialiseForm, onUpdateForm, onCancel };
};

const mapStateToProps = (state, ownProps) => {
  const form = selectForm(state);
  const canSaveForm = selectCanSaveForm(state);
  const completedForm = selectCompletedForm(state);
  const { showCancelButton } = ownProps;
  const onCancel = showCancelButton ? ownProps.onCancel : () => null;
  return { form, canSaveForm, completedForm, onCancel, ...ownProps };
};

FormControlComponent.defaultProps = {
  form: {},
  completedForm: {},
  isDisabled: false,
  saveButtonText: generalStrings.save,
  showCancelButton: true,
  cancelButtonText: modalStrings.cancel,
};

FormControlComponent.propTypes = {
  form: PropTypes.object,
  completedForm: PropTypes.object,
  inputConfig: PropTypes.array.isRequired,
  isDisabled: PropTypes.bool,
  canSaveForm: PropTypes.bool.isRequired,
  saveButtonText: PropTypes.string,
  showCancelButton: PropTypes.bool,
  cancelButtonText: PropTypes.string,
  initialiseForm: PropTypes.func.isRequired,
  onUpdateForm: PropTypes.func.isRequired,
  onSave: PropTypes.func.isRequired,
  onCancel: PropTypes.func.isRequired,
};

export const FormControl = connect(mapStateToProps, mapDispatchToProps)(FormControlComponent);

const localStyles = StyleSheet.create({
  saveButton: {
    ...globalStyles.button,
    flex: 1,
    backgroundColor: SUSSOL_ORANGE,
    alignSelf: 'center',
  },
  saveButtonTextStyle: {
    ...globalStyles.buttonText,
    color: 'white',
    fontSize: 14,
  },
  cancelButton: {
    ...globalStyles.button,
    flex: 1,
    alignSelf: 'center',
  },
  cancelButtonTextStyle: {
    ...globalStyles.buttonText,
    color: SUSSOL_ORANGE,
    fontSize: 14,
  },
  flexOne: { flex: 1 },
  flexTen: { flex: 10 },
  flexRow: { flex: 1, flexDirection: 'row' },
  buttonsRow: { marginTop: 10, flexDirection: 'row-reverse' },
  whiteBackground: { backgroundColor: WHITE },
});
<|endoftext|>