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

import Ember from 'ember';
import DS from 'ember-data';
import isAsync from '../utils/is-async';
import isObject from '../utils/is-object';
import generateUniqueId from '../utils/generate-unique-id';
import IndexeddbAdapter from '../query/indexeddb-adapter';
import QueryObject from '../query/query-object';
import QueryBuilder from '../query/builder';
import FilterOperator from '../query/filter-operator';
import Condition from '../query/condition';
import { SimplePredicate, ComplexPredicate } from '../query/predicate';
import Dexie from 'npm:dexie';

/**
  Default adapter for {{#crossLink "Offline.LocalStore"}}{{/crossLink}}.

  @class Offline
  @namespace Adapter
  @extends DS.Adapter
*/
var OfflineAdapter = DS.Adapter.extend({
  defaultSerializer: 'offline',

  //queue: LFQueue.create(),
  //cache: LFCache.create(),
  //caching: 'model',
  coalesceFindRequests: true,

  /**
    Database name for IndexedDB.

    @property databaseName
    @type String
  */
  databaseName: undefined,

  /*
    Adapter initialization.
  */
  init() {
    this._super(...arguments);
    Ember.assert('Error: database name for IndexedDB is not defined', !Ember.isNone(this.databaseName));
  },

  shouldBackgroundReloadRecord() {
    return false;
  },

  shouldReloadAll() {
    return true;
  },

  /*
    Generate globally unique IDs for records.
  */
  generateIdForRecord: generateUniqueId,

  /**
    Clear adapter's cache and IndexedDB's store.

    @method clear
  */
  clear: function () {
    // clear data in databse
    var db = new Dexie(this.databaseName);
    return db.delete();
  },

  /**
    This is the main entry point into finding records.

    @method findRecord
    @param {DS.Store} store
    @param {DS.Model} type
    @param {Object|String|Integer|null} id
  */
  findRecord(store, type, id) {
    let table = this._getTableForOperationsByModelType(type);
    return table.get(id);
  },

  findAll(store, type) {
    let db = new Dexie(this.databaseName);
    let table = db.table(type.modelName);
    return table.toArray();
  },

  findMany(store, type, ids) {
    let promises = Ember.A();
    let records = Ember.A();
    let addRecord = (record) => {
      records.pushObject(record);
    };

    for (let i = 0; i < ids.length; i++) {
      promises.pushObject(this.findRecord(store, type, ids[i]).then(addRecord));
    }

    return Ember.RSVP.all(promises).then(() => {
      Ember.RSVP.resolve(records.compact());
    }).catch(function(reason) {
      Ember.RSVP.reject(reason);
    });
  },

  queryRecord(store, type, query) {
    return this.query(store, type, query).then(result => new Ember.RSVP.Promise((resolve) => resolve(result[0])));
  },

  /**
    Supports query language objects or queries that look like this:
     {
       <property to query>: <value to match>,
       ...
     }

    (in this case every property added to the query is an "AND" query, not "OR")
  */
  query(store, type, query) {
    let modelName = type.modelName;
    let proj = this._extractProjectionFromQuery(modelName, type, query);
    let originType = null;
    if (query && query.originType) {
      originType = query.originType;

      delete query.originType;
    }

    let indexedDBAdapter = new IndexeddbAdapter(this.databaseName);

    let _this = this;
    let queryForIndexedDBAdapter = query instanceof QueryObject ? query : this._makeQueryObject(store, modelName, query, proj);
    return indexedDBAdapter.query(store, queryForIndexedDBAdapter).then((recordArray) =>
      new Ember.RSVP.Promise((resolve, reject) => {
        let promises = Ember.A();
        for (let i = 0; i < recordArray.length; i++) {
          let record = recordArray[i];
          promises.pushObject(_this._completeLoadRecord(store, type, record, proj, originType));
        }

        Ember.RSVP.all(promises).then(() => {
          resolve(recordArray);
        }).catch((reason) => {
          reject(reason);
        });
      })
    );
  },

  createRecord(store, type, snapshot) {
    return this._updateOrCreate(store, type, snapshot);
  },

  updateRecord(store, type, snapshot) {
    return this._updateOrCreate(store, type, snapshot);
  },

  deleteRecord(store, type, snapshot) {
    let table = this._getTableForOperationsByModelType(type);
    return table.delete(snapshot.id);
  },

  // private

  /**
    Gets [WritableTable](https://github.com/dfahlander/Dexie.js/wiki/WriteableTable) class for given model type.
    Creates [Dexie's schema](https://github.com/dfahlander/Dexie.js/wiki/Version.stores()) that contains only 'id' property for specified model type.

    @method _getTableForOperationsByModelType
    @param {subclass of DS.Model} type Model type.
    @return {WritableTable} Instance of [WritableTable](https://github.com/dfahlander/Dexie.js/wiki/WriteableTable) class for given model type.
    @private
  */
  _getTableForOperationsByModelType(type) {
    let db = new Dexie(this.databaseName);
    let storeSchema = {};
    storeSchema[type.modelName] = 'id';
    db.version(1).stores(storeSchema);
    return db[type.modelName];
  },

  /**
    Gets [WritableTable](https://github.com/dfahlander/Dexie.js/wiki/WriteableTable) class for given snapshot.
    Creates [Dexie's schema](https://github.com/dfahlander/Dexie.js/wiki/Version.stores()) that contains model properties taken from snapshot.

    @method _getTableForOperationsByModelSnapshot
    @param {DS.Snapshot} snapshot Snapshot of model.
    @return {WritableTable} Instance of [WritableTable](https://github.com/dfahlander/Dexie.js/wiki/WriteableTable) class for given snapshot.
    @private
  */
  _getTableForOperationsByModelSnapshot(snapshot) {
    let db = new Dexie(this.databaseName);
    let storeSchema = {};
    storeSchema[snapshot.modelName] = 'id';

    snapshot.eachAttribute(function(name) {
      storeSchema[snapshot.modelName] += ',' + name;
    });

    snapshot.eachRelationship(function(name, relationship) {
      if (relationship.kind === 'hasMany') {
        storeSchema[snapshot.modelName] += ',*' + name;
      } else {
        storeSchema[snapshot.modelName] += ',' + name;
      }
    });

    db.version(1).stores(storeSchema);
    return db[snapshot.modelName];
  },

  _updateOrCreate(store, type, snapshot) {
    const serializer = store.serializerFor(type.modelName);
    const recordHash = serializer.serialize(snapshot, { includeId: true });

    // update(id comes from snapshot) or create(id comes from serialization)
    const id = snapshot.id || recordHash.id;

    let table = this._getTableForOperationsByModelSnapshot(snapshot);
    return table.put(recordHash, id);
  },

  /**
    Makes {{#crossLink "Query.QueryObject"}}{{/crossLink}} out of queries that look like this:
     {
       <property to query>: <value to match>,
       ...
     }.

    @method _makeQueryObject
    @param store Store used for making query
    @param {String} modelName The name of the model type.
    @param {Object} query Query parameters.
    @param {Object|String} [projection] Projection for query.
    @return {QueryObject} Query object for IndexedDB adapter.
    @private
  */
  _makeQueryObject(store, modelName, query, projection) {
    let builder = new QueryBuilder(store, modelName);
    if (projection && isObject(projection) && (projection.projectionName)) {
      builder.selectByProjection(projection.projectionName);
    } else if (projection && typeof projection === 'string') {
      builder.selectByProjection(projection);
    }

    let predicates = [];
    for (let property in query) {
      const queryValue = query[property];

      // I suppose it's possible to encounter problems when queryValue will have 'Date' type...
      predicates.push(new SimplePredicate(property, FilterOperator.Eq, queryValue.toString()));
    }

    if (predicates.length === 1) {
      builder.where(predicates[0]);
    } else if (predicates.length > 1) {
      let cp = new ComplexPredicate(Condition.And, predicates[0], predicates[1]);
      for (let i = 2; i < predicates.length; i++) {
        cp.and(predicates[i]);
      }

      builder.where(cp);
    }

    let queryObject = builder.build();
    if (Ember.isEmpty(queryObject.projectionName)) {
      // Now if projection is not specified then only 'id' field will be selected.
      queryObject.select = [];
    }

    return queryObject;
  },

  /**
    Retrieves projection from query and returns it.
    Retrieved projection removes from the query.

    @method _extractProjectionFromQuery
    @param {String} modelName The name of the model type.
    @param {subclass of DS.Model} typeClass 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.
    @private
  */
  _extractProjectionFromQuery: function(modelName, typeClass, query) {
    if (query && query.projection) {
      let proj = query.projection;
      if (typeof query.projection === 'string') {
        let projName = query.projection;
        proj = typeClass.projections.get(projName);
      }

      delete query.projection;
      return proj;
    }

    // If using Query Language
    if (query && query instanceof QueryObject && !Ember.isNone(query.projectionName)) {
      let proj = typeClass.projections.get(query.projectionName);

      return proj;
    }

    return null;
  },

  /**
    Completes loading record for given projection.

    @method _completeLoadingRecord
    @param {subclass of DS.Store} store Store to use for complete loading record.
    @param {subclass of DS.Model} type Model type.
    @param {Object} record Main record loaded by adapter.
    @param {Object} [projection] Projection for complete loading of record.
    @param {subclass of DS.Model} [originType] Type of model that referencing to main record's model type.
    @return {Object} Completely loaded record with all properties
                     include relationships corresponds to given projection
    @private
  */
  _completeLoadRecord: function(store, type, record, projection, originType) {
    let promises = Ember.A();
    if (!Ember.isNone(projection) && projection.attributes) {
      let attributes = projection.attributes;
      for (let attrName in attributes) {
        if (attributes.hasOwnProperty(attrName)) {
          this._replaceIdToHash(store, type, record, attributes, attrName, promises);
        }
      }
    } else {
      let relationshipNames = Ember.get(type, 'relationshipNames');
      let allRelationshipNames = Ember.A().concat(relationshipNames.belongsTo, relationshipNames.hasMany);
      let relationshipsByName = Ember.get(type, 'relationshipsByName');
      let originTypeModelName = !Ember.isNone(originType) ? originType.modelName : '';
      allRelationshipNames.forEach(function(attrName) {
        // Avoid loops formed by inverse attributes.
        let possibleInverseType = store.modelFor(relationshipsByName.get(attrName).type);
        let relationshipsByNameOfPossibleInverseType = Ember.get(possibleInverseType, 'relationshipsByName');
        let inverseAttribute = relationshipsByName.get(attrName).options.inverse;
        let possibleInverseTypeMeta = !Ember.isNone(inverseAttribute) ? relationshipsByNameOfPossibleInverseType.get(inverseAttribute) : null;
        if ((Ember.isNone(possibleInverseTypeMeta) || relationshipsByName.get(attrName).type !== originTypeModelName) && !Ember.isEmpty(record[attrName])) {
          this._replaceIdToHash(store, type, record, relationshipsByName, attrName, promises);
        }
      }, this);
    }

    return Ember.RSVP.all(promises).then(() => {
      let relationshipNames = Ember.get(type, 'relationshipNames');
      let belongsTo = relationshipNames.belongsTo;
      for (let i = 0; i < belongsTo.length; i++) {
        let relationshipName = belongsTo[i];
        if (!isAsync(type, relationshipName) && !isObject(record[relationshipName])) {
          record[relationshipName] = null;
        }
      }

      let hasMany = relationshipNames.hasMany;
      for (let i = 0; i < hasMany.length; i++) {
        let relationshipName = hasMany[i];
        if (!Ember.isArray(record[relationshipName])) {
          record[relationshipName] = [];
        } else {
          if (!isAsync(type, relationshipName)) {
            let hasUnloadedObjects = false;
            for (let j = 0; j < record[relationshipName].length; j++) {
              if (!isObject(record[relationshipName][j])) {
                hasUnloadedObjects = true;
              }
            }

            if (hasUnloadedObjects) {
              record[relationshipName] = [];
            }
          }
        }
      }

      return record;
    });
  },

  _loadRelatedRecord(store, type, id, proj, originType) {
    let modelName = proj.modelName ? proj.modelName : proj.type;
    let relatedRecord = store.peekRecord(modelName, id);
    if (Ember.isNone(relatedRecord)) {
      let builder = new QueryBuilder(store, modelName);
      builder.byId(id);

      if (proj && proj.modelName) {
        let attrNames = 'id';
        let attrs = proj.attributes;
        for (let key in attrs) {
          if (attrs.hasOwnProperty(key) && !Ember.isNone(attrs[key].kind)) {
            attrNames += ',' + key;
          }
        }

        builder.select(attrNames);
      }

      let query = builder.build();
      if (Ember.$.isEmptyObject(builder._select)) {
        // Now if projection is not specified then only 'id' field will be selected.
        query.select = [];
      }

      query.originType = originType;
      if (proj && proj.modelName) {
        query.projection = proj;
      }

      return this.queryRecord(store, type, query);
    } else {
      let relatedRecordObject = relatedRecord.serialize({ includeId: true });
      return this._completeLoadRecord(store, type, relatedRecordObject, proj, originType);
    }
  },

  _replaceIdToHash(store, type,  record, attributes, attrName, promises) {
    let attr = attributes.hasOwnProperty(attrName) ? attributes[attrName] : attributes.get(attrName);
    let modelName = attr.modelName ? attr.modelName : attr.type;
    let relatedModelType = (attr.kind === 'belongsTo' || attr.kind === 'hasMany') ? store.modelFor(modelName) : null;
    switch (attr.kind) {
      case 'attr':
        break;
      case 'belongsTo':
        if (!isAsync(type, attrName)) {
          // let primaryKeyName = this.serializer.get('primaryKey');
          let id = record[attrName];
          if (!Ember.isNone(id) && (!isObject(id))) {
            promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr, type).then((relatedRecord) => {
              delete record[attrName];
              record[attrName] = relatedRecord;
            }));
          }
        }

        break;
      case 'hasMany':
        if (!isAsync(type, attrName)) {
          if (Ember.isArray(record[attrName])) {
            let ids = Ember.copy(record[attrName]);
            delete record[attrName];
            record[attrName] = [];
            let pushToRecordArray = (relatedRecord) => {
              record[attrName].push(relatedRecord);
            };

            for (var i = 0; i < ids.length; i++) {
              let id = ids[i];
              if (!isObject(id)) {
                promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr, type).then(pushToRecordArray));
              } else {
                pushToRecordArray(id);
              }
            }
          } else {
            record[attrName] = [];
          }
        }

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

