<commit_before>import DS from 'ember-data';
import Ember from 'ember';

export default DS.Adapter.extend({
  /**
   * Database name
   * @type {string}
   */
  dbName: 'database',

  /**
   * Database version number.
   * Whenever this number is greater than the version number of the existing
   * database, this will update it's schema.
   * @type {number}
   */
  version: 1,

  /**
   * Open IndexedDB and upgrade if necessary
   */
  init() {
    let self = this;
    let request = window.indexedDB.open(this.get('dbName'),this.get('version'));

    request.onerror = function() {
      console.log('Unable to open IndexedDB');
    };

    request.onupgradeneeded = function(event) {
      let db = event.target.result;

      db.onerror = function(event) {
        console.log('IndexedDB error: ' + event.target.errorCode);
      };

      for (let model of self.get('models')) {
        if (!db.objectStoreNames.contains(model)) {
          db.createObjectStore(model, {keyPath: 'id'});
        }
      }
    };
  },

  /**
   * Open IndexedDB
   * return {promise} Promise that contains an IDBOpenDBRequest instance
   */
  openDatabase() {
    return new Ember.RSVP.Promise((resolve, reject) => {
      let request = window.indexedDB.open(this.get('dbName'));

      request.onerror = function(event) {
        console.log('Unable to open IndexedDB');
        Ember.run(null, reject, event);
      };

      request.onsuccess = function(event) {
        Ember.run(null, resolve, event.target.result);
      };
    });
  },

  /**
   * Generate a random number for ID
   * return {string} Generated ID
   */
  generateIdForRecord() {
    return Math.random().toString(32).slice(2).substr(0, 5);
  },

  /**
   * Create new record
   * param {object} store DS.Store
   * param {object} type DS.Model class of the record
   * param {object} snapshot DS.Snapshot
   * return {promise} Promise that contains the record
   */
  createRecord(store, type, snapshot) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let data = this.serialize(snapshot, {includeId: true});
        let modelName = type.modelName;
        let objectStore = db.transaction([modelName], 'readwrite').
            objectStore(modelName);
        let request = objectStore.add(data);

        request.onsuccess = function() {
          db.close();
          Ember.run(null, resolve, data);
        };

        request.onerror = function(event) {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  },

  /**
   * Find record by it's ID
   * param {object} store DS.Store
   * param {object} type DS.Model
   * param {string} id ID
   * return {promise} Promise that contains the record
   */
  findRecord(store, type, id) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let modelName = type.modelName;
        let objectStore = db.transaction([modelName]).objectStore(modelName);
        let request = objectStore.get(id);

        request.onsuccess = function() {
          db.close();
          Ember.run(null, resolve, request.result);
        };

        request.onerror = function(event) {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  },

  /**
   * Find all records
   * param {object} store DS.Store
   * param {object} type DS.Model
   * return {promise} Promise that contains the record
   */
  findAll(store, type) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let data = [];
        let modelName = type.modelName;
        let objectStore = db.transaction([modelName]).objectStore(modelName);
        let request = objectStore.openCursor();

        request.onsuccess = function(event) {
          let cursor = event.target.result;

          if (cursor) {
            data.push(cursor.value);
            cursor.continue();
          } else {
            Ember.run(null, resolve, data);
          }
        };

        request.onerror = function(event) {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  },

  /**
   * Update record
   * param {object} store DS.Store
   * param {object} type DS.Model class of the record
   * param {object} snapshot DS.Snapshot
   * return {promise} Promise that contains the record
   */
  updateRecord(store, type, snapshot) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let data = this.serialize(snapshot, {includeId: true});
        let modelName = type.modelName;
        let objectStore = db.transaction([modelName], 'readwrite').
            objectStore(modelName);
        let request = objectStore.get(snapshot.id);

        request.onsuccess = function() {
          let requestUpdate = objectStore.put(data);

          requestUpdate.onsuccess = function() {
            db.close();
            Ember.run(null, resolve, data);
          };

          requestUpdate.onerror = function(event) {
            console.log('IndexedDB error: ' + event.target.errorCode);
            db.close();
            Ember.run(null, reject, event);
          };
        };
      });
    });
  },

  /**
   * Delete record
   * param {object} store DS.Store
   * param {object} type DS.Model class of the record
   * param {object} snapshot DS.Snapshot
   * return {promise} Promise that contains the record
   */
  deleteRecord(store, type, snapshot) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let data = this.serialize(snapshot, {includeId: true});
        let modelName = type.modelName;
        let objectStore = db.transaction([modelName], 'readwrite').
            objectStore(modelName);
        let request = objectStore.delete(snapshot.id);

        request.onsuccess = function() {
          db.close();
          Ember.run(null, resolve, data);
        };

        request.onerror = function() {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  },

  /**
   * TODO: See if using index is feasible
   *
   * Find a record that matches the query
   * param {object} store DS.Store
   * param {object} type DS.Model
   * param {object} query Query object
   * return {promise} Promise that contains the record
   */
  queryRecord(store, type, query) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let modelName = type.modelName;
        let queryKeys = Object.keys(query);
        let objectStore = db.transaction([modelName]).objectStore(modelName);
        let request = objectStore.openCursor();

        request.onsuccess = function(event) {
          let cursor = event.target.result;

          if (cursor) {
            let isQueryKeyMatchCount = 0;

            for (let key of queryKeys) {
              if (cursor.value[key] === query[key]) {
                isQueryKeyMatchCount++;
              }
            }

            if (isQueryKeyMatchCount === queryKeys.length) {
              db.close();
              Ember.run(null, resolve, cursor.value);
            } else {
              cursor.continue();
            }
          } else {
            db.close();
            Ember.run(null, resolve, null);
          }
        };

        request.onerror = function(event) {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  },

  /**
   * TODO: See if using index is feasible
   *
   * Find records that matches the query
   * param {object} store DS.Store
   * param {object} type DS.Model
   * param {object} query Query object
   * return {promise} Promise that contains the records
   */
  query(store, type, query) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let data = [];
        let modelName = type.modelName;
        let queryKeys = Object.keys(query);
        let objectStore = db.transaction([modelName]).objectStore(modelName);
        let request = objectStore.openCursor();

        request.onsuccess = function(event) {
          let cursor = event.target.result;

          if (cursor) {
            let isQueryKeyMatchCount = 0;

            for (let key of queryKeys) {
              if (cursor.value[key] === query[key]) {
                isQueryKeyMatchCount++;
              }
            }

            if (isQueryKeyMatchCount === queryKeys.length) {
              data.push(cursor.value);
            }

            cursor.continue();
          } else {
            db.close();
            Ember.run(null, resolve, data);
          }
        };

        request.onerror = function(event) {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  }
});
<commit_msg>Minor code cleanup<commit_after>import DS from 'ember-data';
import Ember from 'ember';

export default DS.Adapter.extend({
  /**
   * Database name
   * @type {string}
   */
  dbName: 'database',

  /**
   * Database version number.
   * Whenever this number is greater than the version number of the existing
   * database, this will update it's schema.
   * @type {number}
   */
  version: 1,

  /**
   * Open IndexedDB and upgrade if necessary
   */
  init() {
    let self = this;
    let request = window.indexedDB.open(this.get('dbName'),this.get('version'));

    request.onerror = function() {
      console.log('Unable to open IndexedDB');
    };

    request.onupgradeneeded = function(event) {
      let db = event.target.result;

      db.onerror = function(event) {
        console.log('IndexedDB error: ' + event.target.errorCode);
      };

      for (let model of self.get('models')) {
        if (!db.objectStoreNames.contains(model)) {
          db.createObjectStore(model, {keyPath: 'id'});
        }
      }
    };
  },

  /**
   * Open IndexedDB
   * return {promise} Promise that contains an IDBOpenDBRequest instance
   */
  openDatabase() {
    return new Ember.RSVP.Promise((resolve, reject) => {
      let request = window.indexedDB.open(this.get('dbName'));

      request.onerror = function(event) {
        console.log('Unable to open IndexedDB');
        Ember.run(null, reject, event);
      };

      request.onsuccess = function(event) {
        Ember.run(null, resolve, event.target.result);
      };
    });
  },

  /**
   * Generate a random number for ID
   * return {string} Generated ID
   */
  generateIdForRecord() {
    return Math.random().toString(32).slice(2).substr(0, 5);
  },

  /**
   * Create new record
   * param {object} store DS.Store
   * param {object} type DS.Model class of the record
   * param {object} snapshot DS.Snapshot
   * return {promise} Promise that contains the record
   */
  createRecord(store, type, snapshot) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let data = this.serialize(snapshot, {includeId: true});
        let modelName = type.modelName;
        let objectStore = db.transaction([modelName], 'readwrite').
            objectStore(modelName);
        let request = objectStore.add(data);

        request.onsuccess = function() {
          db.close();
          Ember.run(null, resolve, data);
        };

        request.onerror = function(event) {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  },

  /**
   * Find record by it's ID
   * param {object} store DS.Store
   * param {object} type DS.Model
   * param {string} id ID
   * return {promise} Promise that contains the record
   */
  findRecord(store, type, id) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let modelName = type.modelName;
        let objectStore = db.transaction([modelName]).objectStore(modelName);
        let request = objectStore.get(id);

        request.onsuccess = function() {
          db.close();
          Ember.run(null, resolve, request.result);
        };

        request.onerror = function(event) {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  },

  /**
   * Find all records
   * param {object} store DS.Store
   * param {object} type DS.Model
   * return {promise} Promise that contains the record
   */
  findAll(store, type) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let data = [];
        let modelName = type.modelName;
        let objectStore = db.transaction([modelName]).objectStore(modelName);
        let request = objectStore.openCursor();

        request.onsuccess = function(event) {
          let cursor = event.target.result;

          if (cursor) {
            data.push(cursor.value);
            cursor.continue();
          } else {
            Ember.run(null, resolve, data);
          }
        };

        request.onerror = function(event) {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  },

  /**
   * Update record
   * param {object} store DS.Store
   * param {object} type DS.Model class of the record
   * param {object} snapshot DS.Snapshot
   * return {promise} Promise that contains the record
   */
  updateRecord(store, type, snapshot) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let data = this.serialize(snapshot, {includeId: true});
        let modelName = type.modelName;
        let objectStore = db.transaction([modelName], 'readwrite').
            objectStore(modelName);
        let request = objectStore.get(snapshot.id);

        request.onsuccess = function() {
          let requestUpdate = objectStore.put(data);

          requestUpdate.onsuccess = function() {
            db.close();
            Ember.run(null, resolve, data);
          };

          requestUpdate.onerror = function(event) {
            console.log('IndexedDB error: ' + event.target.errorCode);
            db.close();
            Ember.run(null, reject, event);
          };
        };
      });
    });
  },

  /**
   * Delete record
   * param {object} store DS.Store
   * param {object} type DS.Model class of the record
   * param {object} snapshot DS.Snapshot
   * return {promise} Promise that contains the record
   */
  deleteRecord(store, type, snapshot) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let data = this.serialize(snapshot, {includeId: true});
        let modelName = type.modelName;
        let objectStore = db.transaction([modelName], 'readwrite').
            objectStore(modelName);
        let request = objectStore.delete(snapshot.id);

        request.onsuccess = function() {
          db.close();
          Ember.run(null, resolve, data);
        };

        request.onerror = function() {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  },

  /**
   * TODO: See if using index is feasible
   *
   * Find a record that matches the query
   * param {object} store DS.Store
   * param {object} type DS.Model
   * param {object} query Query object
   * return {promise} Promise that contains the record
   */
  queryRecord(store, type, query) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let modelName = type.modelName;
        let queryKeys = Object.keys(query);
        let objectStore = db.transaction([modelName]).objectStore(modelName);
        let request = objectStore.openCursor();

        request.onsuccess = function(event) {
          let cursor = event.target.result;

          if (cursor) {
            let queryKeyMatchCount = 0;

            for (let key of queryKeys) {
              if (cursor.value[key] === query[key]) {
                queryKeyMatchCount++;
              }
            }

            if (queryKeyMatchCount === queryKeys.length) {
              db.close();
              Ember.run(null, resolve, cursor.value);
            } else {
              cursor.continue();
            }
          } else {
            db.close();
            Ember.run(null, resolve, null);
          }
        };

        request.onerror = function(event) {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  },

  /**
   * TODO: See if using index is feasible
   *
   * Find records that matches the query
   * param {object} store DS.Store
   * param {object} type DS.Model
   * param {object} query Query object
   * return {promise} Promise that contains the records
   */
  query(store, type, query) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      this.openDatabase().then(db => {
        let data = [];
        let modelName = type.modelName;
        let queryKeys = Object.keys(query);
        let objectStore = db.transaction([modelName]).objectStore(modelName);
        let request = objectStore.openCursor();

        request.onsuccess = function(event) {
          let cursor = event.target.result;

          if (cursor) {
            let queryKeyMatchCount = 0;

            for (let key of queryKeys) {
              if (cursor.value[key] === query[key]) {
                queryKeyMatchCount++;
              }
            }

            if (queryKeyMatchCount === queryKeys.length) {
              data.push(cursor.value);
            }

            cursor.continue();
          } else {
            db.close();
            Ember.run(null, resolve, data);
          }
        };

        request.onerror = function(event) {
          console.log('IndexedDB error: ' + event.target.errorCode);
          db.close();
          Ember.run(null, reject, event);
        };
      });
    });
  }
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import Configuration from '../configuration';
import TokenAuthenticator from './token';

