<commit_before>import EmberObject, { get } from '@ember/object';
import { assign } from '@ember/polyfills';
import { Promise, resolve } from 'rsvp';
import { isEmpty } from '@ember/utils';
import { cancel, later } from '@ember/runloop';
import TokenAuthenticator from './token';
import config from 'ember-get-config';

/**
  JWT (JSON Web Token) Authenticator that supports automatic token refresh.

  Inspired by [ember-simple-auth-oauth2](https://github.com/simplabs/ember-simple-auth/tree/master/packages/ember-simple-auth-oauth2)

  The factory for this authenticator is registered as
  'authenticator:jwt` in Ember's container.

  @class JWT
  @namespace SimpleAuth.Authenticators
  @module ember-simple-auth-token/authenticators/jwt
  @extends TokenAuthenticator
*/
export default TokenAuthenticator.extend({
  /**
    @method init
    @private
  */
  init() {
    this._super(...arguments);
    const conf = config['ember-simple-auth-token'] || {};
    this.tokenDataPropertyName = conf.tokenDataPropertyName || 'tokenData';
    this.refreshAccessTokens = conf.refreshAccessTokens === false ? false : true;
    this.tokenExpirationInvalidateSession = conf.tokenExpirationInvalidateSession === false ? false : true;
    this.serverTokenRefreshEndpoint = conf.serverTokenRefreshEndpoint || '/api/token-refresh/';
    this.refreshTokenPropertyName = conf.refreshTokenPropertyName || 'refresh_token';
    this.tokenExpireName = conf.tokenExpireName || 'exp';
    this.refreshLeeway = conf.refreshLeeway || 0;
  },

  /**
    Restores the session from a set of session properties.

    It will return a resolving promise if one of two conditions is met:

    1) Both `data.token` and `data.expiresAt` are non-empty and `expiresAt`
       is greater than the calculated `now`.
    2) If `data.token` is non-empty and the decoded token has a key for
       `tokenExpireName`.

    If `refreshAccessTokens` is true, `scheduleAccessTokenRefresh` will
    be called and an automatic token refresh will be initiated.

    @method restore
    @param {Object} data The data to restore the session from
    @return {Promise} A promise that when it resolves results
                                 in the session being authenticated
  */
  restore(data) {
    const dataObject = EmberObject.create(data);

    return new Promise((resolve, reject) => {
      const now = this.getCurrentTime();
      const token = dataObject.get(this.tokenPropertyName);
      const refreshToken = dataObject.get(this.refreshTokenPropertyName);
      let expiresAt = dataObject.get(this.tokenExpireName);

      if (isEmpty(token)) {
        return reject(new Error('empty token'));
      }

      if (isEmpty(expiresAt)) {
        // Fetch the expire time from the token data since `expiresAt`
        // wasn't included in the data object that was passed in.
        const tokenData = this.getTokenData(token);
        expiresAt = tokenData[this.tokenExpireName];
        if (isEmpty(expiresAt)) {
          return resolve(data);
        }
      }

      if (expiresAt > now) {
        const wait = (expiresAt - now - this.refreshLeeway) * 1000;

        if (this.tokenExpirationInvalidateSession) {
          this.scheduleAccessTokenExpiration(expiresAt);
        }

        if (wait > 0) {
          if (this.refreshAccessTokens) {
            this.scheduleAccessTokenRefresh(dataObject.get(this.tokenExpireName), refreshToken);
          }
          return resolve(data);
        } else if (this.refreshAccessTokens) {
          return resolve(this.refreshAccessToken(refreshToken));
        } else {
          return reject(new Error('unable to refresh token'));
        }
      } else {
        // the refresh token might not be expired,
        // we can't test this on the client so attempt to refresh the token.
        // If the server rejects the token the user session will be invalidated
        if (this.refreshAccessTokens) {
          return resolve(this.refreshAccessToken(refreshToken));
        } else {
          return reject(new Error('token is expired'));
        }
      }
    });
  },

  /**
    Authenticates the session with the specified `credentials`.

    It will return a resolving promise if it successfully posts a request
    to the `JWT.serverTokenEndpoint` with the valid credentials.

    An automatic token refresh will be scheduled with the new expiration date
    from the returned refresh token. That expiration will be merged with the
    response and the promise resolved.

    @method authenticate
    @param {Object} credentials The credentials to authenticate the session with
    @param {Object} headers Optional headers to send with the authentication request
    @return {Promise} A promise that resolves when an auth token is
                                 successfully acquired from the server and rejects
                                 otherwise
  */
  authenticate(credentials, headers) {
    return this.makeRequest(this.serverTokenEndpoint, credentials, assign({}, this.headers, headers))
      .then(response => this.handleAuthResponse(response.json));
  },

  /**
    Schedules a token refresh request to be sent to the backend after a calculated
    `wait` time has passed.

    If both `token` and `expiresAt` are non-empty, and `expiresAt` minus the optional
    refres leeway is greater than the calculated `now`, the token refresh will be scheduled
    through later.

    @method scheduleAccessTokenRefresh
    @private
  */
  scheduleAccessTokenRefresh(expiresAt, refreshToken) {
    if (this.refreshAccessTokens) {

      const now = this.getCurrentTime();
      const wait = (expiresAt - now - this.refreshLeeway) * 1000;

      if (!isEmpty(refreshToken) && !isEmpty(expiresAt)) {
        if (wait > 0) {
          cancel(this._refreshTokenTimeout);
          delete this._refreshTokenTimeout;
          this._refreshTokenTimeout = later(this, this.refreshAccessToken, refreshToken, wait);
        } else if (expiresAt > now) {
          throw new Error('refreshLeeway is too large which is preventing token refresh');
        }
      }
    }
  },

  /**
    Makes a refresh token request to grab a new authenticated JWT token from the server.

    It will return a resolving promise if a successful POST is made to the
    `JWT.serverTokenRefreshEndpoint`.

    After the new token is obtained it will schedule the next automatic token refresh
    based on the new `expiresAt` time.

    The session will be updated via the trigger `sessionDataUpdated`.

    @method refreshAccessToken
    @private
  */
  refreshAccessToken(token) {
    const data = this.makeRefreshData(token);

    return this.makeRequest(this.serverTokenRefreshEndpoint, data, this.headers)
      .then(response => {
        const sessionData = this.handleAuthResponse(response.json);
        this.trigger('sessionDataUpdated', sessionData);
        return sessionData;
      })
      .catch(error => {
        this.handleTokenRefreshFail(error.status);
        return Promise.reject(error);
      });
  },

  /**
    Returns a nested object with the token property name.
    Example:  If `refreshTokenPropertyName` is "data.user.refreshToken", `makeRefreshData` will return {data: {user: {refreshToken: "token goes here"}}}

    @method makeRefreshData
    @return {object} An object with the nested property name.
  */
  makeRefreshData(refreshToken) {
    const data = {};
    const nestings = this.refreshTokenPropertyName.split('.');
    const refreshTokenPropertyName = nestings.pop();
    let lastObject = data;

    nestings.forEach(nesting => {
      lastObject[nesting] = {};
      lastObject = lastObject[nesting];
    });

    lastObject[refreshTokenPropertyName] = refreshToken;

    return data;
  },

  /**
    Returns the decoded token with accessible returned values.

    @method getTokenData
    @return {object} An object with properties for the session.
  */
  getTokenData(token) {
    const payload = token.split('.')[1];
    const decodedPayload = window.base64.decode(payload.replace(/-/g, '+').replace(/_/g, '/'));
    const tokenData = decodeURIComponent(window.escape(decodedPayload));

    try {
      return JSON.parse(tokenData);
    } catch (error) {
      return tokenData;
    }
  },

  /**
    Cancels any outstanding automatic token refreshes and returns a resolving
    promise.
    @method invalidate
    @param {Object} data The data of the session to be invalidated
    @return {Promise} A resolving promise
  */
  invalidate() {
    cancel(this._refreshTokenTimeout);
    delete this._refreshTokenTimeout;
    cancel(this._tokenExpirationTimeout);
    delete this._tokenExpirationTimeout;
    return resolve();
  },

  /**
    Returns the current time as a timestamp in seconds
    @method getCurrentTime
    @return {Integer} timestamp
  */
  getCurrentTime() {
    return Math.floor((new Date()).getTime() / 1000);
  },

  /**
    Handles authentication response from server, and returns session data

    @method handleAuthResponse
    @private
   */
  handleAuthResponse(response) {
    const token = get(response, this.tokenPropertyName);

    if (isEmpty(token)) {
      throw new Error('Token is empty. Please check your backend response.');
    }

    const tokenData = this.getTokenData(token);
    const expiresAt = get(tokenData, this.tokenExpireName);
    const tokenExpireData = {};

    tokenExpireData[this.tokenExpireName] = expiresAt;

    if (this.tokenExpirationInvalidateSession) {
      this.scheduleAccessTokenExpiration(expiresAt);
    }

    if (this.refreshAccessTokens) {
      const refreshToken = get(response, this.refreshTokenPropertyName);

      if (isEmpty(refreshToken)) {
        throw new Error('Refresh token is empty. Please check your backend response.');
      }

      this.scheduleAccessTokenRefresh(expiresAt, refreshToken);
    }

    return assign(response, tokenExpireData, {tokenData: tokenData});
  },

  /**
    Handles token refresh fail status. If the server response to a token refresh has a
    status of 401 or 403 then the token in the session will be invalidated and
    the sessionInvalidated provided by ember-simple-auth will be triggered.

    @method handleTokenRefreshFail
  */

  handleTokenRefreshFail(refreshStatus) {
    if (refreshStatus === 401 || refreshStatus === 403) {
      return this.invalidate().then(() => {
        this.trigger('sessionDataInvalidated');
      });
    }
  },

  /**
    Schedules session invalidation at the time token expires.

    @method scheduleAccessTokenExpiration
    @private
  */
  scheduleAccessTokenExpiration(expiresAt) {
    const now = this.getCurrentTime();
    const wait = Math.max((expiresAt - now) * 1000, 0);

    if (!isEmpty(expiresAt)) {
      cancel(this._tokenExpirationTimeout);
      delete this._tokenExpirationTimeout;
      this._tokenExpirationTimeout = later(this, this.handleAccessTokenExpiration, wait);
    }
  },

  /**
    Handles access token expiration

    @method handleAccessTokenExpiration
    @private
  */
  handleAccessTokenExpiration() {
    return this.invalidate().then(() => {
      this.trigger('sessionDataInvalidated');
    });
  }
});
<commit_msg>Use atob or Buffer for decoding base64 strings<commit_after>/* global Buffer */