export default OfflineAdapter;
<commit_msg>Fix offline adapter id type of string must be in hash<commit_after>/**
  @module ember-flexberry-data
*/

import Ember from 'ember';
import DS from 'ember-data';
import isAsync from '../utils/is-async';
import isObject from '../utils/is-object';
import generateUniqueId from '../utils/generate-unique-id';
import IndexeddbAdapter from '../query/indexeddb-adapter';
import QueryObject from '../query/query-object';
import QueryBuilder from '../query/builder';
import FilterOperator from '../query/filter-operator';
import Condition from '../query/condition';
import { SimplePredicate, ComplexPredicate } from '../query/predicate';
import Dexie from 'npm:dexie';

/**
  Default adapter for {{#crossLink "Offline.LocalStore"}}{{/crossLink}}.

  @class Offline
  @namespace Adapter
  @extends DS.Adapter
*/
var OfflineAdapter = DS.Adapter.extend({
  defaultSerializer: 'offline',

  //queue: LFQueue.create(),
  //cache: LFCache.create(),
  //caching: 'model',
  coalesceFindRequests: true,

  /**
    Database name for IndexedDB.

    @property databaseName
    @type String
  */
  databaseName: undefined,

  /*
    Adapter initialization.
  */
  init() {
    this._super(...arguments);
    Ember.assert('Error: database name for IndexedDB is not defined', !Ember.isNone(this.databaseName));
  },

  shouldBackgroundReloadRecord() {
    return false;
  },

  shouldReloadAll() {
    return true;
  },

  /*
    Generate globally unique IDs for records.
  */
  generateIdForRecord: generateUniqueId,

  /**
    Clear adapter's cache and IndexedDB's store.

    @method clear
  */
  clear: function () {
    // clear data in databse
    var db = new Dexie(this.databaseName);
    return db.delete();
  },

  /**
    This is the main entry point into finding records.

    @method findRecord
    @param {DS.Store} store
    @param {DS.Model} type
    @param {Object|String|Integer|null} id
  */
  findRecord(store, type, id) {
    let table = this._getTableForOperationsByModelType(type);
    return table.get(id);
  },

  findAll(store, type) {
    let db = new Dexie(this.databaseName);
    let table = db.table(type.modelName);
    return table.toArray();
  },

  findMany(store, type, ids) {
    let promises = Ember.A();
    let records = Ember.A();
    let addRecord = (record) => {
      records.pushObject(record);
    };

    for (let i = 0; i < ids.length; i++) {
      promises.pushObject(this.findRecord(store, type, ids[i]).then(addRecord));
    }

    return Ember.RSVP.all(promises).then(() => {
      Ember.RSVP.resolve(records.compact());
    }).catch(function(reason) {
      Ember.RSVP.reject(reason);
    });
  },

  queryRecord(store, type, query) {
    return this.query(store, type, query).then(result => new Ember.RSVP.Promise((resolve) => resolve(result[0])));
  },

  /**
    Supports query language objects or queries that look like this:
     {
       <property to query>: <value to match>,
       ...
     }

    (in this case every property added to the query is an "AND" query, not "OR")
  */
  query(store, type, query) {
    let modelName = type.modelName;
    let proj = this._extractProjectionFromQuery(modelName, type, query);
    let originType = null;
    if (query && query.originType) {
      originType = query.originType;

      delete query.originType;
    }

    let indexedDBAdapter = new IndexeddbAdapter(this.databaseName);

    let _this = this;
    let queryForIndexedDBAdapter = query instanceof QueryObject ? query : this._makeQueryObject(store, modelName, query, proj);
    return indexedDBAdapter.query(store, queryForIndexedDBAdapter).then((recordArray) =>
      new Ember.RSVP.Promise((resolve, reject) => {
        let promises = Ember.A();
        for (let i = 0; i < recordArray.length; i++) {
          let record = recordArray[i];
          promises.pushObject(_this._completeLoadRecord(store, type, record, proj, originType));
        }

        Ember.RSVP.all(promises).then(() => {
          resolve(recordArray);
        }).catch((reason) => {
          reject(reason);
        });
      })
    );
  },

  createRecord(store, type, snapshot) {
    return this._updateOrCreate(store, type, snapshot);
  },

  updateRecord(store, type, snapshot) {
    return this._updateOrCreate(store, type, snapshot);
  },

  deleteRecord(store, type, snapshot) {
    let table = this._getTableForOperationsByModelType(type);
    return table.delete(snapshot.id);
  },

  // private

  /**
    Gets [WritableTable](https://github.com/dfahlander/Dexie.js/wiki/WriteableTable) class for given model type.
    Creates [Dexie's schema](https://github.com/dfahlander/Dexie.js/wiki/Version.stores()) that contains only 'id' property for specified model type.

    @method _getTableForOperationsByModelType
    @param {subclass of DS.Model} type Model type.
    @return {WritableTable} Instance of [WritableTable](https://github.com/dfahlander/Dexie.js/wiki/WriteableTable) class for given model type.
    @private
  */
  _getTableForOperationsByModelType(type) {
    let db = new Dexie(this.databaseName);
    let storeSchema = {};
    storeSchema[type.modelName] = 'id';
    db.version(1).stores(storeSchema);
    return db[type.modelName];
  },

  /**
    Gets [WritableTable](https://github.com/dfahlander/Dexie.js/wiki/WriteableTable) class for given snapshot.
    Creates [Dexie's schema](https://github.com/dfahlander/Dexie.js/wiki/Version.stores()) that contains model properties taken from snapshot.

    @method _getTableForOperationsByModelSnapshot
    @param {DS.Snapshot} snapshot Snapshot of model.
    @return {WritableTable} Instance of [WritableTable](https://github.com/dfahlander/Dexie.js/wiki/WriteableTable) class for given snapshot.
    @private
  */
  _getTableForOperationsByModelSnapshot(snapshot) {
    let db = new Dexie(this.databaseName);
    let storeSchema = {};
    storeSchema[snapshot.modelName] = 'id';

    snapshot.eachAttribute(function(name) {
      storeSchema[snapshot.modelName] += ',' + name;
    });

    snapshot.eachRelationship(function(name, relationship) {
      if (relationship.kind === 'hasMany') {
        storeSchema[snapshot.modelName] += ',*' + name;
      } else {
        storeSchema[snapshot.modelName] += ',' + name;
      }
    });

    db.version(1).stores(storeSchema);
    return db[snapshot.modelName];
  },

  _updateOrCreate(store, type, snapshot) {
    const serializer = store.serializerFor(type.modelName);
    const recordHash = serializer.serialize(snapshot, { includeId: true });
    return this._getTableForOperationsByModelType(snapshot.type).put(recordHash);
  },

  /**
    Makes {{#crossLink "Query.QueryObject"}}{{/crossLink}} out of queries that look like this:
     {
       <property to query>: <value to match>,
       ...
     }.

    @method _makeQueryObject
    @param store Store used for making query
    @param {String} modelName The name of the model type.
    @param {Object} query Query parameters.
    @param {Object|String} [projection] Projection for query.
    @return {QueryObject} Query object for IndexedDB adapter.
    @private
  */
  _makeQueryObject(store, modelName, query, projection) {
    let builder = new QueryBuilder(store, modelName);
    if (projection && isObject(projection) && (projection.projectionName)) {
      builder.selectByProjection(projection.projectionName);
    } else if (projection && typeof projection === 'string') {
      builder.selectByProjection(projection);
    }

    let predicates = [];
    for (let property in query) {
      const queryValue = query[property];

      // I suppose it's possible to encounter problems when queryValue will have 'Date' type...
      predicates.push(new SimplePredicate(property, FilterOperator.Eq, queryValue.toString()));
    }

    if (predicates.length === 1) {
      builder.where(predicates[0]);
    } else if (predicates.length > 1) {
      let cp = new ComplexPredicate(Condition.And, predicates[0], predicates[1]);
      for (let i = 2; i < predicates.length; i++) {
        cp.and(predicates[i]);
      }

      builder.where(cp);
    }

    let queryObject = builder.build();
    if (Ember.isEmpty(queryObject.projectionName)) {
      // Now if projection is not specified then only 'id' field will be selected.
      queryObject.select = [];
    }

    return queryObject;
  },

  /**
    Retrieves projection from query and returns it.
    Retrieved projection removes from the query.

    @method _extractProjectionFromQuery
    @param {String} modelName The name of the model type.
    @param {subclass of DS.Model} typeClass 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.
    @private
  */
  _extractProjectionFromQuery: function(modelName, typeClass, query) {
    if (query && query.projection) {
      let proj = query.projection;
      if (typeof query.projection === 'string') {
        let projName = query.projection;
        proj = typeClass.projections.get(projName);
      }

      delete query.projection;
      return proj;
    }

    // If using Query Language
    if (query && query instanceof QueryObject && !Ember.isNone(query.projectionName)) {
      let proj = typeClass.projections.get(query.projectionName);

      return proj;
    }

    return null;
  },

  /**
    Completes loading record for given projection.

    @method _completeLoadingRecord
    @param {subclass of DS.Store} store Store to use for complete loading record.
    @param {subclass of DS.Model} type Model type.
    @param {Object} record Main record loaded by adapter.
    @param {Object} [projection] Projection for complete loading of record.
    @param {subclass of DS.Model} [originType] Type of model that referencing to main record's model type.
    @return {Object} Completely loaded record with all properties
                     include relationships corresponds to given projection
    @private
  */
  _completeLoadRecord: function(store, type, record, projection, originType) {
    let promises = Ember.A();
    if (!Ember.isNone(projection) && projection.attributes) {
      let attributes = projection.attributes;
      for (let attrName in attributes) {
        if (attributes.hasOwnProperty(attrName)) {
          this._replaceIdToHash(store, type, record, attributes, attrName, promises);
        }
      }
    } else {
      let relationshipNames = Ember.get(type, 'relationshipNames');
      let allRelationshipNames = Ember.A().concat(relationshipNames.belongsTo, relationshipNames.hasMany);
      let relationshipsByName = Ember.get(type, 'relationshipsByName');
      let originTypeModelName = !Ember.isNone(originType) ? originType.modelName : '';
      allRelationshipNames.forEach(function(attrName) {
        // Avoid loops formed by inverse attributes.
        let possibleInverseType = store.modelFor(relationshipsByName.get(attrName).type);
        let relationshipsByNameOfPossibleInverseType = Ember.get(possibleInverseType, 'relationshipsByName');
        let inverseAttribute = relationshipsByName.get(attrName).options.inverse;
        let possibleInverseTypeMeta = !Ember.isNone(inverseAttribute) ? relationshipsByNameOfPossibleInverseType.get(inverseAttribute) : null;
        if ((Ember.isNone(possibleInverseTypeMeta) || relationshipsByName.get(attrName).type !== originTypeModelName) && !Ember.isEmpty(record[attrName])) {
          this._replaceIdToHash(store, type, record, relationshipsByName, attrName, promises);
        }
      }, this);
    }

    return Ember.RSVP.all(promises).then(() => {
      let relationshipNames = Ember.get(type, 'relationshipNames');
      let belongsTo = relationshipNames.belongsTo;
      for (let i = 0; i < belongsTo.length; i++) {
        let relationshipName = belongsTo[i];
        if (!isAsync(type, relationshipName) && !isObject(record[relationshipName])) {
          record[relationshipName] = null;
        }
      }

      let hasMany = relationshipNames.hasMany;
      for (let i = 0; i < hasMany.length; i++) {
        let relationshipName = hasMany[i];
        if (!Ember.isArray(record[relationshipName])) {
          record[relationshipName] = [];
        } else {
          if (!isAsync(type, relationshipName)) {
            let hasUnloadedObjects = false;
            for (let j = 0; j < record[relationshipName].length; j++) {
              if (!isObject(record[relationshipName][j])) {
                hasUnloadedObjects = true;
              }
            }

            if (hasUnloadedObjects) {
              record[relationshipName] = [];
            }
          }
        }
      }

      return record;
    });
  },

  _loadRelatedRecord(store, type, id, proj, originType) {
    let modelName = proj.modelName ? proj.modelName : proj.type;
    let relatedRecord = store.peekRecord(modelName, id);
    if (Ember.isNone(relatedRecord)) {
      let builder = new QueryBuilder(store, modelName);
      builder.byId(id);

      if (proj && proj.modelName) {
        let attrNames = 'id';
        let attrs = proj.attributes;
        for (let key in attrs) {
          if (attrs.hasOwnProperty(key) && !Ember.isNone(attrs[key].kind)) {
            attrNames += ',' + key;
          }
        }

        builder.select(attrNames);
      }

      let query = builder.build();
      if (Ember.$.isEmptyObject(builder._select)) {
        // Now if projection is not specified then only 'id' field will be selected.
        query.select = [];
      }

      query.originType = originType;
      if (proj && proj.modelName) {
        query.projection = proj;
      }

      return this.queryRecord(store, type, query);
    } else {
      let relatedRecordObject = relatedRecord.serialize({ includeId: true });
      return this._completeLoadRecord(store, type, relatedRecordObject, proj, originType);
    }
  },

  _replaceIdToHash(store, type,  record, attributes, attrName, promises) {
    let attr = attributes.hasOwnProperty(attrName) ? attributes[attrName] : attributes.get(attrName);
    let modelName = attr.modelName ? attr.modelName : attr.type;
    let relatedModelType = (attr.kind === 'belongsTo' || attr.kind === 'hasMany') ? store.modelFor(modelName) : null;
    switch (attr.kind) {
      case 'attr':
        break;
      case 'belongsTo':
        if (!isAsync(type, attrName)) {
          // let primaryKeyName = this.serializer.get('primaryKey');
          let id = record[attrName];
          if (!Ember.isNone(id) && (!isObject(id))) {
            promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr, type).then((relatedRecord) => {
              delete record[attrName];
              record[attrName] = relatedRecord;
            }));
          }
        }

        break;
      case 'hasMany':
        if (!isAsync(type, attrName)) {
          if (Ember.isArray(record[attrName])) {
            let ids = Ember.copy(record[attrName]);
            delete record[attrName];
            record[attrName] = [];
            let pushToRecordArray = (relatedRecord) => {
              record[attrName].push(relatedRecord);
            };

            for (var i = 0; i < ids.length; i++) {
              let id = ids[i];
              if (!isObject(id)) {
                promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr, type).then(pushToRecordArray));
              } else {
                pushToRecordArray(id);
              }
            }
          } else {
            record[attrName] = [];
          }
        }

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