/**
  Extended Token Authenticator that includes refresh by default.

  _The factory for this authenticator is registered as
  `'simple-auth-authenticator:jwt'` in Ember's container._

  @class Jwt
  @namespace SimpleAuth.Authenticators
  @module simple-auth-token/authenticators/jwt
  @extends TokenAuthenticator
*/
export default TokenAuthenticator.extend({
  /**
    The endpoint on the server for refreshing a token.
    @property serverTokenRefreshEndpoint
    @type String
    @default '/api-token-refresh/'
  */
  serverTokenRefreshEndpoint: '/api-token-refresh/',

  /**
    Sets whether the authenticator automatically refreshes access tokens.
    @property refreshAccessTokens
    @type Boolean
    @default true
  */
  refreshAccessTokens: true,

  /**
    The amount of time to wait before refreshing the token - set automatically.
    @property refreshTokenTimeout
    @private
  */
  refreshTokenTimeout: null,

  /**
    The name for which decoded token field represents the token expire time.
    @property tokenExpireName
    @type String
    @default 'exp'
  */
  tokenExpireName: 'exp',

  /**
    Default time unit.
    @property timeFactor
    @type Integer
    @default 1 (seconds)
  */
  timeFactor: 1,

  /**
    @method init
    @private
  */
  init: function() {
    this.serverTokenEndpoint = Configuration.serverTokenEndpoint;
    this.serverTokenRefreshEndpoint = Configuration.serverTokenRefreshEndpoint;
    this.identificationField = Configuration.identificationField;
    this.tokenPropertyName = Configuration.tokenPropertyName;
    this.refreshAccessTokens = Configuration.refreshAccessTokens;
    this.tokenExpireName = Configuration.tokenExpireName;
    this.timeFactor = Configuration.timeFactor;
    this.headers = Configuration.headers;
  },

  /**
    Restores the session from a set of session properties; __will return a
    resolving promise when there's a non-empty `token` in the `data`__
    and a rejecting promise otherwise.
    This method also schedules automatic token refreshing when there are values
    for `token` and `expiresAt` in the `data` and automatic token
    refreshing is not disabled.
    @method restore
    @param {Object} data The data to restore the session from
    @return {Ember.RSVP.Promise} A promise that when it resolves results in the session being authenticated
  */
  restore: function(data){
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject){
      var now = (new Date()).getTime(),
        expiresAt = _this.resolveTime(data.expiresAt);
      if(!Ember.isEmpty(data.expiresAt) && !Ember.isEmpty(data.token) && data.expiresAt > now){
        if(_this.refreshAccessTokens){
          _this.refreshAccessToken(data.token).then(function(data){
            resolve(data);
          }, reject);
        }else{
          reject();
        }
      }else{
        if(Ember.isEmpty(data.token)){
          reject();
        }else{
          var tokenData = _this.getTokenData({'token': data.token}),
            tokenExpiresAt = tokenData[_this.tokenExpireName];
          expiresAt = _this.resolveTime(tokenExpiresAt);
          _this.scheduleAccessTokenRefresh(expiresAt, data.token);
          resolve(data);
        }
      }
    });
  },

  /**
    Authenticates the session with the specified `credentials`; the credentials
    are `POST`ed to the
    [`Authenticators.Token#serverTokenEndpoint`](#SimpleAuth-Authenticators-Token-serverTokenEndpoint)
    and if they are valid the server returns an auth token in
    response. __If the credentials are valid and authentication succeeds, a
    promise that resolves with the server's response is returned__, otherwise a
    promise that rejects with the server error is returned.

    @method authenticate
    @param {Object} options The credentials to authenticate the session with
    @return {Ember.RSVP.Promise} A promise that resolves when an auth token is successfully acquired from the server and rejects otherwise
  */
  authenticate: function(credentials) {
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      var data = _this.getAuthenticateData(credentials);
      _this.makeRequest(_this.serverTokenEndpoint, data).then(function(response) {
        Ember.run(function() {
          var tokenData = _this.getTokenData(response),
              expiresAt = tokenData[_this.tokenExpireName];
          _this.scheduleAccessTokenRefresh(expiresAt, response.token);
          response = Ember.merge(response, { expiresAt: expiresAt });
          resolve(_this.getResponseData(response));
        });
      }, function(xhr) {
        Ember.run(function() {
          reject(xhr.responseJSON || xhr.responseText);
        });
      });
    });
  },

  /**
    @method scheduleAccessTokenRefresh
    @private
  */
  scheduleAccessTokenRefresh: function(expiresAt, token) {
    if(this.refreshAccessTokens){
      expiresAt = this.resolveTime(expiresAt);
      var now = (new Date()).getTime(),
        wait = expiresAt - now;
      if(!Ember.isEmpty(token) && !Ember.isEmpty(expiresAt) && expiresAt > now){
        Ember.run.cancel(this._refreshTokenTimeout);
        delete this._refreshTokenTimeout;
        if(!Ember.testing){
          this._refreshTokenTimeout = Ember.run.later(this, this.refreshAccessToken, token, wait);
        }
      }
    }
  },

  /**
    @method refreshAccessToken
    @private
  */
  refreshAccessToken: function(token) {
    var _this = this;
    var data  = {token: token};
    return new Ember.RSVP.Promise(function(resolve, reject) {
      _this.makeRequest(_this.serverTokenRefreshEndpoint, data).then(function(response) {
        Ember.run(function() {
          var tokenData = _this.getTokenData(response),
            expiresAt = tokenData[_this.tokenExpireName],
            data = Ember.merge(response, {expiresAt: expiresAt, token: response.token});
          _this.scheduleAccessTokenRefresh(expiresAt, response.token);
          _this.trigger('sessionDataUpdated', data);
          resolve(response);
        });
      }, function(xhr, status, error) {
        Ember.Logger.warn('Access token could not be refreshed - server responded with ' + error + '.');
        reject();
      });
    });
  },

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

    @method getTokenData
    @return {object} An object with properties for the session.
  */
  getTokenData: function(response) {
    var token = response.token.split('.');

    if(token.length > 1){
      token = token[1];
    }else{
      token = token[0];
    }

    return JSON.parse(atob(token));
  },

  /**
    @method makeRequest
    @private
  */
  makeRequest: function(url, data) {
    return Ember.$.ajax({
      url: url,
      type: 'POST',
      data: JSON.stringify(data),
      dataType: 'json',
      contentType: 'application/json',
      beforeSend: function(xhr, settings) {
        xhr.setRequestHeader('Accept', settings.accepts.json);
      },
      headers: this.headers
    });
  },

  /**
    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 {Ember.RSVP.Promise} A resolving promise
  */
  invalidate: function() {
    Ember.run.cancel(this._refreshTokenTimeout);
    delete this._refreshTokenTimeout;
    return new Ember.RSVP.resolve();
  },

  /**
    Handles converting between time units for data between different systems.
    Default: seconds(1)
    @method resolveTime
    @private
  */
  resolveTime: function(time){
    return new Date(time * this.timeFactor).getTime();
  },
});
<commit_msg>Removed unused token data to ember merge<commit_after>import Ember from 'ember';
import Configuration from '../configuration';
import TokenAuthenticator from './token';