import EmberObject, { get } from '@ember/object';
import { assign } from '@ember/polyfills';
import { Promise, resolve } from 'rsvp';
import { isEmpty } from '@ember/utils';
import { cancel, later } from '@ember/runloop';
import TokenAuthenticator from './token';
import config from 'ember-get-config';

const decode = str => {
  console.log(str);
  return atob ? atob(str) : Buffer.from(str, 'base64').toString('utf-8');
};

/**
  JWT (JSON Web Token) Authenticator that supports automatic token refresh.

  Inspired by [ember-simple-auth-oauth2](https://github.com/simplabs/ember-simple-auth/tree/master/packages/ember-simple-auth-oauth2)

  The factory for this authenticator is registered as
  'authenticator:jwt` in Ember's container.

  @class JWT
  @namespace SimpleAuth.Authenticators
  @module ember-simple-auth-token/authenticators/jwt
  @extends TokenAuthenticator
*/
export default TokenAuthenticator.extend({
  /**
    @method init
    @private
  */
  init() {
    this._super(...arguments);
    const conf = config['ember-simple-auth-token'] || {};
    this.tokenDataPropertyName = conf.tokenDataPropertyName || 'tokenData';
    this.refreshAccessTokens = conf.refreshAccessTokens === false ? false : true;
    this.tokenExpirationInvalidateSession = conf.tokenExpirationInvalidateSession === false ? false : true;
    this.serverTokenRefreshEndpoint = conf.serverTokenRefreshEndpoint || '/api/token-refresh/';
    this.refreshTokenPropertyName = conf.refreshTokenPropertyName || 'refresh_token';
    this.tokenExpireName = conf.tokenExpireName || 'exp';
    this.refreshLeeway = conf.refreshLeeway || 0;
  },

  /**
    Restores the session from a set of session properties.

    It will return a resolving promise if one of two conditions is met:

    1) Both `data.token` and `data.expiresAt` are non-empty and `expiresAt`
       is greater than the calculated `now`.
    2) If `data.token` is non-empty and the decoded token has a key for
       `tokenExpireName`.

    If `refreshAccessTokens` is true, `scheduleAccessTokenRefresh` will
    be called and an automatic token refresh will be initiated.

    @method restore
    @param {Object} data The data to restore the session from
    @return {Promise} A promise that when it resolves results
                                 in the session being authenticated
  */
  restore(data) {
    const dataObject = EmberObject.create(data);

    return new Promise((resolve, reject) => {
      const now = this.getCurrentTime();
      const token = dataObject.get(this.tokenPropertyName);
      const refreshToken = dataObject.get(this.refreshTokenPropertyName);
      let expiresAt = dataObject.get(this.tokenExpireName);

      if (isEmpty(token)) {
        return reject(new Error('empty token'));
      }

      if (isEmpty(expiresAt)) {
        // Fetch the expire time from the token data since `expiresAt`
        // wasn't included in the data object that was passed in.
        const tokenData = this.getTokenData(token);
        expiresAt = tokenData[this.tokenExpireName];
        if (isEmpty(expiresAt)) {
          return resolve(data);
        }
      }

      if (expiresAt > now) {
        const wait = (expiresAt - now - this.refreshLeeway) * 1000;

        if (this.tokenExpirationInvalidateSession) {
          this.scheduleAccessTokenExpiration(expiresAt);
        }

        if (wait > 0) {
          if (this.refreshAccessTokens) {
            this.scheduleAccessTokenRefresh(dataObject.get(this.tokenExpireName), refreshToken);
          }
          return resolve(data);
        } else if (this.refreshAccessTokens) {
          return resolve(this.refreshAccessToken(refreshToken));
        } else {
          return reject(new Error('unable to refresh token'));
        }
      } else {
        // the refresh token might not be expired,
        // we can't test this on the client so attempt to refresh the token.
        // If the server rejects the token the user session will be invalidated
        if (this.refreshAccessTokens) {
          return resolve(this.refreshAccessToken(refreshToken));
        } else {
          return reject(new Error('token is expired'));
        }
      }
    });
  },

  /**
    Authenticates the session with the specified `credentials`.

    It will return a resolving promise if it successfully posts a request
    to the `JWT.serverTokenEndpoint` with the valid credentials.

    An automatic token refresh will be scheduled with the new expiration date
    from the returned refresh token. That expiration will be merged with the
    response and the promise resolved.

    @method authenticate
    @param {Object} credentials The credentials to authenticate the session with
    @param {Object} headers Optional headers to send with the authentication request
    @return {Promise} A promise that resolves when an auth token is
                                 successfully acquired from the server and rejects
                                 otherwise
  */
  authenticate(credentials, headers) {
    return this.makeRequest(this.serverTokenEndpoint, credentials, assign({}, this.headers, headers))
      .then(response => this.handleAuthResponse(response.json));
  },

  /**
    Schedules a token refresh request to be sent to the backend after a calculated
    `wait` time has passed.

    If both `token` and `expiresAt` are non-empty, and `expiresAt` minus the optional
    refres leeway is greater than the calculated `now`, the token refresh will be scheduled
    through later.

    @method scheduleAccessTokenRefresh
    @private
  */
  scheduleAccessTokenRefresh(expiresAt, refreshToken) {
    if (this.refreshAccessTokens) {

      const now = this.getCurrentTime();
      const wait = (expiresAt - now - this.refreshLeeway) * 1000;

      if (!isEmpty(refreshToken) && !isEmpty(expiresAt)) {
        if (wait > 0) {
          cancel(this._refreshTokenTimeout);
          delete this._refreshTokenTimeout;
          this._refreshTokenTimeout = later(this, this.refreshAccessToken, refreshToken, wait);
        } else if (expiresAt > now) {
          throw new Error('refreshLeeway is too large which is preventing token refresh');
        }
      }
    }
  },

  /**
    Makes a refresh token request to grab a new authenticated JWT token from the server.

    It will return a resolving promise if a successful POST is made to the
    `JWT.serverTokenRefreshEndpoint`.

    After the new token is obtained it will schedule the next automatic token refresh
    based on the new `expiresAt` time.

    The session will be updated via the trigger `sessionDataUpdated`.

    @method refreshAccessToken
    @private
  */
  refreshAccessToken(token) {
    const data = this.makeRefreshData(token);

    return this.makeRequest(this.serverTokenRefreshEndpoint, data, this.headers)
      .then(response => {
        const sessionData = this.handleAuthResponse(response.json);
        this.trigger('sessionDataUpdated', sessionData);
        return sessionData;
      })
      .catch(error => {
        this.handleTokenRefreshFail(error.status);
        return Promise.reject(error);
      });
  },

  /**
    Returns a nested object with the token property name.
    Example:  If `refreshTokenPropertyName` is "data.user.refreshToken", `makeRefreshData` will return {data: {user: {refreshToken: "token goes here"}}}

    @method makeRefreshData
    @return {object} An object with the nested property name.
  */
  makeRefreshData(refreshToken) {
    const data = {};
    const nestings = this.refreshTokenPropertyName.split('.');
    const refreshTokenPropertyName = nestings.pop();
    let lastObject = data;

    nestings.forEach(nesting => {
      lastObject[nesting] = {};
      lastObject = lastObject[nesting];
    });

    lastObject[refreshTokenPropertyName] = refreshToken;

    return data;
  },

  /**
    Returns the decoded token with accessible returned values.

    @method getTokenData
    @return {object} An object with properties for the session.
  */
  getTokenData(token) {
    const payload = token.split('.')[1];
    const decodedPayload = decode(payload.replace(/-/g, '+').replace(/_/g, '/'));
    const tokenData = decodeURIComponent(window.escape(decodedPayload));

    try {
      return JSON.parse(tokenData);
    } catch (error) {
      return tokenData;
    }
  },

  /**
    Cancels any outstanding automatic token refreshes and returns a resolving
    promise.
    @method invalidate
    @param {Object} data The data of the session to be invalidated
    @return {Promise} A resolving promise
  */
  invalidate() {
    cancel(this._refreshTokenTimeout);
    delete this._refreshTokenTimeout;
    cancel(this._tokenExpirationTimeout);
    delete this._tokenExpirationTimeout;
    return resolve();
  },

  /**
    Returns the current time as a timestamp in seconds
    @method getCurrentTime
    @return {Integer} timestamp
  */
  getCurrentTime() {
    return Math.floor((new Date()).getTime() / 1000);
  },

  /**
    Handles authentication response from server, and returns session data

    @method handleAuthResponse
    @private
   */
  handleAuthResponse(response) {
    const token = get(response, this.tokenPropertyName);

    if (isEmpty(token)) {
      throw new Error('Token is empty. Please check your backend response.');
    }

    const tokenData = this.getTokenData(token);
    const expiresAt = get(tokenData, this.tokenExpireName);
    const tokenExpireData = {};

    tokenExpireData[this.tokenExpireName] = expiresAt;

    if (this.tokenExpirationInvalidateSession) {
      this.scheduleAccessTokenExpiration(expiresAt);
    }

    if (this.refreshAccessTokens) {
      const refreshToken = get(response, this.refreshTokenPropertyName);

      if (isEmpty(refreshToken)) {
        throw new Error('Refresh token is empty. Please check your backend response.');
      }

      this.scheduleAccessTokenRefresh(expiresAt, refreshToken);
    }

    return assign(response, tokenExpireData, {tokenData: tokenData});
  },

  /**
    Handles token refresh fail status. If the server response to a token refresh has a
    status of 401 or 403 then the token in the session will be invalidated and
    the sessionInvalidated provided by ember-simple-auth will be triggered.

    @method handleTokenRefreshFail
  */

  handleTokenRefreshFail(refreshStatus) {
    if (refreshStatus === 401 || refreshStatus === 403) {
      return this.invalidate().then(() => {
        this.trigger('sessionDataInvalidated');
      });
    }
  },

  /**
    Schedules session invalidation at the time token expires.

    @method scheduleAccessTokenExpiration
    @private
  */
  scheduleAccessTokenExpiration(expiresAt) {
    const now = this.getCurrentTime();
    const wait = Math.max((expiresAt - now) * 1000, 0);

    if (!isEmpty(expiresAt)) {
      cancel(this._tokenExpirationTimeout);
      delete this._tokenExpirationTimeout;
      this._tokenExpirationTimeout = later(this, this.handleAccessTokenExpiration, wait);
    }
  },

  /**
    Handles access token expiration

    @method handleAccessTokenExpiration
    @private
  */
  handleAccessTokenExpiration() {
    return this.invalidate().then(() => {
      this.trigger('sessionDataInvalidated');
    });
  }
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import layout from '../templates/components/bm-menu';
import computedStyleFor from 'ember-burger-menu/computed/style-for';

const {
  computed,
  observer,
  A: emberArray,
  inject: { service }
} = Ember;

export const OUTLET_MENU_ANIMATIONS = [
  'push',
  'rotate-out'
];

export default Ember.Component.extend({
  layout,
  state: service('burgerMenu'),

  itemTagName: 'div',
  dismissOnItemClick: false,

  style: computedStyleFor('menu').readOnly(),

  renderInPlace: computed('state.animation', function() {
    return OUTLET_MENU_ANIMATIONS.indexOf(this.get('state.animation')) === -1;
  }).readOnly(),

  menuItems: computed(function() {
    return emberArray([]);
  }).readOnly(),

  onOpenChange: observer('state.open', function() {
    let actionName = this.get('state.open') ? 'onOpen' : 'onClose';
    this.sendAction(actionName);
  })
});
<commit_msg>Remove unneeded property<commit_after>import Ember from 'ember';
import layout from '../templates/components/bm-menu';
import computedStyleFor from 'ember-burger-menu/computed/style-for';

const {
  computed,
  observer,
  A: emberArray,
  inject: { service }
} = Ember;

export const OUTLET_MENU_ANIMATIONS = [
  'push',
  'rotate-out'
];

export default Ember.Component.extend({
  layout,
  state: service('burgerMenu'),

  itemTagName: 'div',
  dismissOnItemClick: false,

  style: computedStyleFor('menu').readOnly(),

  renderInPlace: computed('state.animation', function() {
    return OUTLET_MENU_ANIMATIONS.indexOf(this.get('state.animation')) === -1;
  }).readOnly(),

  menuItems: computed(function() {
    return emberArray([]);
  }).readOnly(),

  onOpenChange: observer('state.open', function() {
    this.sendAction(this.get('state.open') ? 'onOpen' : 'onClose');
  })
});
<|endoftext|>


<commit_before><commit_msg>compacted JS<commit_after><|endoftext|>


<commit_before>import AbilityTracker from 'parser/shared/modules/AbilityTracker';
import HealingDone from 'parser/shared/modules/HealingDone';
import DamageDone from 'parser/shared/modules/DamageDone';
import CastEfficiency from 'parser/shared/modules/CastEfficiency';
import CoreAbilities from 'parser/shared/modules/Abilities';
import Analyzer from 'parser/core/Analyzer';
import SPELLS from 'common/SPELLS/index';
import ManaTracker from './ManaTracker';

class HealingEfficiencyTracker extends Analyzer {
  static dependencies = {
    manaTracker: ManaTracker,
    abilityTracker: AbilityTracker,
    healingDone: HealingDone,
    damageDone: DamageDone,
    castEfficiency: CastEfficiency,
    abilities: CoreAbilities,
  };

  getSpellStats(spellId, healingSpellIds = null) {
    let spellInfo = {};
    const ability = this.abilityTracker.getAbility(spellId);

    spellInfo.spell = SPELLS[spellId];
    spellInfo.casts = ability.casts || 0;

    spellInfo.healingHits = ability.healingHits || 0;
    spellInfo.healingDone = ability.healingEffective || 0;
    spellInfo.overhealingDone = ability.healingOverheal || 0;
    spellInfo.healingAbsorbed = ability.healingAbsorbed || 0;

    if (healingSpellIds) {
      for (const healingSpellId in healingSpellIds) {
        const healingAbility = this.abilityTracker.getAbility(healingSpellIds[healingSpellId]);

        spellInfo.healingHits += healingAbility.healingHits || 0;
        spellInfo.healingDone += healingAbility.healingEffective || 0;
        spellInfo.overhealingDone += healingAbility.healingOverheal || 0;
        spellInfo.healingAbsorbed += healingAbility.healingAbsorbed || 0;
      }
    }

    spellInfo.damageHits = ability.damageHits || 0;
    spellInfo.damageDone = ability.damageEffective || 0;
    spellInfo.damageAbsorbed = ability.damageAbsorbed || 0;

    spellInfo.manaSpent = this.manaTracker.spendersObj[spellId] ? this.manaTracker.spendersObj[spellId].spent : 0;
    spellInfo.manaPercentSpent = spellInfo.manaSpent / this.manaTracker.spent;
    spellInfo.manaGained = this.manaTracker;

    // All of the following information can be derived from the data in SpellInfo.
    // Now we can add custom logic for spells.
    spellInfo = this.getCustomSpellStats(spellInfo, spellId);

    spellInfo.percentOverhealingDone = spellInfo.overhealingDone / (spellInfo.healingDone + spellInfo.healingAbsorbed) || 0;
    spellInfo.percentHealingDone = spellInfo.healingDone / this.healingDone.total.regular || 0;
    spellInfo.percentDamageDone = spellInfo.damageDone / this.damageDone.total.regular || 0;

    spellInfo.hpm = (spellInfo.healingDone / spellInfo.manaSpent) | 0;
    spellInfo.dpm = (spellInfo.damageDone / spellInfo.manaSpent) | 0;

    spellInfo.timeSpentCasting = this.castEfficiency.getTimeSpentCasting(spellId).timeSpentCasting + this.castEfficiency.getTimeSpentCasting(spellId).gcdSpent;
    spellInfo.percentTimeSpentCasting = spellInfo.timeSpentCasting / this.owner.fightDuration;

    spellInfo.hpet = (spellInfo.healingDone / spellInfo.timeSpentCasting) | 0;
    spellInfo.dpet = (spellInfo.damageDone / spellInfo.timeSpentCasting) | 0;

    return spellInfo;
  }

  getCustomSpellStats(spellInfo, spellId) {
    // Overwrite this function to add specific logic for spells.
    return spellInfo;
  }

  getAllSpellStats(includeCooldowns = false) {
    const spells = {};
    let topHpm = 0;
    let topDpm = 0;
    let topHpet = 0;
    let topDpet = 0;

    for (const index in this.abilities.abilities) {
      const ability = this.abilities.abilities[index];

      if (ability.spell && ability.spell.manaCost && ability.spell.manaCost > 0) {
        if (includeCooldowns || ability.category !== 'Cooldown') {
          spells[ability.spell.id] = this.getSpellStats(ability.spell.id, ability.healSpellIds);

          topHpm = Math.max(topHpm, spells[ability.spell.id].hpm);
          topDpm = Math.max(topDpm, spells[ability.spell.id].dpm);
          topHpet = Math.max(topHpet, spells[ability.spell.id].hpet);
          topDpet = Math.max(topDpet, spells[ability.spell.id].dpet);
        }
      }
    }

    return {
      spells,
      topHpm,
      topDpm,
      topHpet,
      topDpet,
    };
  }
}

export default HealingEfficiencyTracker;
<commit_msg>fix bug with mana percentages for custom modified spells<commit_after>import AbilityTracker from 'parser/shared/modules/AbilityTracker';
import HealingDone from 'parser/shared/modules/HealingDone';
import DamageDone from 'parser/shared/modules/DamageDone';
import CastEfficiency from 'parser/shared/modules/CastEfficiency';
import CoreAbilities from 'parser/shared/modules/Abilities';
import Analyzer from 'parser/core/Analyzer';
import SPELLS from 'common/SPELLS/index';
import ManaTracker from './ManaTracker';

class HealingEfficiencyTracker extends Analyzer {
  static dependencies = {
    manaTracker: ManaTracker,
    abilityTracker: AbilityTracker,
    healingDone: HealingDone,
    damageDone: DamageDone,
    castEfficiency: CastEfficiency,
    abilities: CoreAbilities,
  };

  getSpellStats(spellId, healingSpellIds = null) {
    let spellInfo = {};
    const ability = this.abilityTracker.getAbility(spellId);

    spellInfo.spell = SPELLS[spellId];
    spellInfo.casts = ability.casts || 0;

    spellInfo.healingHits = ability.healingHits || 0;
    spellInfo.healingDone = ability.healingEffective || 0;
    spellInfo.overhealingDone = ability.healingOverheal || 0;
    spellInfo.healingAbsorbed = ability.healingAbsorbed || 0;

    if (healingSpellIds) {
      for (const healingSpellId in healingSpellIds) {
        const healingAbility = this.abilityTracker.getAbility(healingSpellIds[healingSpellId]);

        spellInfo.healingHits += healingAbility.healingHits || 0;
        spellInfo.healingDone += healingAbility.healingEffective || 0;
        spellInfo.overhealingDone += healingAbility.healingOverheal || 0;
        spellInfo.healingAbsorbed += healingAbility.healingAbsorbed || 0;
      }
    }

    spellInfo.damageHits = ability.damageHits || 0;
    spellInfo.damageDone = ability.damageEffective || 0;
    spellInfo.damageAbsorbed = ability.damageAbsorbed || 0;

    spellInfo.manaSpent = this.manaTracker.spendersObj[spellId] ? this.manaTracker.spendersObj[spellId].spent : 0;
    spellInfo.manaGained = this.manaTracker;

    // All of the following information can be derived from the data in SpellInfo.
    // Now we can add custom logic for spells.
    spellInfo = this.getCustomSpellStats(spellInfo, spellId);

    spellInfo.percentOverhealingDone = spellInfo.overhealingDone / (spellInfo.healingDone + spellInfo.healingAbsorbed) || 0;
    spellInfo.percentHealingDone = spellInfo.healingDone / this.healingDone.total.regular || 0;
    spellInfo.percentDamageDone = spellInfo.damageDone / this.damageDone.total.regular || 0;

    spellInfo.manaPercentSpent = spellInfo.manaSpent / this.manaTracker.spent;

    spellInfo.hpm = (spellInfo.healingDone / spellInfo.manaSpent) | 0;
    spellInfo.dpm = (spellInfo.damageDone / spellInfo.manaSpent) | 0;

    spellInfo.timeSpentCasting = this.castEfficiency.getTimeSpentCasting(spellId).timeSpentCasting + this.castEfficiency.getTimeSpentCasting(spellId).gcdSpent;
    spellInfo.percentTimeSpentCasting = spellInfo.timeSpentCasting / this.owner.fightDuration;

    spellInfo.hpet = (spellInfo.healingDone / spellInfo.timeSpentCasting) | 0;
    spellInfo.dpet = (spellInfo.damageDone / spellInfo.timeSpentCasting) | 0;

    return spellInfo;
  }

  getCustomSpellStats(spellInfo, spellId) {
    // Overwrite this function to add specific logic for spells.
    return spellInfo;
  }

  getAllSpellStats(includeCooldowns = false) {
    const spells = {};
    let topHpm = 0;
    let topDpm = 0;
    let topHpet = 0;
    let topDpet = 0;

    for (const index in this.abilities.abilities) {
      const ability = this.abilities.abilities[index];

      if (ability.spell && ability.spell.manaCost && ability.spell.manaCost > 0) {
        if (includeCooldowns || ability.category !== 'Cooldown') {
          spells[ability.spell.id] = this.getSpellStats(ability.spell.id, ability.healSpellIds);

          topHpm = Math.max(topHpm, spells[ability.spell.id].hpm);
          topDpm = Math.max(topDpm, spells[ability.spell.id].dpm);
          topHpet = Math.max(topHpet, spells[ability.spell.id].hpet);
          topDpet = Math.max(topDpet, spells[ability.spell.id].dpet);
        }
      }
    }

    return {
      spells,
      topHpm,
      topDpm,
      topHpet,
      topDpet,
    };
  }
}

export default HealingEfficiencyTracker;
<|endoftext|>


<commit_before>const React = require('react');
const { ScrollView, Text, Image } = require('react-native');

const gStyles = require('styles'); // global styles
const lStyles = require('./styles'); // local styles

class HomeView extends React.Component {

    static propTypes = {
        navigation: React.PropTypes.object.isRequired
    };

    componentWillReceiveProps({ style }) {

        // Compose passed-in style
        Styles = gStyles.compose(style || {}, lStyles);
    }

    render() {

        const { navigation, styles } = this.props;

        // Styles = Styles.compose(style, );

        return (
            <ScrollView style={{ padding: 128 }}>

                <Text onPress={() => {

                    navigation.navigate('Home');
                }}>Home</Text>
                <Text onPress={() => {

                    navigation.navigate('Counter');
                }}>Counter</Text>

                <Text style={styles.title}>Welcome!</Text>
                <Image
                    style={styles.duck}
                    source={require('../assets/duck.jpg')}
                />
            </ScrollView>
        );
    }
}

module.exports = gStyles.addStyleHelpers(HomeView, lStyles);
<commit_msg>Remove unnecessary hooks for styles<commit_after>const React = require('react');
const { ScrollView, Text, Image } = require('react-native');

const gStyles = require('styles'); // global styles
const lStyles = require('./styles'); // local styles

class HomeView extends React.Component {

    static propTypes = {
        navigation: React.PropTypes.object.isRequired
    };

    render() {

        const { navigation, styles } = this.props;

        return (
            <ScrollView style={{ padding: 128 }}>

                <Text onPress={() => {

                    navigation.navigate('Home');
                }}>Home</Text>
                <Text onPress={() => {

                    navigation.navigate('Counter');
                }}>Counter</Text>

                <Text style={styles.title}>Welcome!</Text>
                <Image
                    style={styles.duck}
                    source={require('../assets/duck.jpg')}
                />
            </ScrollView>
        );
    }
}

module.exports = gStyles.addStyleHelpers(HomeView, lStyles);
<|endoftext|>


<commit_before>import Ember from 'ember';
import FormGroupComponent from './em-form-group';
import ControlMixin from 'ember-rapid-forms/mixins/control';

/*
Form Text Area

Syntax:
{{em-text property="property name" rows=4 cols=40}}
 */
export default FormGroupComponent.extend({
  controlView: Ember.TextArea.extend(ControlMixin, {
    attributeBindings: ['placeholder', 'required', 'autofocus', 'disabled'],
    placeholder: Ember.computed.alias('mainComponent.placeholder'),
    model: Ember.computed.alias('mainComponent.model'),
    propertyName: Ember.computed.alias('mainComponent.propertyName'),
    rows: Ember.computed.alias('mainComponent.rows'),
    cols: Ember.computed.alias('mainComponent.cols'),
    disabled: Ember.computed.alias('mainComponent.disabled')
  }),
  property: null,
  label: null,
  placeholder: null,
  rows: null,
  cols: null,
  disabled: null,
  controlWrapper: Ember.computed('form.formLayout', function() {
    if (this.get('form.formLayout') === 'horizontal') {
      return 'col-sm-10';
    }
    return null;
  })
});
<commit_msg>Removed unnecessary line<commit_after>import Ember from 'ember';
import FormGroupComponent from './em-form-group';
import ControlMixin from 'ember-rapid-forms/mixins/control';

/*
Form Text Area

Syntax:
{{em-text property="property name" rows=4 cols=40}}
 */
export default FormGroupComponent.extend({
  controlView: Ember.TextArea.extend(ControlMixin, {
    placeholder: Ember.computed.alias('mainComponent.placeholder'),
    model: Ember.computed.alias('mainComponent.model'),
    propertyName: Ember.computed.alias('mainComponent.propertyName'),
    rows: Ember.computed.alias('mainComponent.rows'),
    cols: Ember.computed.alias('mainComponent.cols'),
    disabled: Ember.computed.alias('mainComponent.disabled')
  }),
  property: null,
  label: null,
  placeholder: null,
  rows: null,
  cols: null,
  disabled: null,
  controlWrapper: Ember.computed('form.formLayout', function() {
    if (this.get('form.formLayout') === 'horizontal') {
      return 'col-sm-10';
    }
    return null;
  })
});
<|endoftext|>


<commit_before>import gulp from 'gulp';
import aureliaTemplateLint from 'gulp-aurelia-template-lint';
import eslint from 'gulp-eslint';
import gulpStyleLint from 'gulp-stylelint';

// We rely on a global boolean and process.exit to exit the task in error
// in case of lint errors. We need to do this because lint tasks either exit
// with a 0 status code or they display a stacktrace in case of lint errors.
let foundLintErrors = false;

const main = gulp.series(
    jsLint,
    styleLint,
    templateLint,
    failOnErrors
);

function jsLint() {
    return gulp.src(['app/**/*.js', 'test/**/*.js'])
        .pipe(eslint())
        .pipe(eslint.format())
        .pipe(eslint.result(result => {
            if (result.errorCount > 0) {
                foundLintErrors = true;
            }
        }));
}

function styleLint() {
    return gulp.src('app/**/*.scss')
        .pipe(gulpStyleLint({
            reporters: [
                { formatter: 'string', console: true },
                { formatter: lintReport => {
                    if (lintReport.some(fileReport => fileReport.errored)) {
                        foundLintErrors = true;
                    }
                }},
            ],
            failAfterError: false,
        }));
}

function templateLint() {
    function mustIgnoreError(error, file) {
        if (file === '/app/game/play/widgets/trump/trump.html' &&
                error.message.startsWith('duplicated id:')) {
            return true;
        }

        return false;
    }

    return gulp.src('./app/**/*.html')
        .pipe(aureliaTemplateLint(null, (error, file) => {
            if (!mustIgnoreError(error, file)) {
                foundLintErrors = true;
                console.log(`[Template lint] ${error.message} [file: ${file} ln: ${error.line} col: ${error.column}]`);
                if (error.detail) console.log(`  * ${error.detail}`);
            }
        }));
}

function failOnErrors() {
    if (foundLintErrors) {
        process.exit(1);
    }
}

export { main as default };
<commit_msg>fix(aurelia): can run template lint on Windows<commit_after>import gulp from 'gulp';
import aureliaTemplateLint from 'gulp-aurelia-template-lint';
import eslint from 'gulp-eslint';
import gulpStyleLint from 'gulp-stylelint';
import path from 'path';

// We rely on a global boolean and process.exit to exit the task in error
// in case of lint errors. We need to do this because lint tasks either exit
// with a 0 status code or they display a stacktrace in case of lint errors.
let foundLintErrors = false;

const main = gulp.series(
    jsLint,
    styleLint,
    templateLint,
    failOnErrors
);

function jsLint() {
    return gulp.src(['app/**/*.js', 'test/**/*.js'])
        .pipe(eslint())
        .pipe(eslint.format())
        .pipe(eslint.result(result => {
            if (result.errorCount > 0) {
                foundLintErrors = true;
            }
        }));
}

function styleLint() {
    return gulp.src('app/**/*.scss')
        .pipe(gulpStyleLint({
            reporters: [
                { formatter: 'string', console: true },
                { formatter: lintReport => {
                    if (lintReport.some(fileReport => fileReport.errored)) {
                        foundLintErrors = true;
                    }
                }},
            ],
            failAfterError: false,
        }));
}

function templateLint() {
    function mustIgnoreError(error, file) {
        if (file.endsWith(path.join('app', 'game', 'play', 'widgets', 'trump', 'trump.html')) &&
                error.message.startsWith('duplicated id:')) {
            return true;
        }

        return false;
    }

    return gulp.src('./app/**/*.html')
        .pipe(aureliaTemplateLint(null, (error, file) => {
            if (!mustIgnoreError(error, file)) {
                foundLintErrors = true;
                console.log(`[Template lint] ${error.message} [file: ${file} ln: ${error.line} col: ${error.column}]`);
                if (error.detail) console.log(`  * ${error.detail}`);
            }
        }));
}

function failOnErrors() {
    if (foundLintErrors) {
        process.exit(1);
    }
}

export { main as default };
<|endoftext|>


<commit_before>import Ember from 'ember';
import layout from '../templates/components/fd-tabs';
import TabPane from '../components/fd-tabs/pane';
import FdWorkPanelToggler from '../mixins/fd-work-panel-toggler';

/**
 Tab component for dynamic tab functionality
 ### Usage
 Just nest any number of yielded [Components.TabPane](Components.TabPane.html) components that hold the tab content.
 The tab navigation is automatically generated from the tab panes' `title` property:
 ```hbs
 {{#fd-tabs as |tab|}}
   {{#tab.pane dataTab='tab1' title="Tab 1"}}
     <p>...</p>
   {{/tab.pane}}
   {{#tab.pane dataTab='tab2' title="Tab 2"}}
     <p>...</p>
   {{/tab.pane}}
 {{/fd-tabs}}
 ```
 @class Tab
 @namespace Components
 @extends Ember.Component
 @uses Mixins.ComponentParent
 @public
 */
export default Ember.Component.extend(FdWorkPanelToggler, {

  layout: layout,
  tabsWidth: 0,
  containerWidth: 0,
  hideTabsCount: 0,
  overflowButtonShow: false,
  overflowedTabs: true,
  inactiveTabs: false,
  dynamicTabs: false,
  selectedTab: '',
  reloadTabs: true,
  activeTab: Ember.computed.oneWay('childPanes.firstObject.dataTab'),
  options: {
    tabPadding: 25,
    containerPadding: 0,
    dropdownSize: 70
  },

  /**
   * Array of registered child components
   *
   * @property children
   * @type array
   * @protected
   */
  children: null,

  /**
   * Array of hidden child components
   *
   * @property _hideTabs
   * @type array
   * @protected
   */
  _hideTabs: null,

  /**
   * All child components
   *
   * @property childPanes
   * @type array
   * @readonly
   * @private
   */
  childPanes: Ember.computed.filter('children', function(view) {
    return view instanceof TabPane;
  }),

  /**
   * Array of showed child components
   *
   * @property children
   * @type array
   * @private
   */
  _showedTabs: Ember.computed('tabs', {
    get() {
      return Ember.getWithDefault(this, 'tabs', null);
    },

    set(key, value) {
      return value;
    }
  }),

  /**
   * Array of objects that define the tab structure
   *
   * @property tabs
   * @type array
   * @readonly
   * @private
   */
  tabs: Ember.computed('childPanes.@each.{tab,title,dataTab}', function() {
    let items = Ember.A();
    this.get('childPanes').forEach((pane) => {
      let item = pane.getProperties('tab', 'title', 'dataTab');
      items.push(item);
    });

    return items;
  }),

  tabsObserver: Ember.observer('tabs.[]', function() {
    if (this.overflowedTabs) {
      this.reinitTabs();
    }
  }),

  _calculateWidths: function() {
    let width = 0;
    let countHide = this.get('tabs').length;
    let _this = this;
    _this.containerWidth = Ember.$(this.element).parent().width() - _this.options.containerPadding - _this.options.dropdownSize;

    Ember.$(_this.element).find('a.item').each(function() {
      if ((width + Ember.$(this).outerWidth(true)) < _this.containerWidth) {
        width += Ember.$(this).outerWidth(true);
        countHide--;
      }
    });

    this.hideTabsCount = countHide;
  },

  _hideTab: function() {
    let hideTabs = this.get('hideTabsCount');

    for (let i = 0; i < hideTabs; i++) {
      let _currentTabs = this.get('_showedTabs');
      let lastTab = Ember.A(_currentTabs).get('lastObject');

      this.get('_hideTabs').pushObject(lastTab);
      _currentTabs.popObject();
      this.set('_showedTabs', _currentTabs);
    }

    this.get('_hideTabs').reverse();
  },

  init() {
    this._super(...arguments);
    this.set('children', Ember.A());
    this.set('_hideTabs', Ember.A());

    if (this.overflowedTabs) {
      Ember.run.schedule('afterRender', this, function() {
        let _this = this;
        Ember.$(window).resize(function() {
          if (!(_this.get('isDestroyed') || _this.get('isDestroyed'))) {
            _this.reinitTabs();
          }
        });
      });
    }
  },

  reinitTabs() {
    Ember.run.schedule('afterRender', this, function() {
      this.set('overflowButtonShow', false);
      this.set('_hideTabs', Ember.A());
      this.set('_showedTabs', this.get('tabs').slice());

      Ember.run.schedule('afterRender', this,	function() {
        this._calculateWidths();

        this.set('overflowButtonShow', this.get('hideTabsCount') > 0);
        if (this.overflowButtonShow) {
          this._hideTab();
          if (this.dynamicTabs) {
            this.send('changeLastTab', this.get('_hideTabs').get('lastObject').title);
          }
        };

        let activeTab = this.get('activeTab');
        let showedTabs= this.get('_showedTabs');

        let activeTabShowed = showedTabs.filter(function(element) {
          return element.dataTab === activeTab;
        });

        if (activeTabShowed.length === 0) {
          activeTab = this.get('tabs').get('firstObject') ? this.get('tabs').get('firstObject').dataTab : null;
        }
        this.set('activeTab', activeTab);
      });
    });
  },

  /**
   * Register a component as a child of this parent
   *
   * @method registerChild
   * @param child
   * @public
   */
  registerChild(child) {
    Ember.run.schedule('actions', this, function() {
      this.get('children').addObject(child);
    });
  },

  /**
   * Remove the child component from this parent component
   *
   * @method removeChild
   * @param child
   * @public
   */
  removeChild(child) {
    Ember.run.schedule('actions', this, function() {
      this.get('children').removeObject(child);
    });
  },

  didInsertElement() {
    if (this.overflowedTabs) {
      this.reinitTabs();
    }
  },

  actions: {
    tabClick(tab) {
      let previous = this.get('activeTab');
      let activeTab = tab.dataTab === previous && this.inactiveTabs ? null : tab.dataTab;
      this.set('activeTab', activeTab);

      this.sendAction('tabClick', tab);
    },

    changeLastTab(selectedValue) {
      let _this = this;

      let _currentTabs = _this.get('_showedTabs');
      let _currentHideTabs =  _this.get('_hideTabs');
      let lastTab = _currentTabs.get('lastObject');

      let replaceTabIndex = -1;
      _currentHideTabs.forEach(function(element, index) {
        if (element.title === selectedValue) {
          replaceTabIndex = index;
          return;
        }
      });

      if (replaceTabIndex > -1) {
        let activeTabIndex = _currentHideTabs[replaceTabIndex].dataTab;
        _currentTabs.popObject();
        _currentTabs.pushObject(_currentHideTabs[replaceTabIndex]);
        _currentHideTabs.replace(replaceTabIndex, 1, lastTab);

        _this.set('_showedTabs', _currentTabs);
        _this.set('_hideTabs', _currentHideTabs);
        _this.set('activeTab', activeTabIndex);
      }
    }
  }
});
<commit_msg>Fix JSHint<commit_after>import Ember from 'ember';
import layout from '../templates/components/fd-tabs';
import TabPane from '../components/fd-tabs/pane';
import FdWorkPanelToggler from '../mixins/fd-work-panel-toggler';

/**
 Tab component for dynamic tab functionality
 ### Usage
 Just nest any number of yielded [Components.TabPane](Components.TabPane.html) components that hold the tab content.
 The tab navigation is automatically generated from the tab panes' `title` property:
 ```hbs
 {{#fd-tabs as |tab|}}
   {{#tab.pane dataTab='tab1' title="Tab 1"}}
     <p>...</p>
   {{/tab.pane}}
   {{#tab.pane dataTab='tab2' title="Tab 2"}}
     <p>...</p>
   {{/tab.pane}}
 {{/fd-tabs}}
 ```
 @class Tab
 @namespace Components
 @extends Ember.Component
 @uses Mixins.ComponentParent
 @public
 */
export default Ember.Component.extend(FdWorkPanelToggler, {

  layout: layout,
  tabsWidth: 0,
  containerWidth: 0,
  hideTabsCount: 0,
  overflowButtonShow: false,
  overflowedTabs: true,
  inactiveTabs: false,
  dynamicTabs: false,
  selectedTab: '',
  reloadTabs: true,
  activeTab: Ember.computed.oneWay('childPanes.firstObject.dataTab'),
  options: {
    tabPadding: 25,
    containerPadding: 0,
    dropdownSize: 70
  },

  /**
   * Array of registered child components
   *
   * @property children
   * @type array
   * @protected
   */
  children: null,

  /**
   * Array of hidden child components
   *
   * @property _hideTabs
   * @type array
   * @protected
   */
  _hideTabs: null,

  /**
   * All child components
   *
   * @property childPanes
   * @type array
   * @readonly
   * @private
   */
  childPanes: Ember.computed.filter('children', function(view) {
    return view instanceof TabPane;
  }),

  /**
   * Array of showed child components
   *
   * @property children
   * @type array
   * @private
   */
  _showedTabs: Ember.computed('tabs', {
    get() {
      return Ember.getWithDefault(this, 'tabs', null);
    },

    set(key, value) {
      return value;
    }
  }),

  /**
   * Array of objects that define the tab structure
   *
   * @property tabs
   * @type array
   * @readonly
   * @private
   */
  tabs: Ember.computed('childPanes.@each.{tab,title,dataTab}', function() {
    let items = Ember.A();
    this.get('childPanes').forEach((pane) => {
      let item = pane.getProperties('tab', 'title', 'dataTab');
      items.push(item);
    });

    return items;
  }),

  tabsObserver: Ember.observer('tabs.[]', function() {
    if (this.overflowedTabs) {
      this.reinitTabs();
    }
  }),

  _calculateWidths: function() {
    let width = 0;
    let countHide = this.get('tabs').length;
    let _this = this;
    _this.containerWidth = Ember.$(this.element).parent().width() - _this.options.containerPadding - _this.options.dropdownSize;

    Ember.$(_this.element).find('a.item').each(function() {
      if ((width + Ember.$(this).outerWidth(true)) < _this.containerWidth) {
        width += Ember.$(this).outerWidth(true);
        countHide--;
      }
    });

    this.hideTabsCount = countHide;
  },

  _hideTab: function() {
    let hideTabs = this.get('hideTabsCount');

    for (let i = 0; i < hideTabs; i++) {
      let _currentTabs = this.get('_showedTabs');
      let lastTab = Ember.A(_currentTabs).get('lastObject');

      this.get('_hideTabs').pushObject(lastTab);
      _currentTabs.popObject();
      this.set('_showedTabs', _currentTabs);
    }

    this.get('_hideTabs').reverse();
  },

  init() {
    this._super(...arguments);
    this.set('children', Ember.A());
    this.set('_hideTabs', Ember.A());

    if (this.overflowedTabs) {
      Ember.run.schedule('afterRender', this, function() {
        let _this = this;
        Ember.$(window).resize(function() {
          if (!(_this.get('isDestroyed') || _this.get('isDestroyed'))) {
            _this.reinitTabs();
          }
        });
      });
    }
  },

  reinitTabs() {
    Ember.run.schedule('afterRender', this, function() {
      this.set('overflowButtonShow', false);
      this.set('_hideTabs', Ember.A());
      this.set('_showedTabs', this.get('tabs').slice());

      Ember.run.schedule('afterRender', this,	function() {
        this._calculateWidths();

        this.set('overflowButtonShow', this.get('hideTabsCount') > 0);
        if (this.overflowButtonShow) {
          this._hideTab();
          if (this.dynamicTabs) {
            this.send('changeLastTab', this.get('_hideTabs').get('lastObject').title);
          }
        }

        let activeTab = this.get('activeTab');
        let showedTabs= this.get('_showedTabs');

        let activeTabShowed = showedTabs.filter(function(element) {
          return element.dataTab === activeTab;
        });

        if (activeTabShowed.length === 0) {
          activeTab = this.get('tabs').get('firstObject') ? this.get('tabs').get('firstObject').dataTab : null;
        }
        this.set('activeTab', activeTab);
      });
    });
  },

  /**
   * Register a component as a child of this parent
   *
   * @method registerChild
   * @param child
   * @public
   */
  registerChild(child) {
    Ember.run.schedule('actions', this, function() {
      this.get('children').addObject(child);
    });
  },

  /**
   * Remove the child component from this parent component
   *
   * @method removeChild
   * @param child
   * @public
   */
  removeChild(child) {
    Ember.run.schedule('actions', this, function() {
      this.get('children').removeObject(child);
    });
  },

  didInsertElement() {
    if (this.overflowedTabs) {
      this.reinitTabs();
    }
  },

  actions: {
    tabClick(tab) {
      let previous = this.get('activeTab');
      let activeTab = tab.dataTab === previous && this.inactiveTabs ? null : tab.dataTab;
      this.set('activeTab', activeTab);

      this.sendAction('tabClick', tab);
    },

    changeLastTab(selectedValue) {
      let _this = this;

      let _currentTabs = _this.get('_showedTabs');
      let _currentHideTabs =  _this.get('_hideTabs');
      let lastTab = _currentTabs.get('lastObject');

      let replaceTabIndex = -1;
      _currentHideTabs.forEach(function(element, index) {
        if (element.title === selectedValue) {
          replaceTabIndex = index;
          return;
        }
      });

      if (replaceTabIndex > -1) {
        let activeTabIndex = _currentHideTabs[replaceTabIndex].dataTab;
        _currentTabs.popObject();
        _currentTabs.pushObject(_currentHideTabs[replaceTabIndex]);
        _currentHideTabs.replace(replaceTabIndex, 1, lastTab);

        _this.set('_showedTabs', _currentTabs);
        _this.set('_hideTabs', _currentHideTabs);
        _this.set('activeTab', activeTabIndex);
      }
    }
  }
});
<|endoftext|>


<commit_before>/*
 * Route controller.
 */

/*
 * Module dependencies.
 */
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var express = require('express');
var async = require('async');

/**
 * Main route controller class.
 *
 * @param {Application} application Application.
 * @param {Object} settings Application settings object.
 * @class RouteController
 */
var RouteController = module.exports = function(application, settings) {
  this.application = application;
  this.settings = settings;

  var self = this;
  var app = this.application.application;
  var method = settings.method || 'all';

  // Prepare route register params.
  var params = [self.settings.path];

  // Prepare the middleware, if available.
  if ('middleware' in settings) {
    params.push(settings.middleware);
  }

  // Prepare the route handler.
  params.push(function(request, response) {
    self.handle(request, response);
  });

  // Register on express.
  app[method].apply(app, params);
};

/**
 * Handle a request.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object
 */
RouteController.prototype.handle = function(request, response) {
  var self = this;
  var settings = this.settings;

  this.access(request, response, function(err, allow) {
    if (err) {
      RouteController.error(request, response, err);
    }

    if (allow) {
      // A route can have either a content or callback property.
      if (settings.content) {
        RouteController.respond(request, response, settings.content);
      }
      else if (settings.callback) {
        settings.callback(request, response, function(err, content, code) {
          if (err) {
            RouteController.error(request, response, err);
          }

          if (content) {
            RouteController.respond(request, response, content, code);
          }
          else {
            // If there's no content, return 404 error.
            RouteController.notFound(request, response);
          }
        });
      }
    }
    else {
      // Access denied.
      RouteController.forbidden(request, response);
    }
  });
};

/**
 * Access check.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 * @param {Function} callaback Function called with the access check results.
 */
RouteController.prototype.access = function(request, response, callback) {
  var settings = this.settings;

  // If this is an object, do access check and other mandatory stuff.
  if (settings.access) {
    if (typeof settings.access === 'boolean') {
      // Access check is a string/permission key.
      return callback(null, settings.access);
    }
    else if (typeof settings.access === 'string') {
      return this.application.access(request, settings.access, callback);
    }
    else if (typeof settings.access === 'function') {
      // Access check is a callback.
      return settings.access(request, response, callback);
    }
  }

  // Default to false. Deny access.
  return callback(null, false);
};

/**
 * Respond to a request.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 * @param {Object|String} content Content to send.
 * @param {Number} [code] HTTP status code.
 */
RouteController.respond = function(request, response, content, code) {
  // Default to 200 (success).
  var code = code || 200;

  var payload = {
    status: {
      code: code
    }
  };

  if (content) {
    payload.data = content;
  }

  response.status(code).send(payload);
};

/**
 * Respond to a request with a "server error".
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.error = function(request, response, error) {
  RouteController.respond(request, response, {
    title: 'Server error',
    description: "The server couldn't process the request.",
    error: error.toString()
  }, 500);
};

/**
 * Respond to a request with a "page not found" error.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.notFound = function(request, response) {
  RouteController.respond(request, response, {
    title: 'Page not found',
    description: "The page you're looking for wasn't found."
  }, 404);
};

/**
 * Respond to a request with a "forbidden" error.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.forbidden = function(request, response) {
  RouteController.respond(request, response, {
    title: 'Forbidden',
    description: "You don't have permission to access this page."
  }, 403);
};
<commit_msg>Refactoring handle() method of RouteController object for better access check and to cover all cases.<commit_after>/*
 * Route controller.
 */

/*
 * Module dependencies.
 */
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var express = require('express');
var async = require('async');

/**
 * Main route controller class.
 *
 * @param {Application} application Application.
 * @param {Object} settings Application settings object.
 * @class RouteController
 */
var RouteController = module.exports = function(application, settings) {
  this.application = application;
  this.settings = settings;

  var self = this;
  var app = this.application.application;
  var method = settings.method || 'all';

  // Prepare route register params.
  var params = [self.settings.path];

  // Prepare the middleware, if available.
  if ('middleware' in settings) {
    params.push(settings.middleware);
  }

  // Prepare the route handler.
  params.push(function(request, response) {
    self.handle(request, response);
  });

  // Register on express.
  app[method].apply(app, params);
};

/**
 * Handle a request.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object
 */
RouteController.prototype.handle = function(request, response) {
  var self = this;
  var settings = this.settings;

  this.access(request, response, function(error, allow) {
    if (error) {
      return RouteController.error(request, response, error);
    }

    if (allow !== true) {
      // Access denied.
      return RouteController.forbidden(request, response);
    }

    if (settings.content) {
      return RouteController.respond(request, response, settings.content);
    }

    if (settings.callback) {
      return settings.callback(request, response, function(error, content, code) {
        if (error) {
          return RouteController.error(request, response, error);
        }

        RouteController.respond(request, response, content, code);
      });
    }

    // If there's no content nor a callback, return 404 error.
    RouteController.notFound(request, response);
  });
};

/**
 * Access check.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 * @param {Function} callaback Function called with the access check results.
 */
RouteController.prototype.access = function(request, response, callback) {
  var settings = this.settings;

  // If this is an object, do access check and other mandatory stuff.
  if (settings.access) {
    if (typeof settings.access === 'boolean') {
      // Access check is a string/permission key.
      return callback(null, settings.access);
    }
    else if (typeof settings.access === 'string') {
      return this.application.access(request, settings.access, callback);
    }
    else if (typeof settings.access === 'function') {
      // Access check is a callback.
      return settings.access(request, response, callback);
    }
  }

  // Default to false. Deny access.
  return callback(null, false);
};

/**
 * Respond to a request.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 * @param {Object|String} content Content to send.
 * @param {Number} [code] HTTP status code.
 */
RouteController.respond = function(request, response, content, code) {
  // Default to 200 (success).
  var code = code || 200;

  var payload = {
    status: {
      code: code
    }
  };

  if (content) {
    payload.data = content;
  }

  response.status(code).send(payload);
};

/**
 * Respond to a request with a "server error".
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.error = function(request, response, error) {
  RouteController.respond(request, response, {
    title: 'Server error',
    description: "The server couldn't process the request.",
    error: error.toString()
  }, 500);
};

/**
 * Respond to a request with a "page not found" error.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.notFound = function(request, response) {
  RouteController.respond(request, response, {
    title: 'Page not found',
    description: "The page you're looking for wasn't found."
  }, 404);
};

/**
 * Respond to a request with a "forbidden" error.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.forbidden = function(request, response) {
  RouteController.respond(request, response, {
    title: 'Forbidden',
    description: "You don't have permission to access this page."
  }, 403);
};
<|endoftext|>