export default OfflineAdapter;
<|endoftext|>


<commit_before>var request = require('request'),
  qs = require('querystring'),
  helpers = require('./helpers');

/**
 * hubspot API wrapper for the API version 3. This object should not be
 * instantiated directly but by using the version wrapper {@link hubspotAPI}.
 *
 * @param options Configuration options
 * @return Instance of {@link hubspotAPI}
 */
function hubspotAPI(options) {

  if (!options) {
    throw new Error('You have to provide a authentication details for this to work.');
  }

  if (!options.api_key && !options.access_token) {
    throw new Error('API requires api_key or access_token');
  }

  /**
   * Authenticate using either the API key or with oauth but not both.
   */
  if (options.api_key) {

    /**
     * if user has api_key, ignore oauth stuff.
     */

    this.api_key = options.api_key;
  } else {

    this.access_token = options.access_token;
  }

  this.httpUri = 'https://api.hubapi.com';
  this.DEBUG = options.DEBUG || false;
  this.contentType = options.contentType || 'application/json';
  this.userAgent = options.userAgent || 'node-hubspot';
}

module.exports = hubspotAPI;

/**
 * Sends a given request as a JSON object to the hubspot API and finally
 * calls the given callback function with the resulting JSON object. This
 * method should not be called directly but will be used internally by all API
 * methods defined.
 *
 * @param resource hubspot API resource to call
 * @param method hubspot API method to call
 * @param availableParams Parameters available for the specified API method
 * @param givenParams Parameters to call the hubspot API with
 * @param callback Callback function to call on success
 */