/**
  Extended Token Authenticator that includes refresh by default.

  _The factory for this authenticator is registered as
  `'simple-auth-authenticator:jwt'` in Ember's container._

  @class Jwt
  @namespace SimpleAuth.Authenticators
  @module simple-auth-token/authenticators/jwt
  @extends TokenAuthenticator
*/
export default TokenAuthenticator.extend({
  /**
    The endpoint on the server for refreshing a token.
    @property serverTokenRefreshEndpoint
    @type String
    @default '/api-token-refresh/'
  */
  serverTokenRefreshEndpoint: '/api-token-refresh/',

  /**
    Sets whether the authenticator automatically refreshes access tokens.
    @property refreshAccessTokens
    @type Boolean
    @default true
  */
  refreshAccessTokens: true,

  /**
    The amount of time to wait before refreshing the token - set automatically.
    @property refreshTokenTimeout
    @private
  */
  refreshTokenTimeout: null,

  /**
    The name for which decoded token field represents the token expire time.
    @property tokenExpireName
    @type String
    @default 'exp'
  */
  tokenExpireName: 'exp',

  /**
    Default time unit.
    @property timeFactor
    @type Integer
    @default 1 (seconds)
  */
  timeFactor: 1,

  /**
    @method init
    @private
  */
  init: function() {
    this.serverTokenEndpoint = Configuration.serverTokenEndpoint;
    this.serverTokenRefreshEndpoint = Configuration.serverTokenRefreshEndpoint;
    this.identificationField = Configuration.identificationField;
    this.tokenPropertyName = Configuration.tokenPropertyName;
    this.refreshAccessTokens = Configuration.refreshAccessTokens;
    this.tokenExpireName = Configuration.tokenExpireName;
    this.timeFactor = Configuration.timeFactor;
    this.headers = Configuration.headers;
  },

  /**
    Restores the session from a set of session properties; __will return a
    resolving promise when there's a non-empty `token` in the `data`__
    and a rejecting promise otherwise.
    This method also schedules automatic token refreshing when there are values
    for `token` and `expiresAt` in the `data` and automatic token
    refreshing is not disabled.
    @method restore
    @param {Object} data The data to restore the session from
    @return {Ember.RSVP.Promise} A promise that when it resolves results in the session being authenticated
  */
  restore: function(data){
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject){
      var now = (new Date()).getTime(),
        expiresAt = _this.resolveTime(data.expiresAt);
      if(!Ember.isEmpty(data.expiresAt) && !Ember.isEmpty(data.token) && data.expiresAt > now){
        if(_this.refreshAccessTokens){
          _this.refreshAccessToken(data.token).then(function(data){
            resolve(data);
          }, reject);
        }else{
          reject();
        }
      }else{
        if(Ember.isEmpty(data.token)){
          reject();
        }else{
          var tokenData = _this.getTokenData({'token': data.token}),
            tokenExpiresAt = tokenData[_this.tokenExpireName];
          expiresAt = _this.resolveTime(tokenExpiresAt);
          _this.scheduleAccessTokenRefresh(expiresAt, data.token);
          resolve(data);
        }
      }
    });
  },

  /**
    Authenticates the session with the specified `credentials`; the credentials
    are `POST`ed to the
    [`Authenticators.Token#serverTokenEndpoint`](#SimpleAuth-Authenticators-Token-serverTokenEndpoint)
    and if they are valid the server returns an auth token in
    response. __If the credentials are valid and authentication succeeds, a
    promise that resolves with the server's response is returned__, otherwise a
    promise that rejects with the server error is returned.

    @method authenticate
    @param {Object} options The credentials to authenticate the session with
    @return {Ember.RSVP.Promise} A promise that resolves when an auth token is successfully acquired from the server and rejects otherwise
  */
  authenticate: function(credentials) {
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      var data = _this.getAuthenticateData(credentials);
      _this.makeRequest(_this.serverTokenEndpoint, data).then(function(response) {
        Ember.run(function() {
          var tokenData = _this.getTokenData(response),
              expiresAt = tokenData[_this.tokenExpireName];
          _this.scheduleAccessTokenRefresh(expiresAt, response.token);
          response = Ember.merge(response, { expiresAt: expiresAt });
          resolve(_this.getResponseData(response));
        });
      }, function(xhr) {
        Ember.run(function() {
          reject(xhr.responseJSON || xhr.responseText);
        });
      });
    });
  },

  /**
    @method scheduleAccessTokenRefresh
    @private
  */
  scheduleAccessTokenRefresh: function(expiresAt, token) {
    if(this.refreshAccessTokens){
      expiresAt = this.resolveTime(expiresAt);
      var now = (new Date()).getTime(),
        wait = expiresAt - now;
      if(!Ember.isEmpty(token) && !Ember.isEmpty(expiresAt) && expiresAt > now){
        Ember.run.cancel(this._refreshTokenTimeout);
        delete this._refreshTokenTimeout;
        if(!Ember.testing){
          this._refreshTokenTimeout = Ember.run.later(this, this.refreshAccessToken, token, wait);
        }
      }
    }
  },

  /**
    @method refreshAccessToken
    @private
  */
  refreshAccessToken: function(token) {
    var _this = this;
    var data  = {token: token};
    return new Ember.RSVP.Promise(function(resolve, reject) {
      _this.makeRequest(_this.serverTokenRefreshEndpoint, data).then(function(response) {
        Ember.run(function() {
          var tokenData = _this.getTokenData(response),
            expiresAt = tokenData[_this.tokenExpireName],
            data = Ember.merge(response, {expiresAt: expiresAt});
          _this.scheduleAccessTokenRefresh(expiresAt, response.token);
          _this.trigger('sessionDataUpdated', data);
          resolve(response);
        });
      }, function(xhr, status, error) {
        Ember.Logger.warn('Access token could not be refreshed - server responded with ' + error + '.');
        reject();
      });
    });
  },

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

    @method getTokenData
    @return {object} An object with properties for the session.
  */
  getTokenData: function(response) {
    var token = response.token.split('.');
    if(token.length > 1){
      return JSON.parse(atob(token[1]));
    }else{
      return JSON.parse(atob(token[0]));
    }
  },

  /**
    @method makeRequest
    @private
  */
  makeRequest: function(url, data) {
    return Ember.$.ajax({
      url: url,
      type: 'POST',
      data: JSON.stringify(data),
      dataType: 'json',
      contentType: 'application/json',
      beforeSend: function(xhr, settings) {
        xhr.setRequestHeader('Accept', settings.accepts.json);
      },
      headers: this.headers
    });
  },

  /**
    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 {Ember.RSVP.Promise} A resolving promise
  */
  invalidate: function() {
    Ember.run.cancel(this._refreshTokenTimeout);
    delete this._refreshTokenTimeout;
    return new Ember.RSVP.resolve();
  },

  /**
    Handles converting between time units for data between different systems.
    Default: seconds(1)
    @method resolveTime
    @private
  */
  resolveTime: function(time){
    return new Date(time * this.timeFactor).getTime();
  },
});
<|endoftext|>


<commit_before>
// add a component to render a line chart with
// different fill colors for positive & negative
//
// Source: https://stackoverflow.com/a/36941860/211771

export function initialize(/* application */) {
  var Chart = window.Chart;
  Chart.defaults.DualFillLine = Chart.helpers.clone(Chart.defaults.line);
  Chart.controllers.DualFillLine = Chart.controllers.line.extend({
      update: function () {
          // get the min and max values
          // if the min is non-zero, force a zero min
          var min = Math.min.apply(null, [...this.chart.data.datasets[0].data, 0]);
          var max = Math.max.apply(null, this.chart.data.datasets[0].data);
          var yScale = this.getScaleForId(this.getDataset().yAxisID);

          // figure out the pixels for these and the value 0
          var top = yScale.getPixelForValue(max);
          var zero = yScale.getPixelForValue(0);
          var bottom = yScale.getPixelForValue(min);

          // build a gradient that switches color at the 0 point
          var ctx = this.chart.chart.ctx;
          var gradient = ctx.createLinearGradient(0, top, 0, bottom);
          var ratio = Math.min((zero - top) / (bottom - top), 1);
          gradient.addColorStop(0, 'rgba(75,192,75,0.4)');
          gradient.addColorStop(ratio, 'rgba(75,192,75,0.4)');
          gradient.addColorStop(ratio, 'rgba(192,75,75,0.4)');
          gradient.addColorStop(1, 'rgba(192,75,75,0.4)');
          this.chart.data.datasets[0].backgroundColor = gradient;

          return Chart.controllers.line.prototype.update.apply(this, arguments);
      }
  });

}

export default {
  name: 'dual-fill-chart',
  initialize
};
<commit_msg>Add border color to dual-fill line chart<commit_after>
// add a component to render a line chart with
// different fill colors for positive & negative
//
// Source: https://stackoverflow.com/a/36941860/211771

export function initialize(/* application */) {
  var Chart = window.Chart;
  Chart.defaults.DualFillLine = Chart.helpers.clone(Chart.defaults.line);
  Chart.controllers.DualFillLine = Chart.controllers.line.extend({
      update: function () {
          // get the min and max values
          // if the min is non-zero, force a zero min
          var min = Math.min.apply(null, [...this.chart.data.datasets[0].data, 0]);
          var max = Math.max.apply(null, this.chart.data.datasets[0].data);
          var yScale = this.getScaleForId(this.getDataset().yAxisID);

          // figure out the pixels for these and the value 0
          var top = yScale.getPixelForValue(max);
          var zero = yScale.getPixelForValue(0);
          var bottom = yScale.getPixelForValue(min);

          // build a gradient that switches color at the 0 point
          var ctx = this.chart.chart.ctx;
          var gradient = ctx.createLinearGradient(0, top, 0, bottom);
          var ratio = Math.min((zero - top) / (bottom - top), 1);
          gradient.addColorStop(0, 'rgba(75,192,75,0.4)');
          gradient.addColorStop(ratio, 'rgba(75,192,75,0.4)');
          gradient.addColorStop(ratio, 'rgba(192,75,75,0.4)');
          gradient.addColorStop(1, 'rgba(192,75,75,0.4)');
          this.chart.data.datasets[0].borderColor = gradient;
          this.chart.data.datasets[0].backgroundColor = gradient;

          return Chart.controllers.line.prototype.update.apply(this, arguments);
      }
  });

}

export default {
  name: 'dual-fill-chart',
  initialize
};
<|endoftext|>


<commit_before>/**
  @module ember-flexberry-gis
*/

import Ember from 'ember';
import BaseMapCommand from './base';

/**
  Go to map-command.
  Moves map to a given geographic point.

  @class GoToMapCommand
  @extends BaseMapCommand
*/
export default BaseMapCommand.extend({
  /**
    Executes map-command.

    @method execute
  */
  _execute(options) {
    this._super(...arguments);

    let point = Ember.get(options, 'point');
    let crs = Ember.get(options, 'crs');
    let xCaption = Ember.get(options, 'xCaption');
    let yCaption = Ember.get(options, 'yCaption');

    let latlng = null;
    latlng = crs.unproject(point);

    let leafletMap = this.get('leafletMap');
    let popupContent = `${xCaption}: ${point.x}; ${yCaption}: ${point.y}`;

    leafletMap.openPopup(popupContent, latlng);
    leafletMap.panTo(latlng);
  }
});
<commit_msg>Update go-to.js<commit_after>/**
  @module ember-flexberry-gis
*/

import Ember from 'ember';
import BaseMapCommand from './base';