hubspotAPI.prototype.execute = function (verb, path, availableParams, givenParams, callback) {
  var self = this,
    headers,
    uri,
    body,
    finalParams,
    currentParam,
    parsedResponse,
    requestOptions;

  headers = {
    "Content-Type": this.contentType,
    "User-Agent": this.userAgent
  };

  uri = this.httpUri + '/' + path;

  finalParams = {};

  for (var i = 0; i < availableParams.length; i++) {
    currentParam = availableParams[i];
    if (typeof givenParams[currentParam] !== 'undefined')
      finalParams[currentParam] = givenParams[currentParam];
  }

  /**
   * For OAuth: &access_token=TOKEN
   * For API Keys: &hapikey=KEY
   * @see http://developers.hubspot.com/auth/oauth_overview
   */
  if (this.api_key) {
    finalParams.hapikey = this.api_key;
  }

  if (this.access_token) {
    finalParams.access_token = this.access_token;
  }

  // some endpoints use array-like parameters that must be stringified.
  // also must user querystring instead of qs.
  // qs output is param[0]=a&param[1]=b&param[2]=c
  // querystring output is param=a&param=b&param=c and this is required.
  uri = uri + "?" + qs.stringify(finalParams)

  requestOptions = {
    uri: uri,
    method: verb,
    headers: headers,
    gzip: true
  };

  if (verb === "POST") {
    if (typeof givenParams == 'string') {
      requestOptions.body = givenParams;
    } else {
      requestOptions.form = finalParams;
    }
  }

  if (self.DEBUG) {
    console.log("req", requestOptions);
  }

  request(requestOptions, function (error, res, body) {
    if (self.DEBUG) {
      console.log("error", error);
      console.log("res statusCode", res.statusCode);
      console.log("body", body);
    }
    if (error) {
      return callback(new Error('Unable to connect to the hubspot API endpoint.'));
    }

    if (helpers.isAnError(res.statusCode)) {
      return callback(helpers.createHubspotError(body, res.statusCode));
    }

    try {
      parsedResponse = JSON.parse(body);
    } catch (error) {
      return callback(new Error('Error parsing JSON answer from hubspot API.'));
    }

    if (parsedResponse.status === "error" || helpers.isAnError(res.statusCode)) {
      return callback(helpers.createHubspotError(parsedResponse, res.statusCode));
    }

    return callback(null, parsedResponse);
  });

};

hubspotAPI.prototype.get = function (path, availableParams, givenParams, callback) {
  this.execute("GET", path, availableParams, givenParams, callback);
};

hubspotAPI.prototype.post = function (path, availableParams, givenParams, callback) {
  this.execute("POST", path, availableParams, givenParams, callback);
};

hubspotAPI.prototype.put = function (path, availableParams, givenParams, callback) {
  this.execute("PUT", path, availableParams, givenParams, callback);
};

hubspotAPI.prototype.patch = function (path, availableParams, givenParams, callback) {
  this.execute("PATCH", path, availableParams, givenParams, callback);
};

hubspotAPI.prototype.delete = function (path, availableParams, givenParams, callback) {
  this.execute("DELETE", path, availableParams, givenParams, callback);
};

/*****************************************************************************/
/************************* EVENT Methods *************************************/
/*****************************************************************************/

// un-published API endpoint.
hubspotAPI.prototype.event_completions = function (params, callback) {
  var path = "analytics/v2/event/completions/total",
    availableParams = [
      "offset"
    ];

  this.post(path, availableParams, params, callback);
};

hubspotAPI.prototype.events = function (params, callback) {
  var path = "reports/v1/events",
    availableParams = [];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* SOURCES Methods ***********************************/
/*****************************************************************************/

// un-published API endpoint.
hubspotAPI.prototype.sources_daily = function (params, callback) {
  var path = "analytics/v2/sources/summary/daily",
    availableParams = [];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* PAGES Methods *************************************/
/*****************************************************************************/

/*****************************************************************************/
/************************* CONTACTS Methods **********************************/
/*****************************************************************************/

/**
 * http://developers.hubspot.com/docs/methods/contacts/get_contacts
 * @param params
 * @param callback
 */
hubspotAPI.prototype.contacts_statistics = function (params, callback) {
  var path = "contacts/v1/contacts/statistics",
    availableParams = [];
  this.get(path, availableParams, params, callback);
};

/**
 * http://developers.hubspot.com/docs/methods/contacts/get_contacts
 * contacts/v1/lists/all/contacts/all
 * @param params
 * @param callback
 */

hubspotAPI.prototype.all_contacts = function (params, callback) {
  var path = "contacts/v1/lists/all/contacts/all",
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ],
    defaultProps = [
      'city',
      'zip',
      'closedate',
      'company',
      'conversion',
      'createdate',
      'days_to_close',
      'hs_analytics_num_visits',
      'hs_analytics_last_visit_timestamp',
      'hs_email_first_open_date',
      'email',
      'first_conversion_date',
      'firstname',
      'hubspotscore',
      'lastmodifieddate',
      'lastname',
      'lifecyclestage',
      'hs_lifecyclestage_customer_date',
      'hs_lifecyclestage_marketingqualifiedlead_date',
      'hs_lifecyclestage_subscriber_date',
      'hs_lifecyclestage_salesqualifiedlead_date',
      'website'
    ];

  if (params.property && Array.isArray(params.property)) {
    params.property = helpers.unique(params.property.concat(defaultProps))
  } else {
    params.property = defaultProps;
  }
  this.get(path, availableParams, params, callback);
};

/**
 * contacts/v1/contact
 */
hubspotAPI.prototype.contacts_contact = function (params, callback) {
  var version = params.version ? params.version : "v1";

  var path = "contacts/" + version + "/contact",
    availableParams = [

    ];

  var properties = [];
  for (var key in params) {
    properties.push({
      property: key,
      value: params[key]
    });
  }

  params = JSON.stringify({
    properties: properties
  });

  this.post(path, availableParams, params, callback);
};

/**
 * contacts/v1/lists
 */
hubspotAPI.prototype.contacts_lists = function (params, callback) {
  var version = params.version ? params.version : "v1";

  var path = "contacts/" + version + "/lists",
    availableParams = [
      "count",
      "offset"
    ];

  this.get(path, availableParams, params, callback);
};

/**
 * contacts/v1/lists/static
 */
hubspotAPI.prototype.contacts_static_lists = function (params, callback) {
  var version = params.version ? params.version : "v1";

  var path = "contacts/" + version + "/lists/static",
    availableParams = [
      "count",
      "offset"
    ];

  this.get(path, availableParams, params, callback);
};

/**
 * contacts/v1/lists/:list_id/add
 */
hubspotAPI.prototype.contacts_lists_add = function (params, callback) {
  var version = params.version ? params.version : "v1";
  var list_id = params['list_id'];
  var contact_id = params['contact_id'];

  var path = "contacts/" + version + "/lists/" + list_id + "/add";
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ];

  this.post(path, availableParams, JSON.stringify({'vids':[contact_id]}), callback);
};

/**
 * contacts/v1/properties
 * @param params
 * @param callback
 */
hubspotAPI.prototype.contacts_properties = function (params, callback) {
  var version = params.version ? params.version : "v1";

  var path = "contacts/" + version + "/properties",
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ];

  this.get(path, availableParams, params, callback);
};

/**
 * contacts/v1/forms and contacts/v2/forms
 * @param params
 * @param callback
 */
hubspotAPI.prototype.contacts_forms = function (params, callback) {
  var version = params.version ? params.version : "v1";

  var path = "contacts/" + version + "/forms",
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* WORKFLOWS Methods *************************************/
/*****************************************************************************/
/**
 * automation/v2/workflows
 * @param params
 * @param callback
 */
hubspotAPI.prototype.automation_workflows = function (params, callback) {
  var path = "automation/v2/workflows",
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* LEADS Methods *************************************/
/*****************************************************************************/

/**
 * leads/v2/forms
 * @param params
 * @param callback
 */
hubspotAPI.prototype.leads_forms = function (params, callback) {
  var path = "leads/v2/forms",
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* PROPECTS Methods **********************************/
/*****************************************************************************/

/**
 * leads/v2/forms
 * @param params
 * @param callback
 */
hubspotAPI.prototype.prospects = function (params, callback) {
  var path = "prospects/v1/timeline",
    availableParams = [
      "timeOffset",
      "orgOffset"
    ];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* SETTINGS Methods **********************************/
/*****************************************************************************/

//http://developers.hubspot.com/docs/methods/settings/get_settings
hubspotAPI.prototype.settings = function (params, callback) {
  var verb = params.verb ? params.verb : "get";

  var path = "settings/v1/settings",
    availableParams = [
      "sm",
      "name",
      "value",
      "domains",
      "readOnly"
    ];

  this[verb](path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* EMAIL Methods **********************************/
/*****************************************************************************/

//These are un-documented - use at your own risk.
hubspotAPI.prototype.cosemail = function (params, callback) {
  var path = "cosemail/v1/emaildashboard";
  var availableParams = [
    "limit",
    "offset",
    "property",
  ];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* AUTH Methods **********************************/
/*****************************************************************************/

//These are un-documented - use at your own risk.
hubspotAPI.prototype.refresh = function (params, callback) {
  var path = "auth/v1/refresh";
  var availableParams = [
    "refresh_token",
    "client_id",
    "grant_type",
  ];

  if (!params) {
    params = {};
  }
  params.grant_type = "refresh_token";

  this.post(path, availableParams, params, callback);
};

<commit_msg>ensure we pass JSON body<commit_after>var request = require('request'),
  qs = require('querystring'),
  helpers = require('./helpers');

/**
 * hubspot API wrapper for the API version 3. This object should not be
 * instantiated directly but by using the version wrapper {@link hubspotAPI}.
 *
 * @param options Configuration options
 * @return Instance of {@link hubspotAPI}
 */
function hubspotAPI(options) {

  if (!options) {
    throw new Error('You have to provide a authentication details for this to work.');
  }

  if (!options.api_key && !options.access_token) {
    throw new Error('API requires api_key or access_token');
  }

  /**
   * Authenticate using either the API key or with oauth but not both.
   */
  if (options.api_key) {

    /**
     * if user has api_key, ignore oauth stuff.
     */

    this.api_key = options.api_key;
  } else {

    this.access_token = options.access_token;
  }

  this.httpUri = 'https://api.hubapi.com';
  this.DEBUG = options.DEBUG || false;
  this.contentType = options.contentType || 'application/json';
  this.userAgent = options.userAgent || 'node-hubspot';
}

module.exports = hubspotAPI;

/**
 * Sends a given request as a JSON object to the hubspot API and finally
 * calls the given callback function with the resulting JSON object. This
 * method should not be called directly but will be used internally by all API
 * methods defined.
 *
 * @param resource hubspot API resource to call
 * @param method hubspot API method to call
 * @param availableParams Parameters available for the specified API method
 * @param givenParams Parameters to call the hubspot API with
 * @param callback Callback function to call on success
 */
hubspotAPI.prototype.execute = function (verb, path, availableParams, givenParams, callback) {
  var self = this,
    headers,
    uri,
    body,
    finalParams,
    currentParam,
    parsedResponse,
    requestOptions;

  headers = {
    "Content-Type": this.contentType,
    "User-Agent": this.userAgent
  };

  uri = this.httpUri + '/' + path;

  finalParams = {};

  for (var i = 0; i < availableParams.length; i++) {
    currentParam = availableParams[i];
    if (typeof givenParams[currentParam] !== 'undefined')
      finalParams[currentParam] = givenParams[currentParam];
  }

  /**
   * For OAuth: &access_token=TOKEN
   * For API Keys: &hapikey=KEY
   * @see http://developers.hubspot.com/auth/oauth_overview
   */
  if (this.api_key) {
    finalParams.hapikey = this.api_key;
  }

  if (this.access_token) {
    finalParams.access_token = this.access_token;
  }

  // some endpoints use array-like parameters that must be stringified.
  // also must user querystring instead of qs.
  // qs output is param[0]=a&param[1]=b&param[2]=c
  // querystring output is param=a&param=b&param=c and this is required.
  uri = uri + "?" + qs.stringify(finalParams)

  requestOptions = {
    uri: uri,
    method: verb,
    headers: headers,
    gzip: true
  };

  if (verb === "POST") {
    if (typeof givenParams == 'string') {
      requestOptions.body = givenParams;
    } else {
      requestOptions.form = finalParams;
    }
  }

  if (self.DEBUG) {
    console.log("req", requestOptions);
  }

  request(requestOptions, function (error, res, body) {
    if (self.DEBUG) {
      console.log("error", error);
      console.log("res statusCode", res.statusCode);
      console.log("body", body);
    }
    if (error) {
      return callback(new Error('Unable to connect to the hubspot API endpoint.'));
    }

/*
    if (helpers.isAnError(res.statusCode)) {
      return callback(helpers.createHubspotError(body, res.statusCode));
    }
*/

    try {
      parsedResponse = JSON.parse(body);
    } catch (error) {
      return callback(new Error('Error parsing JSON answer from hubspot API.'));
    }

    if (parsedResponse.status === "error" || helpers.isAnError(res.statusCode)) {
      return callback(helpers.createHubspotError(parsedResponse, res.statusCode));
    }

    return callback(null, parsedResponse);
  });

};

hubspotAPI.prototype.get = function (path, availableParams, givenParams, callback) {
  this.execute("GET", path, availableParams, givenParams, callback);
};

hubspotAPI.prototype.post = function (path, availableParams, givenParams, callback) {
  this.execute("POST", path, availableParams, givenParams, callback);
};

hubspotAPI.prototype.put = function (path, availableParams, givenParams, callback) {
  this.execute("PUT", path, availableParams, givenParams, callback);
};

hubspotAPI.prototype.patch = function (path, availableParams, givenParams, callback) {
  this.execute("PATCH", path, availableParams, givenParams, callback);
};

hubspotAPI.prototype.delete = function (path, availableParams, givenParams, callback) {
  this.execute("DELETE", path, availableParams, givenParams, callback);
};

/*****************************************************************************/
/************************* EVENT Methods *************************************/
/*****************************************************************************/

// un-published API endpoint.
hubspotAPI.prototype.event_completions = function (params, callback) {
  var path = "analytics/v2/event/completions/total",
    availableParams = [
      "offset"
    ];

  this.post(path, availableParams, params, callback);
};

hubspotAPI.prototype.events = function (params, callback) {
  var path = "reports/v1/events",
    availableParams = [];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* SOURCES Methods ***********************************/
/*****************************************************************************/

// un-published API endpoint.
hubspotAPI.prototype.sources_daily = function (params, callback) {
  var path = "analytics/v2/sources/summary/daily",
    availableParams = [];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* PAGES Methods *************************************/
/*****************************************************************************/

/*****************************************************************************/
/************************* CONTACTS Methods **********************************/
/*****************************************************************************/

/**
 * http://developers.hubspot.com/docs/methods/contacts/get_contacts
 * @param params
 * @param callback
 */
hubspotAPI.prototype.contacts_statistics = function (params, callback) {
  var path = "contacts/v1/contacts/statistics",
    availableParams = [];
  this.get(path, availableParams, params, callback);
};

/**
 * http://developers.hubspot.com/docs/methods/contacts/get_contacts
 * contacts/v1/lists/all/contacts/all
 * @param params
 * @param callback
 */

hubspotAPI.prototype.all_contacts = function (params, callback) {
  var path = "contacts/v1/lists/all/contacts/all",
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ],
    defaultProps = [
      'city',
      'zip',
      'closedate',
      'company',
      'conversion',
      'createdate',
      'days_to_close',
      'hs_analytics_num_visits',
      'hs_analytics_last_visit_timestamp',
      'hs_email_first_open_date',
      'email',
      'first_conversion_date',
      'firstname',
      'hubspotscore',
      'lastmodifieddate',
      'lastname',
      'lifecyclestage',
      'hs_lifecyclestage_customer_date',
      'hs_lifecyclestage_marketingqualifiedlead_date',
      'hs_lifecyclestage_subscriber_date',
      'hs_lifecyclestage_salesqualifiedlead_date',
      'website'
    ];

  if (params.property && Array.isArray(params.property)) {
    params.property = helpers.unique(params.property.concat(defaultProps))
  } else {
    params.property = defaultProps;
  }
  this.get(path, availableParams, params, callback);
};

/**
 * contacts/v1/contact
 */
hubspotAPI.prototype.contacts_contact = function (params, callback) {
  var version = params.version ? params.version : "v1";

  var path = "contacts/" + version + "/contact",
    availableParams = [

    ];

  var properties = [];
  for (var key in params) {
    properties.push({
      property: key,
      value: params[key]
    });
  }

  params = JSON.stringify({
    properties: properties
  });

  this.post(path, availableParams, params, callback);
};

/**
 * contacts/v1/lists
 */
hubspotAPI.prototype.contacts_lists = function (params, callback) {
  var version = params.version ? params.version : "v1";

  var path = "contacts/" + version + "/lists",
    availableParams = [
      "count",
      "offset"
    ];

  this.get(path, availableParams, params, callback);
};

/**
 * contacts/v1/lists/static
 */
hubspotAPI.prototype.contacts_static_lists = function (params, callback) {
  var version = params.version ? params.version : "v1";

  var path = "contacts/" + version + "/lists/static",
    availableParams = [
      "count",
      "offset"
    ];

  this.get(path, availableParams, params, callback);
};

/**
 * contacts/v1/lists/:list_id/add
 */
hubspotAPI.prototype.contacts_lists_add = function (params, callback) {
  var version = params.version ? params.version : "v1";
  var list_id = params['list_id'];
  var contact_id = params['contact_id'];

  var path = "contacts/" + version + "/lists/" + list_id + "/add";
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ];

  this.post(path, availableParams, JSON.stringify({'vids':[contact_id]}), callback);
};

/**
 * contacts/v1/properties
 * @param params
 * @param callback
 */
hubspotAPI.prototype.contacts_properties = function (params, callback) {
  var version = params.version ? params.version : "v1";

  var path = "contacts/" + version + "/properties",
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ];

  this.get(path, availableParams, params, callback);
};

/**
 * contacts/v1/forms and contacts/v2/forms
 * @param params
 * @param callback
 */
hubspotAPI.prototype.contacts_forms = function (params, callback) {
  var version = params.version ? params.version : "v1";

  var path = "contacts/" + version + "/forms",
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* WORKFLOWS Methods *************************************/
/*****************************************************************************/
/**
 * automation/v2/workflows
 * @param params
 * @param callback
 */
hubspotAPI.prototype.automation_workflows = function (params, callback) {
  var path = "automation/v2/workflows",
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* LEADS Methods *************************************/
/*****************************************************************************/

/**
 * leads/v2/forms
 * @param params
 * @param callback
 */
hubspotAPI.prototype.leads_forms = function (params, callback) {
  var path = "leads/v2/forms",
    availableParams = [
      "count",
      "property",
      "vidOffset"
    ];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* PROPECTS Methods **********************************/
/*****************************************************************************/

/**
 * leads/v2/forms
 * @param params
 * @param callback
 */
hubspotAPI.prototype.prospects = function (params, callback) {
  var path = "prospects/v1/timeline",
    availableParams = [
      "timeOffset",
      "orgOffset"
    ];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* SETTINGS Methods **********************************/
/*****************************************************************************/

//http://developers.hubspot.com/docs/methods/settings/get_settings
hubspotAPI.prototype.settings = function (params, callback) {
  var verb = params.verb ? params.verb : "get";

  var path = "settings/v1/settings",
    availableParams = [
      "sm",
      "name",
      "value",
      "domains",
      "readOnly"
    ];

  this[verb](path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* EMAIL Methods **********************************/
/*****************************************************************************/

//These are un-documented - use at your own risk.
hubspotAPI.prototype.cosemail = function (params, callback) {
  var path = "cosemail/v1/emaildashboard";
  var availableParams = [
    "limit",
    "offset",
    "property",
  ];

  this.get(path, availableParams, params, callback);
};

/*****************************************************************************/
/************************* AUTH Methods **********************************/
/*****************************************************************************/

//These are un-documented - use at your own risk.
hubspotAPI.prototype.refresh = function (params, callback) {
  var path = "auth/v1/refresh";
  var availableParams = [
    "refresh_token",
    "client_id",
    "grant_type",
  ];

  if (!params) {
    params = {};
  }
  params.grant_type = "refresh_token";

  this.post(path, availableParams, params, callback);
};

<|endoftext|>


<commit_before>//
// Copyright (c) 2017 DrSmugleaf
//

"use strict"
const AutoChannel = require("./base/autochannel")
const commando = require("discord.js-commando")
const responses = require("../../util/constants").responses.AUTO_CHANNEL

module.exports = class AutoChannelCommand extends commando.Command {
  constructor(client) {
    super(client, {
      name: "autochannel",
      aliases: ["autochannel", "autoch", "auto-channel", "auto-ch"],
      group: "autochannel",
      memberName: "autochannel",
      description: "Configure the automatic creation of voice and text channels depending on the games being played in your server.",
      examples: ["autochannel"],
      guildOnly: true,
      throttling: {
        usages: 2,
        duration: 3
      },
      args: [
        {
          key: "mode",
          prompt: "What do you want to do? (disable, enable, threshold)",
          type: "string",
          validate: (value) => {
            return ["disable", "enable", "threshold"].includes(value)
          }
        },
        {
          key: "threshold",
          prompt: "What do you want to set the channel creation/deletion threshold to?",
          type: "integer",
          default: ""
        }
      ]
    })

    this.client.on("dbReady", () => {
      new AutoChannel(this.client)
    })
  }

  hasPermission(msg) {
    return msg.member.hasPermission("MANAGE_CHANNELS")
  }

  async run(msg, args) {
    const mode = args.mode
    var threshold = args.threshold
    const settings = msg.guild.settings.get("auto-channel", {})

    switch (mode) {
    case "disable":
      settings.enabled = false
      msg.guild.settings.set("auto-channel", settings)
      return msg.reply(responses.DISABLED[msg.language])
    case "enable":
      settings.enabled = true
      msg.guild.settings.set("auto-channel", settings)
      return msg.reply(responses.ENABLED[msg.language])
    case "threshold":
      if(!threshold) threshold = await this.args[1].obtainSimple(msg)
      settings.threshold = threshold
      msg.guild.settings.set("auto-channel", settings)
      return msg.reply(responses.SET_THRESHOLD[msg.language](threshold))
    }
  }
}
<commit_msg>Added check for cancelling the command when prompted for a threshold<commit_after>//
// Copyright (c) 2017 DrSmugleaf
//

"use strict"
const AutoChannel = require("./base/autochannel")
const commando = require("discord.js-commando")
const responses = require("../../util/constants").responses.AUTO_CHANNEL

module.exports = class AutoChannelCommand extends commando.Command {
  constructor(client) {
    super(client, {
      name: "autochannel",
      aliases: ["autochannel", "autoch", "auto-channel", "auto-ch"],
      group: "autochannel",
      memberName: "autochannel",
      description: "Configure the automatic creation of voice and text channels depending on the games being played in your server.",
      examples: ["autochannel"],
      guildOnly: true,
      throttling: {
        usages: 2,
        duration: 3
      },
      args: [
        {
          key: "mode",
          prompt: "What do you want to do? (disable, enable, threshold)",
          type: "string",
          validate: (value) => {
            return ["disable", "enable", "threshold"].includes(value)
          }
        },
        {
          key: "threshold",
          prompt: "What do you want to set the channel creation/deletion threshold to?",
          type: "integer",
          default: ""
        }
      ]
    })

    this.client.on("dbReady", () => {
      new AutoChannel(this.client)
    })
  }

  hasPermission(msg) {
    return msg.member.hasPermission("MANAGE_CHANNELS")
  }

  async run(msg, args) {
    const mode = args.mode
    var threshold = args.threshold
    const settings = msg.guild.settings.get("auto-channel", {})

    switch (mode) {
    case "disable":
      settings.enabled = false
      msg.guild.settings.set("auto-channel", settings)
      return msg.reply(responses.DISABLED[msg.language])
    case "enable":
      settings.enabled = true
      msg.guild.settings.set("auto-channel", settings)
      return msg.reply(responses.ENABLED[msg.language])
    case "threshold":
      if(!threshold) threshold = await this.args[1].obtainSimple(msg)
      if(!threshold) return
      settings.threshold = threshold
      msg.guild.settings.set("auto-channel", settings)
      return msg.reply(responses.SET_THRESHOLD[msg.language](threshold))
    }
  }
}
<|endoftext|>


<commit_before>import moment from 'moment';

import computed from './-base';

export default computed((params) => {
  let maybeHideSuffix;

  if (params.length > 2) {
    maybeHideSuffix = params.pop();
  }

  return moment(...params).toNow(maybeHideSuffix);
});
<commit_msg>Update to-now.js<commit_after>import moment from 'moment';

import computed from './-base';

export default computed((params) => {
  let maybeHidePrefix;

  if (params.length > 2) {
    maybeHidePrefix = params.pop();
  }

  return moment(...params).toNow(maybeHidePrefix);
});
<|endoftext|>


<commit_before>const { Command } = require('discord.js-commando')
const { oneLineCommaListsAnd } = require('common-tags')
const moment = require('moment')

module.exports = class ChannelInfoCommand extends Command {
  constructor (client) {
    super(client, {
      name: 'channelinfo',
      memberName: 'channelinfo',
      group: 'information',
      description: 'Displays information about a channel.',
      aliases: [
        'cinfo'
      ],
      clientPermissions: [
        'EMBED_LINKS'
      ],
      throttling: {
        usages: 2,
        duration: 10
      },
      args: [
        {
          key: 'channel',
          label: 'voice channel/text channel',
          prompt: 'What channel would you like to lookup?',
          type: 'channel'
        }
      ],
      guildOnly: true
    })
  }

  run (message, args) {
    var channel = args.channel
    if (channel.type === 'voice') {
      var channelUsers
      if (channel.members.size > 1) {
        channelUsers = oneLineCommaListsAnd`${channel.members.array()}`
      } else {
        channelUsers = 'None'
      }
      message.embed({
        author: { name: this.client.user.tag, icon_url: this.client.user.displayAvatarURL() },
        footer: { text: message.author.tag, icon_url: message.author.displayAvatarURL() },
        timestamp: new Date(),
        title: channel.name,
        description: `Since ${moment(channel.createdAt).format('llll')} ${moment.tz(moment.tz.guess()).format('z')})`,
        fields: [
          {
            'name': 'Type',
            'value': channel.type,
            'inline': true
          },
          {
            'name': 'ID',
            'value': channel.id,
            'inline': true
          },
          {
            'name': 'Position',
            'value': channel.rawPosition,
            'inline': true
          },
          {
            'name': 'Bitrate',
            'value': `${channel.bitrate}kbps`,
            'inline': true
          },
          {
            'name': 'User Count',
            'value': `${channel.members.size.toLocaleString()}/${channel.userLimit === 0 ? 'Infinity' : channel.userLimit}`,
            'inline': true
          },
          {
            'name': 'Users',
            'value': channelUsers,
            'inline': true
          }
        ],
        color: this.client.getClientColor(message)
      })
    } else if (channel.type === 'text') {
      message.embed({
        author: { name: this.client.user.tag, icon_url: this.client.user.displayAvatarURL() },
        footer: { text: message.author.tag, icon_url: message.author.displayAvatarURL() },
        timestamp: new Date(),
        title: channel.name,
        description: `Since ${moment(channel.createdAt).format('llll')} ${moment.tz(moment.tz.guess()).format('z')})`,
        fields: [
          {
            'name': 'Type',
            'value': channel.type,
            'inline': true
          },
          {
            'name': 'ID',
            'value': channel.id,
            'inline': true
          },
          {
            'name': 'Position',
            'value': channel.rawPosition,
            'inline': true
          }
        ],
        color: this.client.getClientColor(message)
      })
    } else {
      return message.reply('please choose a valid voice or text channel.')
    }
  }
}
<commit_msg>Commands: Remove unintentional brackets.<commit_after>const { Command } = require('discord.js-commando')
const { oneLineCommaListsAnd } = require('common-tags')
const moment = require('moment')

module.exports = class ChannelInfoCommand extends Command {
  constructor (client) {
    super(client, {
      name: 'channelinfo',
      memberName: 'channelinfo',
      group: 'information',
      description: 'Displays information about a channel.',
      aliases: [
        'cinfo'
      ],
      clientPermissions: [
        'EMBED_LINKS'
      ],
      throttling: {
        usages: 2,
        duration: 10
      },
      args: [
        {
          key: 'channel',
          label: 'voice channel/text channel',
          prompt: 'What channel would you like to lookup?',
          type: 'channel'
        }
      ],
      guildOnly: true
    })
  }

  run (message, args) {
    var channel = args.channel
    if (channel.type === 'voice') {
      var channelUsers
      if (channel.members.size > 1) {
        channelUsers = oneLineCommaListsAnd`${channel.members.array()}`
      } else {
        channelUsers = 'None'
      }
      message.embed({
        author: { name: this.client.user.tag, icon_url: this.client.user.displayAvatarURL() },
        footer: { text: message.author.tag, icon_url: message.author.displayAvatarURL() },
        timestamp: new Date(),
        title: channel.name,
        description: `Since ${moment(channel.createdAt).format('llll')} ${moment.tz(moment.tz.guess()).format('z')}`,
        fields: [
          {
            'name': 'Type',
            'value': channel.type,
            'inline': true
          },
          {
            'name': 'ID',
            'value': channel.id,
            'inline': true
          },
          {
            'name': 'Position',
            'value': channel.rawPosition,
            'inline': true
          },
          {
            'name': 'Bitrate',
            'value': `${channel.bitrate}kbps`,
            'inline': true
          },
          {
            'name': 'User Count',
            'value': `${channel.members.size.toLocaleString()}/${channel.userLimit === 0 ? 'Infinity' : channel.userLimit}`,
            'inline': true
          },
          {
            'name': 'Users',
            'value': channelUsers,
            'inline': true
          }
        ],
        color: this.client.getClientColor(message)
      })
    } else if (channel.type === 'text') {
      message.embed({
        author: { name: this.client.user.tag, icon_url: this.client.user.displayAvatarURL() },
        footer: { text: message.author.tag, icon_url: message.author.displayAvatarURL() },
        timestamp: new Date(),
        title: channel.name,
        description: `Since ${moment(channel.createdAt).format('llll')} ${moment.tz(moment.tz.guess()).format('z')}`,
        fields: [
          {
            'name': 'Type',
            'value': channel.type,
            'inline': true
          },
          {
            'name': 'ID',
            'value': channel.id,
            'inline': true
          },
          {
            'name': 'Position',
            'value': channel.rawPosition,
            'inline': true
          }
        ],
        color: this.client.getClientColor(message)
      })
    } else {
      return message.reply('please choose a valid voice or text channel.')
    }
  }
}
<|endoftext|>


<commit_before>var helper = require('./util/helper');

var buildObjectFromKeyedArray = helper.buildObjectFromKeyedArray;
var buildObjectFromStringArray = helper.buildObjectFromStringArray;

// native interface constructor

function NativeInterface(client) {
    this.client = client;
}

// Native Geo Commands

NativeInterface.prototype.geoadd = function(locationName, point, zSetName, callBack) {
    this.client.send_command('geoadd', [zSetName, point.longitude, point.latitude, locationName], callBack);
};

NativeInterface.prototype.geoadd_multi = function(locationSet, zSetName, callBack) {

    var argArray = [zSetName];
    var locationName;
    var location;

    for (locationName in locationSet) {
        location = locationSet[locationName];
        argArray.push(location.longitude, location.latitude, locationName);
    }

    this.client.send_command('geoadd', argArray, callBack);
};

NativeInterface.prototype.geodist = function(locationA, locationB, units, zSetName, callBack) {
    this.client.send_command('geodist', [zSetName, locationA, locationB, units || 'm'], function(err, results) {
        if (err) {
            callBack(err, null);
        } else {
            callBack(null, results);
        }
    });
};

NativeInterface.prototype.geohash = function(member, zSetName, callBack) {

    var argArray = [zSetName].concat([member]);

    this.client.send_command('geohash', argArray, function(err, results) {
        if (err) {
            callBack(err, null);
        } else {
            callBack(null, results[0]);
        }
    });
};

NativeInterface.prototype.geohash_multi = function(members, zSetName, callBack) {

    var argArray = [zSetName].concat(members);
    var locationSet = {};
    var i;

    this.client.send_command('geohash', argArray, function(err, results) {
        if (err) {
            callBack(err, null);
        } else {
            for (i = 0; i < results.length; i++) {
                locationSet[members[i]] = {
                    hash: results[i]
                };
            }
            callBack(null, locationSet);
        }
    });
};

NativeInterface.prototype.geopos = function(member, zSetName, callBack) {

    var argArray = [zSetName].concat([member]);
    var location;

    this.client.send_command('geopos', argArray, function(err, results) {
        if (err) {
            callBack(err, null);
        } else {
            if (results.lenght > 0) {
                location = {
                    latitude: results[0][1],
                    longitude: results[0][0]
                };
            } else {
                location = null;
            }
            callBack(null, location);
        }
    });
};

NativeInterface.prototype.geopos_multi = function(members, zSetName, callBack) {

    var argArray = [zSetName].concat(members);
    var locationSet = {};
    var i;

    this.client.send_command('geopos', argArray, function(err, results) {
        if (err) {
            callBack(err, null);
        } else {
            for (i = 0; i < results.length; i++) {
                if (results[i] !== null) {
                    locationSet[members[i]] = {
                        latitude: results[i][1],
                        longitude: results[i][0]
                    };
                } else {
                    locationSet[members[i]] = null;
                }
            }
            callBack(null, locationSet);
        }
    });
};

NativeInterface.prototype.georadius = function(point, radius, options, zSetName, callBack) {
    var argArray = [zSetName].concat([point.longitude, point.latitude]);
    georadiusGeneric(this.client, 'georadius', argArray, radius, options, callBack);
};

NativeInterface.prototype.georadiusbymember = function(member, radius, options, zSetName, callBack) {
    var argArray = [zSetName].concat([member]);
    georadiusGeneric(this.client, 'georadiusbymember', argArray, radius, options, callBack);
};

NativeInterface.prototype.nearby = NativeInterface.prototype.georadius;
NativeInterface.prototype.nearbymember = NativeInterface.prototype.georadiusbymember;

// Native Ordered Set Commands

NativeInterface.prototype.del = function(zSetName, callBack) {
    this.client.del(zSetName, callBack);
};

NativeInterface.prototype.zrem = function(locations, zSetName, callBack) {
    var argArray = [zSetName].concat(locations);
    this.client.zrem(argArray, callBack);
};

function georadiusGeneric(client, type, argArray, radius, options, callBack) {

    var units = options.units || 'm';
    var withDistances = options.withDistances;
    var withCoordinates = options.withCoordinates;
    var withHashes = options.withHashes;
    var order = options.order;
    var count = options.count;

    argArray.push(radius, units);

    if (withDistances === true) {
        argArray.push('WITHDIST');
    }

    if (withHashes === true) {
        argArray.push('WITHHASH');
    }

    if (withCoordinates === true) {
        argArray.push('WITHCOORD');
    }

    if (order) {
        argArray.push(order === true ? 'ASC' : order);
    }

    if (count) {
        argArray.push('COUNT', count);
    }

    client.send_command(type, argArray, function(err, locations) {

        if (err) {
            callBack(err, null);
        } else {
            if (withCoordinates || withDistances || withHashes) {
                locations = processLocations(locations, withCoordinates, withDistances, withHashes, units);
            } else {
                Object.defineProperty(locations, 'locationSet', {
                    get: function() {
                        return buildObjectFromStringArray(this);
                    }
                });
            }

            callBack(null, locations);
        }
    });
}

function processLocations(results, withCoordinates, withDistances, withHashes, units) {

    var locations = [];
    var location;
    var result;
    var i;
    var j;

    for (i = 0; i < results.length; i++) {

        j = 1;
        result = results[i];

        if (result !== null) {

            location = {
                key: result[0]
            };

            if (withDistances === true) {
                location.distance = parseFloat(result[j]);
                j++;
            }

            if (withHashes === true) {
                location.hash = result[j];
                j++;
            }

            if (withCoordinates === true) {
                location.latitude = parseFloat(result[j][1]);
                location.longitude = parseFloat(result[j][0]);
            }
        } else {
            location = null;
        }

        locations.push(location);
    }

    Object.defineProperty(locations, 'locationSet', {
        get: function() {
            return buildObjectFromKeyedArray(this);
        }
    });

    return locations;
}

module.exports = NativeInterface;
<commit_msg>fixed length<commit_after>var helper = require('./util/helper');

var buildObjectFromKeyedArray = helper.buildObjectFromKeyedArray;
var buildObjectFromStringArray = helper.buildObjectFromStringArray;

// native interface constructor

function NativeInterface(client) {
    this.client = client;
}

// Native Geo Commands

NativeInterface.prototype.geoadd = function(locationName, point, zSetName, callBack) {
    this.client.send_command('geoadd', [zSetName, point.longitude, point.latitude, locationName], callBack);
};

NativeInterface.prototype.geoadd_multi = function(locationSet, zSetName, callBack) {

    var argArray = [zSetName];
    var locationName;
    var location;

    for (locationName in locationSet) {
        location = locationSet[locationName];
        argArray.push(location.longitude, location.latitude, locationName);
    }

    this.client.send_command('geoadd', argArray, callBack);
};

NativeInterface.prototype.geodist = function(locationA, locationB, units, zSetName, callBack) {
    this.client.send_command('geodist', [zSetName, locationA, locationB, units || 'm'], function(err, results) {
        if (err) {
            callBack(err, null);
        } else {
            callBack(null, results);
        }
    });
};

NativeInterface.prototype.geohash = function(member, zSetName, callBack) {

    var argArray = [zSetName].concat([member]);

    this.client.send_command('geohash', argArray, function(err, results) {
        if (err) {
            callBack(err, null);
        } else {
            callBack(null, results[0]);
        }
    });
};

NativeInterface.prototype.geohash_multi = function(members, zSetName, callBack) {

    var argArray = [zSetName].concat(members);
    var locationSet = {};
    var i;

    this.client.send_command('geohash', argArray, function(err, results) {
        if (err) {
            callBack(err, null);
        } else {
            for (i = 0; i < results.length; i++) {
                locationSet[members[i]] = {
                    hash: results[i]
                };
            }
            callBack(null, locationSet);
        }
    });
};

NativeInterface.prototype.geopos = function(member, zSetName, callBack) {

    var argArray = [zSetName].concat([member]);
    var location;

    this.client.send_command('geopos', argArray, function(err, results) {
        if (err) {
            callBack(err, null);
        } else {
            if (results.length > 0) {
                location = {
                    latitude: results[0][1],
                    longitude: results[0][0]
                };
            } else {
                location = null;
            }
            callBack(null, location);
        }
    });
};

NativeInterface.prototype.geopos_multi = function(members, zSetName, callBack) {

    var argArray = [zSetName].concat(members);
    var locationSet = {};
    var i;

    this.client.send_command('geopos', argArray, function(err, results) {
        if (err) {
            callBack(err, null);
        } else {
            for (i = 0; i < results.length; i++) {
                if (results[i] !== null) {
                    locationSet[members[i]] = {
                        latitude: results[i][1],
                        longitude: results[i][0]
                    };
                } else {
                    locationSet[members[i]] = null;
                }
            }
            callBack(null, locationSet);
        }
    });
};

NativeInterface.prototype.georadius = function(point, radius, options, zSetName, callBack) {
    var argArray = [zSetName].concat([point.longitude, point.latitude]);
    georadiusGeneric(this.client, 'georadius', argArray, radius, options, callBack);
};

NativeInterface.prototype.georadiusbymember = function(member, radius, options, zSetName, callBack) {
    var argArray = [zSetName].concat([member]);
    georadiusGeneric(this.client, 'georadiusbymember', argArray, radius, options, callBack);
};

NativeInterface.prototype.nearby = NativeInterface.prototype.georadius;
NativeInterface.prototype.nearbymember = NativeInterface.prototype.georadiusbymember;

// Native Ordered Set Commands

NativeInterface.prototype.del = function(zSetName, callBack) {
    this.client.del(zSetName, callBack);
};

NativeInterface.prototype.zrem = function(locations, zSetName, callBack) {
    var argArray = [zSetName].concat(locations);
    this.client.zrem(argArray, callBack);
};

function georadiusGeneric(client, type, argArray, radius, options, callBack) {

    var units = options.units || 'm';
    var withDistances = options.withDistances;
    var withCoordinates = options.withCoordinates;
    var withHashes = options.withHashes;
    var order = options.order;
    var count = options.count;

    argArray.push(radius, units);

    if (withDistances === true) {
        argArray.push('WITHDIST');
    }

    if (withHashes === true) {
        argArray.push('WITHHASH');
    }

    if (withCoordinates === true) {
        argArray.push('WITHCOORD');
    }

    if (order) {
        argArray.push(order === true ? 'ASC' : order);
    }

    if (count) {
        argArray.push('COUNT', count);
    }

    client.send_command(type, argArray, function(err, locations) {

        if (err) {
            callBack(err, null);
        } else {
            if (withCoordinates || withDistances || withHashes) {
                locations = processLocations(locations, withCoordinates, withDistances, withHashes, units);
            } else {
                Object.defineProperty(locations, 'locationSet', {
                    get: function() {
                        return buildObjectFromStringArray(this);
                    }
                });
            }

            callBack(null, locations);
        }
    });
}

function processLocations(results, withCoordinates, withDistances, withHashes, units) {

    var locations = [];
    var location;
    var result;
    var i;
    var j;

    for (i = 0; i < results.length; i++) {

        j = 1;
        result = results[i];

        if (result !== null) {

            location = {
                key: result[0]
            };

            if (withDistances === true) {
                location.distance = parseFloat(result[j]);
                j++;
            }

            if (withHashes === true) {
                location.hash = result[j];
                j++;
            }

            if (withCoordinates === true) {
                location.latitude = parseFloat(result[j][1]);
                location.longitude = parseFloat(result[j][0]);
            }
        } else {
            location = null;
        }

        locations.push(location);
    }

    Object.defineProperty(locations, 'locationSet', {
        get: function() {
            return buildObjectFromKeyedArray(this);
        }
    });

    return locations;
}

module.exports = NativeInterface;
<|endoftext|>


<commit_before>// --
// OTRS.Config.js - provides the JS config
// Copyright (C) 2001-2010 OTRS AG, http://otrs.org/\n";
// --
// $Id: OTRS.Config.js,v 1.9 2010-06-01 08:31:45 mn Exp $
// --
// This software comes with ABSOLUTELY NO WARRANTY. For details, see
// the enclosed file COPYING for license information (AGPL). If you
// did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
// --

"use strict";

var OTRS = OTRS || {};

/**
 * @namespace
 * @exports TargetNS as OTRS.Config
 * @description
 *      This namespace contains the config options and functions.
 */
OTRS.Config = (function (TargetNS) {
    var Config = {},
        ConfigPrefix = 'Config';

    /**
     * @function
     *      Sets a single option value
     * @param {String} Key The name of the config option (also combined ones like Richtext.Width)
     * @param {Object} Value The value of the option. Can be every kind of javascript variable type.
     * @return nothing
     */
    TargetNS.Set = function (Key, Value) {
        var Keys = Key.split('.'),
            KeyToken,
            ConfigLevel = Config,
            Count = 0;

        for (KeyToken in Keys) {
            if (Keys.hasOwnProperty(KeyToken)) {
                if (Keys.length === Count + 1) {
                    ConfigLevel[ConfigPrefix + Keys[KeyToken]] = Value;
                }
                else if (typeof ConfigLevel[ConfigPrefix + Keys[KeyToken]] === 'undefined') {
                    ConfigLevel[ConfigPrefix + Keys[KeyToken]] = {};
                    ConfigLevel = ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                else {
                    ConfigLevel = ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                Count++;
            }
        }
    };

    /**
     * @function
     *      Gets a single option value
     * @param {String} Key The name of the config option (also combined ones like Richtext.Width)
     * @return {Object} The value of the option. Can be every kind of javascript variable type.
     */
    TargetNS.Get = function (Key) {
        var Keys = Key.split('.'),
            KeyToken,
            ConfigLevel = Config,
            Count = 0;

        for (KeyToken in Keys) {
            if (Keys.hasOwnProperty(KeyToken)) {
                if (Keys.length === Count + 1) {
                    return ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                else {
                    ConfigLevel = ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                Count++;
            }
        }
    };

    /**
     * @function
     * @return nothing
     *      This function includes the given data into the config hash
     * @param {Object} Data The config data to include as a javascript object
     * @param {String} Key The key in the config where the data structure is saved to
     */
    TargetNS.AddConfig = function (Data, Key) {
        function CopyObject(Data) {
            return (OTRS.JSON.Parse(OTRS.JSON.Stringify(Data)));
        }

        var ConfigOptions,
            Keys,
            KeyToken,
            ConfigLevel = Config,
            Count = 0;

        /* Because objects in javascript are called-by-reference, the given data object is just a reference.
         * Saving this to the config hash would only save the reference to the original object.
         * If this original object is changed (or if the object is changed in the config hash) it would also
         * "change" the other references.
         * We have to *really* copy the object, to be sure to have no reference to the given parameter object.
         */
        ConfigOptions = CopyObject(Data);

        if (typeof Key === 'undefined') {
            $.each(Data, function (Key, Value) {
                ConfigLevel[ConfigPrefix + Key] = Value;
            });
        }
        else {
            Keys = Key.split('.');
            for (KeyToken in Keys) {
                if (Keys.length === Count + 1) {
                    ConfigLevel[ConfigPrefix + Keys[KeyToken]] = ConfigOptions;
                }
                else if (typeof ConfigLevel[ConfigPrefix + Keys[KeyToken]] === 'undefined') {
                    ConfigLevel = ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                else {
                    ConfigLevel = ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                Count++;
            }
        }
    };

    /*
     * Different config options, that are provided from start
     */

    /**
     * @field
     * @description This variable contains a hash of blacklisted browsers and their recognition functions.
     * Each function returns true, if the browsers is detected
     */
    TargetNS.AddConfig({
        'Microsoft Internet Explorer 5.5': function () {
            return ($.browser.msie && $.browser.version === '5.5');
        },
        'Microsoft Internet Explorer 6': function () {
            return ($.browser.msie && $.browser.version === '6.0');
        },
        'Konqueror (without WebKit engine)': function () {
            return ($.browser.webkit && navigator.vendor === 'KDE');
        },
        'Netscape, old Mozilla, old Firefox': function () {
            var BrowserVersion,
                BrowserDetected = false;
            if ($.browser.mozilla) {
                BrowserVersion = $.browser.version.split('.');
                if (parseInt(BrowserVersion[0], 10) < 1 || parseInt(BrowserVersion[1], 10) < 8 || parseInt(BrowserVersion[2], 10) < 1) {
                    BrowserDetected = true;
                }
            }
            return BrowserDetected;
        }
    }, 'BrowserBlackList');

    return TargetNS;
}(OTRS.Config || {}));<commit_msg>Updated JSDoc.<commit_after>// --
// OTRS.Config.js - provides the JS config
// Copyright (C) 2001-2010 OTRS AG, http://otrs.org/\n";
// --
// $Id: OTRS.Config.js,v 1.10 2010-06-01 08:34:05 mn Exp $
// --
// This software comes with ABSOLUTELY NO WARRANTY. For details, see
// the enclosed file COPYING for license information (AGPL). If you
// did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
// --

"use strict";

var OTRS = OTRS || {};

/**
 * @namespace
 * @exports TargetNS as OTRS.Config
 * @description
 *      This namespace contains the config options and functions.
 */
OTRS.Config = (function (TargetNS) {
    var Config = {},
        ConfigPrefix = 'Config';

    /**
     * @function
     *      Sets a single option value
     * @param {String} Key The name of the config option (also combined ones like Richtext.Width)
     * @param {Object} Value The value of the option. Can be every kind of javascript variable type.
     * @return nothing
     */
    TargetNS.Set = function (Key, Value) {
        var Keys = Key.split('.'),
            KeyToken,
            ConfigLevel = Config,
            Count = 0;

        for (KeyToken in Keys) {
            if (Keys.hasOwnProperty(KeyToken)) {
                if (Keys.length === Count + 1) {
                    ConfigLevel[ConfigPrefix + Keys[KeyToken]] = Value;
                }
                else if (typeof ConfigLevel[ConfigPrefix + Keys[KeyToken]] === 'undefined') {
                    ConfigLevel[ConfigPrefix + Keys[KeyToken]] = {};
                    ConfigLevel = ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                else {
                    ConfigLevel = ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                Count++;
            }
        }
    };

    /**
     * @function
     *      Gets a single option value
     * @param {String} Key The name of the config option (also combined ones like Richtext.Width)
     * @return {Object} The value of the option. Can be every kind of javascript variable type.
     */
    TargetNS.Get = function (Key) {
        var Keys = Key.split('.'),
            KeyToken,
            ConfigLevel = Config,
            Count = 0;

        for (KeyToken in Keys) {
            if (Keys.hasOwnProperty(KeyToken)) {
                if (Keys.length === Count + 1) {
                    return ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                else {
                    ConfigLevel = ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                Count++;
            }
        }
    };

    /**
     * @function
     * @return nothing
     *      This function includes the given data into the config hash
     * @param {Object} Data The config data to include as a javascript object
     * @param {String} Key The key in the config where the data structure is saved to. If undefined, the Data is added to the root of the hash.
     */
    TargetNS.AddConfig = function (Data, Key) {
        function CopyObject(Data) {
            return (OTRS.JSON.Parse(OTRS.JSON.Stringify(Data)));
        }

        var ConfigOptions,
            Keys,
            KeyToken,
            ConfigLevel = Config,
            Count = 0;

        /* Because objects in javascript are called-by-reference, the given data object is just a reference.
         * Saving this to the config hash would only save the reference to the original object.
         * If this original object is changed (or if the object is changed in the config hash) it would also
         * "change" the other references.
         * We have to *really* copy the object, to be sure to have no reference to the given parameter object.
         */
        ConfigOptions = CopyObject(Data);

        if (typeof Key === 'undefined') {
            $.each(Data, function (Key, Value) {
                ConfigLevel[ConfigPrefix + Key] = Value;
            });
        }
        else {
            Keys = Key.split('.');
            for (KeyToken in Keys) {
                if (Keys.length === Count + 1) {
                    ConfigLevel[ConfigPrefix + Keys[KeyToken]] = ConfigOptions;
                }
                else if (typeof ConfigLevel[ConfigPrefix + Keys[KeyToken]] === 'undefined') {
                    ConfigLevel = ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                else {
                    ConfigLevel = ConfigLevel[ConfigPrefix + Keys[KeyToken]];
                }
                Count++;
            }
        }
    };

    /*
     * Different config options, that are provided from start
     */

    /**
     * @field
     * @description This variable contains a hash of blacklisted browsers and their recognition functions.
     * Each function returns true, if the browsers is detected
     */
    TargetNS.AddConfig({
        'Microsoft Internet Explorer 5.5': function () {
            return ($.browser.msie && $.browser.version === '5.5');
        },
        'Microsoft Internet Explorer 6': function () {
            return ($.browser.msie && $.browser.version === '6.0');
        },
        'Konqueror (without WebKit engine)': function () {
            return ($.browser.webkit && navigator.vendor === 'KDE');
        },
        'Netscape, old Mozilla, old Firefox': function () {
            var BrowserVersion,
                BrowserDetected = false;
            if ($.browser.mozilla) {
                BrowserVersion = $.browser.version.split('.');
                if (parseInt(BrowserVersion[0], 10) < 1 || parseInt(BrowserVersion[1], 10) < 8 || parseInt(BrowserVersion[2], 10) < 1) {
                    BrowserDetected = true;
                }
            }
            return BrowserDetected;
        }
    }, 'BrowserBlackList');

    return TargetNS;
}(OTRS.Config || {}));<|endoftext|>