/**
  Go to map-command.
  Moves map to a given geographic point.

  @class GoToMapCommand
  @extends BaseMapCommand
*/
export default BaseMapCommand.extend({
  /**
    Executes map-command.

    @method execute
  */
  _execute(options) {
    this._super(...arguments);

    let point = Ember.get(options, 'point');
    let crs = Ember.get(options, 'crs');
    let xCaption = Ember.get(options, 'xCaption');
    let yCaption = Ember.get(options, 'yCaption');

    let latlng = null;
    latlng = crs.unproject(point);

    let leafletMap = this.get('leafletMap');
    let popupContent = `${xCaption}: ${point.x}; ${yCaption}: ${point.y}`;

    leafletMap.openPopup(popupContent, latlng);
    leafletMap.panTo(latlng);
  }
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';
import LFAdapter from 'ember-localforage-adapter/adapters/localforage';
import LFSerializer from 'ember-localforage-adapter/serializers/localforage';
import generateUniqueId from '../utils/generate-unique-id';

/**
 * @module ember-flexberry-offline
 */

/**
 * Store that used in offline mode.
 *
 *
 * @class LocalStore
 * @extends DS.Store
 */
export default DS.Store.extend({
  init: function() {
    let owner = Ember.getOwner(this);
    let localStore = this;
    var serializer = LFSerializer.extend(DS.EmbeddedRecordsMixin, {
      /**
       `serializeBelongsTo` can be used to customize how `DS.belongsTo` properties are serialized.
       If there is set option `odata-id` at serializer and `DS.belongsTo` property is not null,
       then property will be serialized like `ids`.

       @method serializeBelongsTo
       @param {DS.Snapshot} snapshot
       @param {Object} json
       @param {Object} relationship
      */
      serializeBelongsTo(snapshot, json, relationship) {
        var option = this.attrsOption(relationship.key);
        if (!option || option.serialize !== 'odata-id') {
          this._super(snapshot, json, relationship);
          return;
        }

        var key = relationship.key;
        var belongsToId = snapshot.belongsTo(key, { id: true });
        var payloadKey = this.keyForRelationship(key, relationship.kind, 'serialize');
        if (Ember.isNone(belongsToId)) {
          json[payloadKey] = null;
        } else {
          json[payloadKey] = belongsToId;
        }
      }
    }).create(owner.ownerInjection(), {
      store: localStore
    });

    var adapter = LFAdapter
      .extend({
        init() {
          this._super(...arguments);
          window.localforage.setDriver(window.localforage.INDEXEDDB);
        },
        generateIdForRecord: generateUniqueId,
        getPaginationQuery: function(page, perPage) {
          let query = {};
          return query;
        },

        getSortingQuery: function(sortingInfo, serializer) {
          let query = {};

          return query;
        },

        getLimitFunctionQuery: function(limitFunction, projectionName) {
          let query = {};

          if (projectionName && typeof (projectionName) === 'string' && projectionName.length > 0) {
            Ember.merge(query, { projection: projectionName });
          }

          return query;
        },
      })
      .create(owner.ownerInjection(), {
        caching: 'none',
        namespace: 'ember-flexberry-offline:store',
        serializer: serializer,
        clear: clearLFAdapter
      });

    this.set('adapter', adapter);

    this._super(...arguments);
  },

  /**
   * Serializer is fetched via this method or adapter.serializer
   *
   * @method serializerFor
   * @public
   */
  serializerFor: function() {
    return this.get('adapter.serializer');
  },

  /**
   * Adapter is fetched via this method or adapter property
   *
   * @method adapterFor
   * @public
   */
  adapterFor: function() {
    return this.get('adapter');
  },

  /**
   * Finds the records for the given model type.
   *
   * See {{#crossLink "DS.Store/findAll:method"}}{{/crossLink}} for details.
   *
   * @method findAll
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} [options] Options.
   * @param {String} options.projection Projection name.
   * @return {DS.AdapterPopulatedRecordArray} Records promise.
   */
  findAll: function(modelName, options) {
    if (options && options.projection) {
      return this.query(modelName, {
        projection: options.projection
      });
    }

    return this._super(...arguments);
  },

  /**
   * Returns a record for a given type and id combination.
   *
   * See {{#crossLink "DS.Store/findRecord:method"}}{{/crossLink}} for details.
   *
   * @method findRecord
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {String|Integer} id Record ID.
   * @param {Object} [options] Options.
   * @param {String} options.projection Projection name.
   * @return {Promise} Record promise.
   */
  findRecord: function(modelName, id, options) {
    if (options && options.projection) {
      // TODO: case of options.reload === false.
      return this.queryRecord(modelName, {
        id: id,
        projection: options.projection
      });
    }

    return this._super(...arguments);
  },

  /**
   * This method delegates a query to the adapter.
   *
   * See {{#crossLink "DS.Store/query:method"}}{{/crossLink}} for details.
   *
   * @method query
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} query An opaque query to be used by the adapter.
   * @param {String} [query.projection] Projection name.
   * @return {Promise} A promise, which is resolved with a
   *                   {{#crossLink "DS.RecordArray"}}RecordArray{{/crossLink}}
   *                   once the server returns.
   */
  query: function(modelName, query) {
    var proj = this._extractProjectionFromQuery(modelName, query);
    var _this = this;
    return this._super(modelName, query).then(function(recordArray) {
      let promises = Ember.A();
      for (let i = 0; i < recordArray.get('length'); i++) {
        let record = recordArray.objectAt(i);
        promises.pushObject(_this._completeLoadRecord(record, proj));
      }

      return Ember.RSVP.all(promises).then(() => {
        return recordArray;
      });
    }).then(function(completeRecordArray) {
      return completeRecordArray;
    });
  },

  /**
   * This method delegates a query to the adapter.
   *
   * See {{#crossLink "DS.Store/queryRecord:method"}}{{/crossLink}} for details.
   *
   * @method queryRecord
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} query An opaque query to be used by the adapter.
   * @param {String} [query.projection] Projection name.
   * @return {Promise} A promise, which is resolved with a
   *                   {{#crossLink "DS.RecordObject"}}RecordObject{{/crossLink}}
   *                   once the server returns.
   */
  queryRecord: function(modelName, query) {
    var proj = this._extractProjectionFromQuery(modelName, query);
    var _this = this;
    return this._super(modelName, query).then(function(record) {
      return _this._completeLoadRecord(record, proj);
    }).then(function(completeRecord) {
      return completeRecord;
    });
  },

  /**
   * Retrieves projection from query and returns it.
   * Retrieved projection removes from the query.
   *
   * @method _extractProjectionFromQuery
   * @private
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} [query] Query parameters.
   * @param {String} query.projection Projection name.
   * @return {Object} Extracted projection from query or null
   *                  if projection is not set in query.
   */
  _extractProjectionFromQuery: function(modelName, query) {
    if (query && query.projection) {
      let proj = query.projection;
      if (typeof query.projection === 'string') {
        let projName = query.projection;
        let typeClass = this.modelFor(modelName);
        proj = typeClass.projections.get(projName);
      }

      delete query.projection;
      return proj;
    }

    return null;
  },

  /**
   * Completes loading record for given projection.
   *
   * @method _completeLoadingRecord
   * @private
   *
   * @param {Object} record Main record loaded from local store.
   * @param {Object} projection Projection for complete loading of record.
   * @return {Object} Completely loaded record with all properties
   *                  include relationships corresponds to given projection
   */
  _completeLoadRecord: function(record, projection) {
    function loadRelatedRecord(mainRecord, id, proj) {
      let relatedRecord = this.peekRecord(proj.modelName, id);
      if (Ember.isNone(relatedRecord)) {
        let options = { projection: proj };
        return this.findRecord(proj.modelName, id, options);
      }
      else {
        return _completeLoadRecord(relatedRecord, proj);
      }
    }

    let promises = Ember.A();
    let attributes = projection.attributes;
    for (var attrName in attributes) {
      if (attributes.hasOwnProperty(attrName)) {
        let attr = attributes[attrName];
        switch (attr.kind) {
          case 'attr':
            break;
          case 'belongsTo':
              let id = record[attrName];
              promises.pushObject(loadRelatedRecord.call(this, record, id, attr).then((relatedRecord) => {
                delete record[attrName];
                record[attrName] = relatedRecord;
              }));
            break;
          case 'hasMany':
            let ids = Ember.copy(record[attrName], true);
            let hasManyRecords = DS.ManyArray().create();
            delete record[attrName];
            record[attrName] = hasManyRecords;
            for (let i = 0; i < ids.length; i++) {
              promises.pushObject(loadRelatedRecord.call(this, record, id, attr).then((relatedRecord) => {
                hasManyRecords.addObject(relatedRecord);
              }));
            }

            break;
          default:
            throw new Error(`Unknown kind of projection attribute: ${attr.kind}`);
        }
      }
    }

    return Ember.RSVP.all(promises).then(() => {
      return record;
    });
  }
});

function clearLFAdapter() {
  // clear cache
  var cache = this.get('cache');
  if(cache) {
    cache.clear();
  }

  // clear data in localforage
  return window.localforage.setItem(this._adapterNamespace(), []);
}
<commit_msg>Fix get of id for hasMany relationship<commit_after>import Ember from 'ember';
import DS from 'ember-data';
import LFAdapter from 'ember-localforage-adapter/adapters/localforage';
import LFSerializer from 'ember-localforage-adapter/serializers/localforage';
import generateUniqueId from '../utils/generate-unique-id';

/**
 * @module ember-flexberry-offline
 */

/**
 * Store that used in offline mode.
 *
 *
 * @class LocalStore
 * @extends DS.Store
 */
export default DS.Store.extend({
  init: function() {
    let owner = Ember.getOwner(this);
    let localStore = this;
    var serializer = LFSerializer.extend(DS.EmbeddedRecordsMixin, {
      /**
       `serializeBelongsTo` can be used to customize how `DS.belongsTo` properties are serialized.
       If there is set option `odata-id` at serializer and `DS.belongsTo` property is not null,
       then property will be serialized like `ids`.

       @method serializeBelongsTo
       @param {DS.Snapshot} snapshot
       @param {Object} json
       @param {Object} relationship
      */
      serializeBelongsTo(snapshot, json, relationship) {
        var option = this.attrsOption(relationship.key);
        if (!option || option.serialize !== 'odata-id') {
          this._super(snapshot, json, relationship);
          return;
        }

        var key = relationship.key;
        var belongsToId = snapshot.belongsTo(key, { id: true });
        var payloadKey = this.keyForRelationship(key, relationship.kind, 'serialize');
        if (Ember.isNone(belongsToId)) {
          json[payloadKey] = null;
        } else {
          json[payloadKey] = belongsToId;
        }
      }
    }).create(owner.ownerInjection(), {
      store: localStore
    });

    var adapter = LFAdapter
      .extend({
        init() {
          this._super(...arguments);
          window.localforage.setDriver(window.localforage.INDEXEDDB);
        },
        generateIdForRecord: generateUniqueId,
        getPaginationQuery: function(page, perPage) {
          let query = {};
          return query;
        },

        getSortingQuery: function(sortingInfo, serializer) {
          let query = {};

          return query;
        },

        getLimitFunctionQuery: function(limitFunction, projectionName) {
          let query = {};

          if (projectionName && typeof (projectionName) === 'string' && projectionName.length > 0) {
            Ember.merge(query, { projection: projectionName });
          }

          return query;
        },
      })
      .create(owner.ownerInjection(), {
        caching: 'none',
        namespace: 'ember-flexberry-offline:store',
        serializer: serializer,
        clear: clearLFAdapter
      });

    this.set('adapter', adapter);

    this._super(...arguments);
  },

  /**
   * Serializer is fetched via this method or adapter.serializer
   *
   * @method serializerFor
   * @public
   */
  serializerFor: function() {
    return this.get('adapter.serializer');
  },

  /**
   * Adapter is fetched via this method or adapter property
   *
   * @method adapterFor
   * @public
   */
  adapterFor: function() {
    return this.get('adapter');
  },

  /**
   * Finds the records for the given model type.
   *
   * See {{#crossLink "DS.Store/findAll:method"}}{{/crossLink}} for details.
   *
   * @method findAll
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} [options] Options.
   * @param {String} options.projection Projection name.
   * @return {DS.AdapterPopulatedRecordArray} Records promise.
   */
  findAll: function(modelName, options) {
    if (options && options.projection) {
      return this.query(modelName, {
        projection: options.projection
      });
    }

    return this._super(...arguments);
  },

  /**
   * Returns a record for a given type and id combination.
   *
   * See {{#crossLink "DS.Store/findRecord:method"}}{{/crossLink}} for details.
   *
   * @method findRecord
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {String|Integer} id Record ID.
   * @param {Object} [options] Options.
   * @param {String} options.projection Projection name.
   * @return {Promise} Record promise.
   */
  findRecord: function(modelName, id, options) {
    if (options && options.projection) {
      // TODO: case of options.reload === false.
      return this.queryRecord(modelName, {
        id: id,
        projection: options.projection
      });
    }

    return this._super(...arguments);
  },

  /**
   * This method delegates a query to the adapter.
   *
   * See {{#crossLink "DS.Store/query:method"}}{{/crossLink}} for details.
   *
   * @method query
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} query An opaque query to be used by the adapter.
   * @param {String} [query.projection] Projection name.
   * @return {Promise} A promise, which is resolved with a
   *                   {{#crossLink "DS.RecordArray"}}RecordArray{{/crossLink}}
   *                   once the server returns.
   */
  query: function(modelName, query) {
    var proj = this._extractProjectionFromQuery(modelName, query);
    var _this = this;
    return this._super(modelName, query).then(function(recordArray) {
      let promises = Ember.A();
      for (let i = 0; i < recordArray.get('length'); i++) {
        let record = recordArray.objectAt(i);
        promises.pushObject(_this._completeLoadRecord(record, proj));
      }

      return Ember.RSVP.all(promises).then(() => {
        return recordArray;
      });
    }).then(function(completeRecordArray) {
      return completeRecordArray;
    });
  },

  /**
   * This method delegates a query to the adapter.
   *
   * See {{#crossLink "DS.Store/queryRecord:method"}}{{/crossLink}} for details.
   *
   * @method queryRecord
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} query An opaque query to be used by the adapter.
   * @param {String} [query.projection] Projection name.
   * @return {Promise} A promise, which is resolved with a
   *                   {{#crossLink "DS.RecordObject"}}RecordObject{{/crossLink}}
   *                   once the server returns.
   */
  queryRecord: function(modelName, query) {
    var proj = this._extractProjectionFromQuery(modelName, query);
    var _this = this;
    return this._super(modelName, query).then(function(record) {
      return _this._completeLoadRecord(record, proj);
    }).then(function(completeRecord) {
      return completeRecord;
    });
  },

  /**
   * Retrieves projection from query and returns it.
   * Retrieved projection removes from the query.
   *
   * @method _extractProjectionFromQuery
   * @private
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} [query] Query parameters.
   * @param {String} query.projection Projection name.
   * @return {Object} Extracted projection from query or null
   *                  if projection is not set in query.
   */
  _extractProjectionFromQuery: function(modelName, query) {
    if (query && query.projection) {
      let proj = query.projection;
      if (typeof query.projection === 'string') {
        let projName = query.projection;
        let typeClass = this.modelFor(modelName);
        proj = typeClass.projections.get(projName);
      }

      delete query.projection;
      return proj;
    }

    return null;
  },

  /**
   * Completes loading record for given projection.
   *
   * @method _completeLoadingRecord
   * @private
   *
   * @param {Object} record Main record loaded from local store.
   * @param {Object} projection Projection for complete loading of record.
   * @return {Object} Completely loaded record with all properties
   *                  include relationships corresponds to given projection
   */
  _completeLoadRecord: function(record, projection) {
    function loadRelatedRecord(mainRecord, id, proj) {
      let relatedRecord = this.peekRecord(proj.modelName, id);
      if (Ember.isNone(relatedRecord)) {
        let options = { projection: proj };
        return this.findRecord(proj.modelName, id, options);
      }
      else {
        return _completeLoadRecord(relatedRecord, proj);
      }
    }

    let promises = Ember.A();
    let attributes = projection.attributes;
    for (var attrName in attributes) {
      if (attributes.hasOwnProperty(attrName)) {
        let attr = attributes[attrName];
        switch (attr.kind) {
          case 'attr':
            break;
          case 'belongsTo':
            let id = record[attrName];
            promises.pushObject(loadRelatedRecord.call(this, record, id, attr).then((relatedRecord) => {
              delete record[attrName];
              record[attrName] = relatedRecord;
            }));
            break;
          case 'hasMany':
            let ids = Ember.copy(record[attrName], true);
            let hasManyRecords = DS.ManyArray().create();
            delete record[attrName];
            record[attrName] = hasManyRecords;
            for (let i = 0; i < ids.length; i++) {
              let id = ids[i];
              promises.pushObject(loadRelatedRecord.call(this, record, id, attr).then((relatedRecord) => {
                hasManyRecords.addObject(relatedRecord);
              }));
            }

            break;
          default:
            throw new Error(`Unknown kind of projection attribute: ${attr.kind}`);
        }
      }
    }

    return Ember.RSVP.all(promises).then(() => {
      return record;
    });
  }
});

function clearLFAdapter() {
  // clear cache
  var cache = this.get('cache');
  if(cache) {
    cache.clear();
  }

  // clear data in localforage
  return window.localforage.setItem(this._adapterNamespace(), []);
}
<|endoftext|>


<commit_before>/**
  @module ember-flexberry-data
*/

import Ember from 'ember';
import DS from 'ember-data';
import OfflineAdapter from '../adapters/offline';
import QueryBuilder from '../query/builder';

/**
  Store that used in offline mode by default.

  @class LocalStore
  @namespace Offline
  @extends DS.Store
  @private
*/
export default DS.Store.extend({
  /**
    Database name for IndexedDB.

    @property dbName
    @type String
    @default 'ember-flexberry-data'
  */
  dbName: Ember.computed({
    get() {
      return this.get('adapter.dbName');
    },
    set(key, value) {
      return this.set('adapter.dbName', value);
    },
  }),

  /**
    Initializing instance.
    [More info](http://emberjs.com/api/data/classes/DS.Store.html#method_init).

    @method init
  */
  init() {
    this._super(...arguments);
    let dbName = this.get('dbName');
    let owner = Ember.getOwner(this);
    this.set('adapter', OfflineAdapter.create(owner.ownerInjection(), dbName ? { dbName } : {}));
  },

  /**
   * Returns an instance of the serializer for a given type.
   * Offline serializers should have name with postfix '-offline'.
   *
   * @method serializerFor
   * @param {String} modelName The name of the model type.
   * @public
   */
  serializerFor: function(modelName) {
    let owner = Ember.getOwner(this);
    let serializer = owner.lookup(`serializer:${modelName}-offline`);
    if (!serializer) {
      serializer = owner.lookup(`serializer:application-offline`);
      if (!serializer) {
        serializer = this.adapterFor(modelName).defaultSerializer;
      }
    }

    return serializer;
  },

  /**
   * Returns an instance of the adapter for a given type.
   * Offline adapters should have name with postfix '-offline'.
   *
   * @method adapterFor
   * @param {String} modelName The name of the model type.
   * @public
   */
  adapterFor: function(modelName) {
    let owner = Ember.getOwner(this);
    let adapter = owner.lookup(`adapter:${modelName}-offline`);
    if (!adapter) {
      adapter = owner.lookup(`adapter:application-offline`);
      if (!adapter) {
        adapter = this.get('adapter');
      }
    }

    return adapter;
  },

  /**
   * Finds the records for the given model type.
   *
   * See {{#crossLink "DS.Store/findAll:method"}}{{/crossLink}} for details.
   *
   * @method findAll
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} [options] Options.
   * @param {String} options.projection Projection name.
   * @return {DS.AdapterPopulatedRecordArray} Records promise.
   */
  findAll: function(modelName, options) {
    Ember.Logger.debug(`Flexberry Local Store::findAll ${modelName}`);

    let builder = new QueryBuilder(this, modelName);

    if (options && options.projection) {
      Ember.Logger.debug(`Flexberry Local Store::findAll using projection '${options.projection}'`);

      builder.selectByProjection(options.projection);
    }

    return this.query(modelName, builder.build());
  },

  /**
   * Returns a record for a given type and id combination.
   *
   * See {{#crossLink "DS.Store/findRecord:method"}}{{/crossLink}} for details.
   *
   * @method findRecord
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {String|Integer} id Record ID.
   * @param {Object} [options] Options.
   * @param {String} options.projection Projection name.
   * @return {Promise} Record promise.
   */
  findRecord: function(modelName, id, options) {
    // TODO: case of options.reload === false.
    Ember.Logger.debug(`Flexberry Local Store::findRecord ${modelName}(${id})`);
    let builder = new QueryBuilder(this, modelName).byId(id);

    if (options && options.projection) {
      Ember.Logger.debug(`Flexberry Local Store::findRecord using projection '${options.projection}'`);

      builder.selectByProjection(options.projection);
    }

    return this.queryRecord(modelName, builder.build());
  },

  /**
   * This method delegates a query to the adapter.
   *
   * See {{#crossLink "DS.Store/query:method"}}{{/crossLink}} for details.
   *
   * @method query
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} query An opaque query to be used by the adapter.
   * @param {String} [query.projection] Projection name.
   * @return {Promise} A promise, which is resolved with a
   *                   {{#crossLink "DS.RecordArray"}}RecordArray{{/crossLink}}
   *                   once the server returns.
   */
  query: function(modelName, query) {
    Ember.Logger.debug(`Flexberry Local Store::query ${modelName}`, query);

    return this._super(...arguments);
  },

  /**
   * This method delegates a query to the adapter.
   *
   * See {{#crossLink "DS.Store/queryRecord:method"}}{{/crossLink}} for details.
   *
   * @method queryRecord
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} query An opaque query to be used by the adapter.
   * @param {String} [query.projection] Projection name.
   * @return {Promise} A promise, which is resolved with a
   *                   {{#crossLink "DS.RecordObject"}}RecordObject{{/crossLink}}
   *                   once the server returns.
   */
  queryRecord: function(modelName, query) {
    Ember.Logger.debug(`Flexberry Local Store::queryRecord ${modelName}`, query);

    return this._super(...arguments);
  },
});
<commit_msg>Fix local store<commit_after>/**
  @module ember-flexberry-data
*/

import Ember from 'ember';
import DS from 'ember-data';
import OfflineAdapter from '../adapters/offline';
import QueryBuilder from '../query/builder';

/**
  Store that used in offline mode by default.

  @class LocalStore
  @namespace Offline
  @extends DS.Store
  @private
*/
export default DS.Store.extend({
  /**
    Database name for IndexedDB.

    @property dbName
    @type String
    @default 'ember-flexberry-data'
  */
  dbName: Ember.computed({
    get() {
      return this.get('adapter.dbName');
    },
    set(key, value) {
      return this.set('adapter.dbName', value);
    },
  }),

  /**
    Initializing instance.
    [More info](http://emberjs.com/api/data/classes/DS.Store.html#method_init).

    @method init
  */
  init() {
    this._super(...arguments);
    let dbName = this.get('dbName');
    let owner = Ember.getOwner(this);
    this.set('adapter', OfflineAdapter.create(owner.ownerInjection(), dbName ? { dbName } : {}));
  },

  /**
   * Returns an instance of the serializer for a given type.
   * Offline serializers should have name with postfix '-offline'.
   *
   * @method serializerFor
   * @param {String} modelName The name of the model type.
   * @public
   */
  serializerFor: function(modelName) {
    let owner = Ember.getOwner(this);
    let serializer = owner.lookup(`serializer:${modelName}-offline`);
    if (!serializer) {
      serializer = owner.lookup(`serializer:application-offline`);
      if (!serializer) {
        serializer = this.adapterFor(modelName).defaultSerializer;
      }
    }

    return serializer;
  },

  /**
   * Returns an instance of the adapter for a given type.
   * Offline adapters should have name with postfix '-offline'.
   *
   * @method adapterFor
   * @param {String} modelName The name of the model type.
   * @public
   */
  adapterFor: function(modelName) {
    let owner = Ember.getOwner(this);
    let adapter = owner.lookup(`adapter:${modelName}-offline`);
    if (!adapter) {
      adapter = owner.lookup(`adapter:application-offline`);
      if (!adapter) {
        adapter = this.get('adapter');
      }
    }

    return adapter;
  },

  /**
   * Finds the records for the given model type.
   *
   * See {{#crossLink "DS.Store/findAll:method"}}{{/crossLink}} for details.
   *
   * @method findAll
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} [options] Options.
   * @param {String} options.projection Projection name.
   * @return {DS.AdapterPopulatedRecordArray} Records promise.
   */
  findAll: function(modelName, options) {
    Ember.Logger.debug(`Flexberry Local Store::findAll ${modelName}`);

    if (options && options.projection) {
      Ember.Logger.debug(`Flexberry Local Store::findAll using projection '${options.projection}'`);

      let builder = new QueryBuilder(this, modelName);
      builder.selectByProjection(options.projection);
      return this.query(modelName, builder.build());
    }

    return this._super(...arguments);
  },

  /**
   * Returns a record for a given type and id combination.
   *
   * See {{#crossLink "DS.Store/findRecord:method"}}{{/crossLink}} for details.
   *
   * @method findRecord
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {String|Integer} id Record ID.
   * @param {Object} [options] Options.
   * @param {String} options.projection Projection name.
   * @return {Promise} Record promise.
   */
  findRecord: function(modelName, id, options) {
    // TODO: case of options.reload === false.
    Ember.Logger.debug(`Flexberry Local Store::findRecord ${modelName}(${id})`);

    let builder = new QueryBuilder(this, modelName).byId(id);
    if (options && options.projection) {
      Ember.Logger.debug(`Flexberry Local Store::findRecord using projection '${options.projection}'`);

      builder.selectByProjection(options.projection);
      return this.queryRecord(modelName, builder.build());
    }

    let queryObject = builder.build();

    // Now if projection is not specified then only 'id' field will be selected.
    queryObject.select = [];
    return this.queryRecord(modelName, queryObject);
  },

  /**
   * This method delegates a query to the adapter.
   *
   * See {{#crossLink "DS.Store/query:method"}}{{/crossLink}} for details.
   *
   * @method query
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} query An opaque query to be used by the adapter.
   * @param {String} [query.projection] Projection name.
   * @return {Promise} A promise, which is resolved with a
   *                   {{#crossLink "DS.RecordArray"}}RecordArray{{/crossLink}}
   *                   once the server returns.
   */
  query: function(modelName, query) {
    Ember.Logger.debug(`Flexberry Local Store::query ${modelName}`, query);

    return this._super(...arguments);
  },

  /**
   * This method delegates a query to the adapter.
   *
   * See {{#crossLink "DS.Store/queryRecord:method"}}{{/crossLink}} for details.
   *
   * @method queryRecord
   * @public
   *
   * @param {String} modelName The name of the model type.
   * @param {Object} query An opaque query to be used by the adapter.
   * @param {String} [query.projection] Projection name.
   * @return {Promise} A promise, which is resolved with a
   *                   {{#crossLink "DS.RecordObject"}}RecordObject{{/crossLink}}
   *                   once the server returns.
   */
  queryRecord: function(modelName, query) {
    Ember.Logger.debug(`Flexberry Local Store::queryRecord ${modelName}`, query);

    return this._super(...arguments);
  },
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import arraySwap from 'ember-redirect/utils/array-swap';
import { lookup, register } from 'ember-redirect/utils/container';

function getDynamicSegments(segments) {
  return segments
    .filter(item => item.type === 1)
    .map(item => item.value);
}

export default function(routeName, options, instance) {
  let routeContainerKey = `route:${routeName}`;
  let routeObject       = lookup(instance, routeContainerKey);

  if (!routeObject) {
    routeObject = Ember.Route.extend({});
    register(instance, routeContainerKey, routeObject, { singleton: false });
  }

  routeObject.reopen({
    beforeModel(transition) {
      let newDynObject       = {};
      let thisRouteName      = this.routeName;
      let routeNames         = this.router.router.recognizer.names;
      let dynSegsOfNextRoute = getDynamicSegments(routeNames[options.redirect].segments);
      let dynSegsOfThisRoute = getDynamicSegments(routeNames[thisRouteName].segments);

      // Make sure we only try to make a redirect at the most nested
      // route and not a parent resource.
      if(this.routeName !== transition.targetName) {
        return false;
      }

      // Make sure that the lengths are the same else we are trying to transition to a route that needs more
      // segments then we can supply.
      if(dynSegsOfNextRoute.length <= dynSegsOfThisRoute.length) {

        dynSegsOfNextRoute.forEach(function(item, index) {
          // This means that we have the same dynamic segment on both this and the next route so we will pair them together.
          if(dynSegsOfThisRoute.indexOf(dynSegsOfNextRoute[index]) !== -1) {
            newDynObject[dynSegsOfNextRoute[index]] = transition.params[thisRouteName][dynSegsOfNextRoute[index]];
            dynSegsOfThisRoute = arraySwap(dynSegsOfThisRoute, index, dynSegsOfThisRoute.indexOf(dynSegsOfNextRoute[index]));
          }
          else {
            newDynObject[dynSegsOfNextRoute[index]] = transition.params[thisRouteName][dynSegsOfThisRoute[index]];
          }
        });

        // Pass along query params.
        newDynObject['queryParams'] = transition.queryParams;

        this.replaceWith(transition.router.recognizer.generate(options.redirect, newDynObject));
      }

      return this._super(...arguments);
    }
  });

  return routeObject;
}
<commit_msg>Fix Ember 2.8 support<commit_after>import Ember from 'ember';
import arraySwap from 'ember-redirect/utils/array-swap';
import { lookup, register } from 'ember-redirect/utils/container';

/**
 * - `type` and `value` are in Ember > v2.8
 * - `name` is in Ember === 2.8
 */
function getDynamicSegments(segments) {
  return segments
    .filter(item => item.type === 1 || !!item.name)
    .map(item => item.value || item.name);
}

export default function(routeName, options, instance) {
  let routeContainerKey = `route:${routeName}`;
  let routeObject       = lookup(instance, routeContainerKey);

  if (!routeObject) {
    routeObject = Ember.Route.extend({});
    register(instance, routeContainerKey, routeObject, { singleton: false });
  }

  routeObject.reopen({
    beforeModel(transition) {
      let newDynObject       = {};
      let thisRouteName      = this.routeName;
      let routeNames         = this.router.router.recognizer.names;
      let dynSegsOfNextRoute = getDynamicSegments(routeNames[options.redirect].segments);
      let dynSegsOfThisRoute = getDynamicSegments(routeNames[thisRouteName].segments);

      // Make sure we only try to make a redirect at the most nested
      // route and not a parent resource.
      if(this.routeName !== transition.targetName) {
        return false;
      }

      // Make sure that the lengths are the same else we are trying to transition to a route that needs more
      // segments then we can supply.
      if(dynSegsOfNextRoute.length <= dynSegsOfThisRoute.length) {

        dynSegsOfNextRoute.forEach(function(item, index) {
          // This means that we have the same dynamic segment on both this and the next route so we will pair them together.
          if(dynSegsOfThisRoute.indexOf(dynSegsOfNextRoute[index]) !== -1) {
            newDynObject[dynSegsOfNextRoute[index]] = transition.params[thisRouteName][dynSegsOfNextRoute[index]];
            dynSegsOfThisRoute = arraySwap(dynSegsOfThisRoute, index, dynSegsOfThisRoute.indexOf(dynSegsOfNextRoute[index]));
          }
          else {
            newDynObject[dynSegsOfNextRoute[index]] = transition.params[thisRouteName][dynSegsOfThisRoute[index]];
          }
        });

        // Pass along query params.
        newDynObject['queryParams'] = transition.queryParams;

        this.replaceWith(transition.router.recognizer.generate(options.redirect, newDynObject));
      }

      return this._super(...arguments);
    }
  });

  return routeObject;
}
<|endoftext|>


<commit_before>/*!
    Copyright (C) 2016 Google Inc.
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
*/

(function (can, $) {
  /*  GGRC.SaveQueue
   *
   *  SaveQueue is used by CMS.Models.Cacheable to prevent firing
   *  multiple requests to the server at once. It makes sure the requests
   *  are grouped together (inside _queue) and then resolved in batches.
   *
   *  It will also try to group POST request and use the custom collection post
   *  API and then redistribute responses in order to trace in latency for
   *  throughput. This is done by a "thread" (of timeouts) per object type (per
   *  bucket) that enqueues as a regular request but then greedily dispatches
   *  requests that arrived while it was in the queue.
   *
   *  enqueue(obj: CMS.Models.Cacheable, save_args) -> null
   */
  can.Construct('GGRC.SaveQueue', {

    DELAY: 100, // Number of ms to wait before the first batch is fired
    BATCH: GGRC.config.MAX_INSTANCES || 3, // Maximum number of POST/PUT requests at any given time
    BATCH_SIZE: 1000,
    _queue: [],
    _buckets: {},
    _timeout: null,

    _enqueue_bucket: function (bucket) {
      var that = this;
      return function () {
        var size = bucket.background ? bucket.objs.length : that.BATCH_SIZE;
        var objs = bucket.objs.splice(0, size);
        var body = _.map(objs, function (obj) {
          var list = {};
          list[bucket.type] = obj.serialize();
          return list;
        });
        var dfd = $.ajax({
          type: 'POST',
          url: '/api/' + bucket.plural,
          data: body,
          beforeSend: function (xhr) {
            if (bucket.background) {
              xhr.setRequestHeader('X-GGRC-BackgroundTask', 'true');
            }
          }
        }).promise();
        dfd.always(function (data, type) {
          if (type === 'error') {
            data = data.responseJSON;
            if (_.isUndefined(data)) {
              return;
            }
          }
          if ('background_task' in data) {
            return CMS.Models.BackgroundTask.findOne({
              id: data.background_task.id
            }).then(function (task) {
              // Resolve all the dfds with the task
              can.each(objs, function (obj) {
                obj._dfd.resolve(task);
              });
            });
          }

          // Push the response to a queue for later processing.
          bucket.save_responses.push([objs, data]);
        }).always(function () {
          if (bucket.objs.length) {
            that._step(that._enqueue_bucket(bucket));
          } else {
            // Process all of the batches of save responses.
            that._process_save_responses(bucket);
            bucket.in_flight = false;
          }
        });

        return dfd;
      };
    },

    _process_save_responses: function (bucket) {
      can.each(bucket.save_responses, function (resp) {
        var objs = resp[0];
        var data = resp[1];
        var cb = function (single) {
          return function () {
            this.created(single[1][bucket.type]);
            return $.when(
              can.Model.Cacheable.resolve_deferred_bindings(this));
          };
        };
        can.each(objs, function (obj, idx) {
          var single = data[idx];
          if (single[0] >= 200 && single[0] < 300) {
            obj._save(cb(single));
          } else {
            obj._dfd.reject(obj, single);
          }
        });
      });

      bucket.save_responses.length = 0;
    },

    _step: function (elem) {
      this._queue.push(elem);
      if (_.isNumber(this._timeout)) {
        clearTimeout(this._timeout);
      }
      this._timeout = setTimeout(function () {
        new GGRC.SaveQueue(this._queue.splice(0, this._queue.length));
      }.bind(this), this.DELAY);
    },

    enqueue: function (obj, args) {
      var type;
      var bucket;
      var bucketName;
      var plural;
      var elem = function () {
        return obj._save.apply(obj, args);
      };
      if (obj.isNew()) {
        type = obj.constructor.table_singular;
        bucketName = type + (obj.run_in_background ? '_bg' : '');
        bucket = this._buckets[bucketName];

        if (_.isUndefined(bucket)) {
          plural = obj.constructor.table_plural;
          bucket = {
            objs: [],
            type: type,
            plural: plural,
            background: obj.run_in_background,
            // List of batch request responses that are yet to be processed.
            save_responses: [],
            in_flight: false // is there a "thread" running for this bucket
          };
          this._buckets[bucketName] = bucket;
        }
        bucket.objs.push(obj);
        if (bucket.in_flight) {
          return;
        }
        elem = this._enqueue_bucket(bucket);
        bucket.in_flight = true;
      }
      this._step(elem);
    }
  }, {
    init: function (queue) {
      this._queue = queue;
      this._resolve();
    },
    _resolve: function () {
      var objs;
      if (!this._queue.length) {
        // Finished
        return;
      }
      objs = this._queue.splice(0, this.constructor.BATCH);
      $.when.apply($, objs.map(function (fn) {
        return fn.apply(this);
      }.bind(this.constructor))).always(this._resolve.bind(this)); // Move on to the next one
    }
  });
})(window.can, window.can.$);
<commit_msg>Add fix JS error to support incorrect data<commit_after>/*!
    Copyright (C) 2016 Google Inc.
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
*/

(function (can, $) {
  /*  GGRC.SaveQueue
   *
   *  SaveQueue is used by CMS.Models.Cacheable to prevent firing
   *  multiple requests to the server at once. It makes sure the requests
   *  are grouped together (inside _queue) and then resolved in batches.
   *
   *  It will also try to group POST request and use the custom collection post
   *  API and then redistribute responses in order to trace in latency for
   *  throughput. This is done by a "thread" (of timeouts) per object type (per
   *  bucket) that enqueues as a regular request but then greedily dispatches
   *  requests that arrived while it was in the queue.
   *
   *  enqueue(obj: CMS.Models.Cacheable, save_args) -> null
   */
  can.Construct('GGRC.SaveQueue', {

    DELAY: 100, // Number of ms to wait before the first batch is fired
    BATCH: GGRC.config.MAX_INSTANCES || 3, // Maximum number of POST/PUT requests at any given time
    BATCH_SIZE: 1000,
    _queue: [],
    _buckets: {},
    _timeout: null,

    _enqueue_bucket: function (bucket) {
      var that = this;
      return function () {
        var size = bucket.background ? bucket.objs.length : that.BATCH_SIZE;
        var objs = bucket.objs.splice(0, size);
        var body = _.map(objs, function (obj) {
          var list = {};
          list[bucket.type] = obj.serialize();
          return list;
        });
        var dfd = $.ajax({
          type: 'POST',
          url: '/api/' + bucket.plural,
          data: body,
          beforeSend: function (xhr) {
            if (bucket.background) {
              xhr.setRequestHeader('X-GGRC-BackgroundTask', 'true');
            }
          }
        }).promise();
        dfd.always(function (data, type) {
          if (type === 'error') {
            data = data.responseJSON;
            if (_.isUndefined(data)) {
              return;
            }
          }
          if ('background_task' in data) {
            return CMS.Models.BackgroundTask.findOne({
              id: data.background_task.id
            }).then(function (task) {
              // Resolve all the dfds with the task
              can.each(objs, function (obj) {
                obj._dfd.resolve(task);
              });
            });
          }

          // Push the response to a queue for later processing.
          bucket.save_responses.push([objs, data]);
        }).always(function () {
          if (bucket.objs.length) {
            that._step(that._enqueue_bucket(bucket));
          } else {
            // Process all of the batches of save responses.
            that._process_save_responses(bucket);
            bucket.in_flight = false;
          }
        });

        return dfd;
      };
    },

    _process_save_responses: function (bucket) {
      can.each(bucket.save_responses, function (resp) {
        var objs = resp[0];
        var data = resp[1];
        var cb = function (single) {
          return function () {
            this.created(single[1][bucket.type]);
            return $.when(
              can.Model.Cacheable.resolve_deferred_bindings(this));
          };
        };
        can.each(objs, function (obj, idx) {
          var single = data[idx];
          // Add extra check to avoid possible exceptions
          single = Array.isArray(single) ? single : false;
          if (single && single[0] >= 200 && single[0] < 300) {
            obj._save(cb(single));
          } else {
            obj._dfd.reject(obj, single);
          }
        });
      });

      bucket.save_responses.length = 0;
    },

    _step: function (elem) {
      this._queue.push(elem);
      if (_.isNumber(this._timeout)) {
        clearTimeout(this._timeout);
      }
      this._timeout = setTimeout(function () {
        new GGRC.SaveQueue(this._queue.splice(0, this._queue.length));
      }.bind(this), this.DELAY);
    },

    enqueue: function (obj, args) {
      var type;
      var bucket;
      var bucketName;
      var plural;
      var elem = function () {
        return obj._save.apply(obj, args);
      };
      if (obj.isNew()) {
        type = obj.constructor.table_singular;
        bucketName = type + (obj.run_in_background ? '_bg' : '');
        bucket = this._buckets[bucketName];

        if (_.isUndefined(bucket)) {
          plural = obj.constructor.table_plural;
          bucket = {
            objs: [],
            type: type,
            plural: plural,
            background: obj.run_in_background,
            // List of batch request responses that are yet to be processed.
            save_responses: [],
            in_flight: false // is there a "thread" running for this bucket
          };
          this._buckets[bucketName] = bucket;
        }
        bucket.objs.push(obj);
        if (bucket.in_flight) {
          return;
        }
        elem = this._enqueue_bucket(bucket);
        bucket.in_flight = true;
      }
      this._step(elem);
    }
  }, {
    init: function (queue) {
      this._queue = queue;
      this._resolve();
    },
    _resolve: function () {
      var objs;
      if (!this._queue.length) {
        // Finished
        return;
      }
      objs = this._queue.splice(0, this.constructor.BATCH);
      $.when.apply($, objs.map(function (fn) {
        return fn.apply(this);
      }.bind(this.constructor))).always(this._resolve.bind(this)); // Move on to the next one
    }
  });
})(window.can, window.can.$);
<|endoftext|>


<commit_before>define([
	'goo/renderer/bounds/BoundingBox',
	'goo/entities/components/Component',
	'goo/renderer/MeshData'
],
/** @lends */
function (
	BoundingBox,
	Component,
	MeshData
) {
	'use strict';

	/**
	 * @class Holds the mesh data, like vertices, normals, indices etc. Also defines the local bounding volume.

	 * {@linkplain http://code.gooengine.com/latest/examples/goo/entities/components/MeshDataComponent/MeshDataComponent-example.html Working example}
	 * @param {MeshData} meshData Target mesh data for this component.
	 * @extends Component
	 */
	function MeshDataComponent(meshData) {
		this.type = 'MeshDataComponent';

		/**
		 * @type {MeshData}
		 */
		this.meshData = meshData;

		/** Bounding volume in local space.
		 * @type {BoundingVolume}
		 */
		this.modelBound = new BoundingBox();

		/** Automatically compute bounding fit.
		 * @type {boolean}
		 * @default
		 */
		this.autoCompute = true;

		/**
		 * @type {SkeletonPose}
		 * @default
		 */
		this.currentPose = null; // SkeletonPose
	}

	MeshDataComponent.type = 'MeshDataComponent';

	MeshDataComponent.prototype = Object.create(Component.prototype);
	MeshDataComponent.prototype.constructor = MeshDataComponent;

	/**
	 * Set the bounding volume type (sphere, box etc).
	 *
	 * @param {BoundingVolume} modelBound Bounding to apply to this meshdata component.
	 * @param {boolean} autoCompute If true, automatically compute bounding fit.
	 */
	MeshDataComponent.prototype.setModelBound = function (modelBound, autoCompute) {
		this.modelBound = modelBound;
		this.autoCompute = autoCompute;
	};

	/**
	 * Compute bounding center and bounds for this mesh.
	 */
	MeshDataComponent.prototype.computeBoundFromPoints = function () {
		if (this.autoCompute && this.modelBound !== null && this.meshData) {
			var verts = this.meshData.getAttributeBuffer('POSITION');
			if (verts !== undefined) {
				this.modelBound.computeFromPoints(verts);
				this.autoCompute = false;
			}
		}
	};

	MeshDataComponent.applyOnEntity = function(obj, entity) {
		if (obj instanceof MeshData) {
			var meshDataComponent = new MeshDataComponent(obj);
			entity.setComponent(meshDataComponent);
			return true;
		}
	};

	return MeshDataComponent;
});<commit_msg>minor code fixes<commit_after>define([
	'goo/renderer/bounds/BoundingBox',
	'goo/entities/components/Component',
	'goo/renderer/MeshData'
],
/** @lends */
function (
	BoundingBox,
	Component,
	MeshData
) {
	'use strict';

	/**
	 * @class Holds the mesh data, like vertices, normals, indices etc. Also defines the local bounding volume.

	 * {@linkplain http://code.gooengine.com/latest/examples/goo/entities/components/MeshDataComponent/MeshDataComponent-example.html Working example}
	 * @param {MeshData} meshData Target mesh data for this component.
	 * @extends Component
	 */
	function MeshDataComponent(meshData) {
		this.type = 'MeshDataComponent';

		/**
		 * @type {MeshData}
		 */
		this.meshData = meshData;

		/** Bounding volume in local space.
		 * @type {BoundingVolume}
		 */
		this.modelBound = new BoundingBox();

		/** Automatically compute bounding fit.
		 * @type {boolean}
		 * @default
		 */
		this.autoCompute = true;

		/**
		 * @type {SkeletonPose}
		 * @default
		 */
		this.currentPose = null; // SkeletonPose
	}

	MeshDataComponent.type = 'MeshDataComponent';

	MeshDataComponent.prototype = Object.create(Component.prototype);
	MeshDataComponent.prototype.constructor = MeshDataComponent;

	/**
	 * Set the bounding volume type (sphere, box etc).
	 *
	 * @param {BoundingVolume} modelBound Bounding to apply to this meshdata component.
	 * @param {boolean} autoCompute If true, automatically compute bounding fit.
	 */
	MeshDataComponent.prototype.setModelBound = function (modelBound, autoCompute) {
		this.modelBound = modelBound;
		this.autoCompute = autoCompute;
	};

	/**
	 * Compute bounding center and bounds for this mesh.
	 */
	MeshDataComponent.prototype.computeBoundFromPoints = function () {
		if (this.autoCompute && this.modelBound !== null && this.meshData) {
			var verts = this.meshData.getAttributeBuffer('POSITION');
			if (verts !== undefined) {
				this.modelBound.computeFromPoints(verts);
				this.autoCompute = false;
			}
		}
	};

	MeshDataComponent.applyOnEntity = function (obj, entity) {
		if (obj instanceof MeshData) {
			var meshDataComponent = new MeshDataComponent(obj);
			entity.setComponent(meshDataComponent);
			return true;
		}
	};

	return MeshDataComponent;
});<|endoftext|>


<commit_before>define([
	'goo/entities/systems/System',
	'goo/renderer/bounds/BoundingBox'
],
/** @lends */
function (
	System,
	BoundingBox
) {
	"use strict";

	/**
	 * @class Calculates and updates all boundings on entities with both transform, meshrenderer and meshdata components
	 */
	function BoundingUpdateSystem () {
		System.call(this, 'BoundingUpdateSystem', ['TransformComponent', 'MeshRendererComponent', 'MeshDataComponent']);
		this._worldBound = new BoundingBox();
		this._computeWorldBound = null;
	}

	BoundingUpdateSystem.prototype = Object.create(System.prototype);

	BoundingUpdateSystem.prototype.process = function (entities) {
		for (var i = 0; i < entities.length; i++) {
			var entity = entities[i];
			var meshDataComponent = entity.meshDataComponent;
			var transformComponent = entity.transformComponent;
			var meshRendererComponent = entity.meshRendererComponent;

			if (meshDataComponent.autoCompute) {
				meshDataComponent.computeBoundFromPoints();
				meshRendererComponent.updateBounds(meshDataComponent.modelBound, transformComponent.worldTransform);
			} else if (transformComponent._updated) {
				meshRendererComponent.updateBounds(meshDataComponent.modelBound, transformComponent.worldTransform);
				// meshDataComponent.setDirty(false);
			}
		}
		if (this._computeWorldBound && this._computeWorldBound instanceof Function) {
			if (entities.length === 0) {
				this._computeWorldBound = null;
				return;
			}

			//this._worldBound = new BoundingSphere(new Vector3(0, 0, 0), 0); // optional for including the center of the scene into the world bound
			this._worldBound = entities[0].meshRendererComponent.worldBound.clone();

			for (var i = 1; i < entities.length; i++) {
				var mrc = entities[i].meshRendererComponent;
				this._worldBound = this._worldBound.merge(mrc.worldBound);
			}
			this._computeWorldBound(this._worldBound);
			this._computeWorldBound = null;
		}
	};

	// function named get actually does a set
	BoundingUpdateSystem.prototype.getWorldBound = function (callback) {
		this._computeWorldBound = callback;
	};

	return BoundingUpdateSystem;
});<commit_msg>World bound excluding-particles-quick-fix<commit_after>define([
	'goo/entities/systems/System',
	'goo/renderer/bounds/BoundingBox'
],
/** @lends */
function (
	System,
	BoundingBox
) {
	"use strict";

	/**
	 * @class Calculates and updates all boundings on entities with both transform, meshrenderer and meshdata components
	 */
	function BoundingUpdateSystem () {
		System.call(this, 'BoundingUpdateSystem', ['TransformComponent', 'MeshRendererComponent', 'MeshDataComponent']);
		this._worldBound = new BoundingBox();
		this._computeWorldBound = null;
	}

	BoundingUpdateSystem.prototype = Object.create(System.prototype);

	BoundingUpdateSystem.prototype.process = function (entities) {
		for (var i = 0; i < entities.length; i++) {
			var entity = entities[i];
			var meshDataComponent = entity.meshDataComponent;
			var transformComponent = entity.transformComponent;
			var meshRendererComponent = entity.meshRendererComponent;

			if (meshDataComponent.autoCompute) {
				meshDataComponent.computeBoundFromPoints();
				meshRendererComponent.updateBounds(meshDataComponent.modelBound, transformComponent.worldTransform);
			} else if (transformComponent._updated) {
				meshRendererComponent.updateBounds(meshDataComponent.modelBound, transformComponent.worldTransform);
				// meshDataComponent.setDirty(false);
			}
		}
		if (this._computeWorldBound && this._computeWorldBound instanceof Function) {
			if (entities.length === 0) {
				this._computeWorldBound = null;
				return;
			}

			//this._worldBound = new BoundingSphere(new Vector3(0, 0, 0), 0); // optional for including the center of the scene into the world bound

			// let's presume that the first entity is not a particle system
			// generally we don't want particle systems to end up in our world bound computing since they have huge world bounds and can mess up stuff
			this._worldBound = entities[0].meshRendererComponent.worldBound.clone();

			for (var i = 1; i < entities.length; i++) {
				if (entities[i].particleComponent) {
					var mrc = entities[i].meshRendererComponent;
					this._worldBound = this._worldBound.merge(mrc.worldBound);
				}
			}
			this._computeWorldBound(this._worldBound);
			this._computeWorldBound = null;
		}
	};

	// function named get actually does a set
	BoundingUpdateSystem.prototype.getWorldBound = function (callback) {
		this._computeWorldBound = callback;
	};

	return BoundingUpdateSystem;
});<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import text from 'js/languages';
import { analysisConfig } from 'js/config';
import analysisKeys from 'constants/AnalysisConstants';

const LossGainBadge = (props, context) => {

  const { lossFromSelectIndex, lossToSelectIndex, results } = props;
  const { lossTotal, gainTotal } = results;
  const { language } = context;
  const labelArray = analysisConfig[analysisKeys.TC_LOSS].labels;

  if (results.hasOwnProperty('error')) {
    return (

{results.message}

    );
  } else {

  return (

{text[language].ANALYSIS_TOTAL_LOSS_LABEL}

{labelArray[lossFromSelectIndex]} – {labelArray[lossToSelectIndex]}

        {lossTotal}
        Ha

    );
  } else {

    return (

{text[language].ANALYSIS_TOTAL_LOSS_LABEL}

{labelArray[lossFromSelectIndex]} – {labelArray[lossToSelectIndex]}

          {lossTotal}
          Ha

{text[language].ANALYSIS_TOTAL_GAIN_LABEL}

{text[language].ANALYSIS_TOTAL_GAIN_RANGE}

          {gainTotal}
          Ha

    );
  }
};

LossGainBadge.propTypes = {
  lossFromSelectIndex: PropTypes.number.isRequired,
  lossToSelectIndex: PropTypes.number.isRequired
};

LossGainBadge.contextTypes = {
  language: PropTypes.string.isRequired
};

export { LossGainBadge as default };
<commit_msg>fix duplicate return<commit_after>import React, { PropTypes } from 'react';
import text from 'js/languages';
import { analysisConfig } from 'js/config';
import analysisKeys from 'constants/AnalysisConstants';

const LossGainBadge = (props, context) => {

  const { lossFromSelectIndex, lossToSelectIndex, results } = props;
  const { lossTotal, gainTotal } = results;
  const { language } = context;
  const labelArray = analysisConfig[analysisKeys.TC_LOSS].labels;

  if (results.hasOwnProperty('error')) {
    return (

{results.message}

    );
  } else {

    return (

{text[language].ANALYSIS_TOTAL_LOSS_LABEL}

{labelArray[lossFromSelectIndex]} – {labelArray[lossToSelectIndex]}

          {lossTotal}
          Ha

{text[language].ANALYSIS_TOTAL_GAIN_LABEL}

{text[language].ANALYSIS_TOTAL_GAIN_RANGE}

          {gainTotal}
          Ha

    );
  }
};

LossGainBadge.propTypes = {
  lossFromSelectIndex: PropTypes.number.isRequired,
  lossToSelectIndex: PropTypes.number.isRequired
};

LossGainBadge.contextTypes = {
  language: PropTypes.string.isRequired
};

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