<commit_before>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**

If you don't know which DAO implementation to choose, EasyDAO is
    ready to help. Simply require foam.dao.EasyDAO and create() with the flags
    to indicate what behavior you're looking for. Under the hood, EasyDAO
    will create one or more DAO instances to service your requirements.

Since EasyDAO is a proxy, just use it like you would any other
    DAO, without worrying about the internal DAO doing the work.

*/
foam.CLASS({
  package: 'foam.dao',
  name: 'EasyDAO',
  extends: 'foam.dao.ProxyDAO',

  implements: [ 'foam.mlang.Expressions' ],

  requires: [
    'foam.dao.MDAO',
    'foam.dao.JournalDAO',
    'foam.dao.GUIDDAO',
    'foam.dao.IDBDAO',
    'foam.dao.SequenceNumberDAO',
    'foam.dao.CachingDAO',
    'foam.dao.SyncDAO',
    'foam.dao.ContextualizingDAO',
    'foam.dao.DeDupDAO',
    'foam.dao.ClientDAO',
    'foam.box.Context',
    'foam.box.HTTPBox',
    'foam.box.SocketBox',
    //'foam.core.dao.MergeDAO',
    //'foam.core.dao.MigrationDAO',
    //'foam.core.dao.VersionNoDAO',
    //'foam.dao.EasyClientDAO',
    'foam.dao.LoggingDAO',
    'foam.dao.TimingDAO',
  ],

  imports: [ 'document' ],

  constants: {
    // Aliases for daoType
    ALIASES: {
      ARRAY: 'foam.dao.ArrayDAO',
      MDAO:  'foam.dao.MDAO',
      IDB:   'foam.dao.IDBDAO',
      LOCAL: 'foam.dao.LocalStorageDAO',
    }
  },

  properties: [
    {
      /** The developer-friendly name for this EasyDAO. */
      name: 'name',
      factory: function() { return this.of.id; },
    },
    {
      /** This is set automatically when you create an EasyDAO.
        @private */
      name: 'delegate'
    },
    {
      /** Have EasyDAO use a sequence number to index items. Note that
        .seqNo and .guid features are mutually
        exclusive. */
      class: 'Boolean',
      name: 'seqNo',
      value: false,
    },
    {
      /** Have EasyDAO generate guids to index items. Note that .seqNo and .guid features are mutually exclusive. */
      class: 'Boolean',
      name: 'guid',
      label: 'GUID',
      value: false,
    },
    {
      /** The property on your items to use to store the sequence number or guid. This is required for .seqNo or .guid mode. */
      name: 'seqProperty',
      class: 'Property',
    },
    {
      /** Enable local in-memory caching of the DAO. */
      class: 'Boolean',
      name: 'cache',
      value: false,
    },
    {
      /** Enable value de-duplication to save memory when caching. */
      class: 'Boolean',
      name: 'dedup',
      value: false,
    },
    {
      /** Keep a history of all state changes to the DAO. */
      name: 'journal',
      value: false
    },
    {
      /** Enable logging on the DAO. */
      class: 'Boolean',
      name: 'logging',
      value: false,
    },
    {
      /** Enable time tracking for concurrent DAO operations. */
      class: 'Boolean',
      name: 'timing',
      value: false,
    },
    {
      /** Contextualize objects on .find, re-creating them with this EasyDAO's
        exports, as if they were children of this EasyDAO. */
      class: 'Boolean',
      name: 'contextualize',
      value: false,
    },
//     {
//       class: 'Boolean',
//       name: 'cloning',
//       value: false,
//       //documentation: "True to clone results on select"
//     },
    {
      /**

Selects the basic functionality this EasyDAO should provide.
        You can specify an instance of a DAO model definition such as
        MDAO, or a constant indicating your requirements.

Choices are:

IDB: Use IndexDB for storage.

LOCAL: Use local storage.

MDAO: Use non-persistent in-memory storage.

      */
      name: 'daoType',
      value: 'foam.dao.IDBDAO',
    },
    {
      /** Automatically generate indexes as necessary, if using an MDAO or cache. */
      class: 'Boolean',
      name: 'autoIndex',
      value: false,
    },
//     {
//       /** Creates an internal MigrationDAO and applies the given array of MigrationRule. */
//       class: 'FObjectArray',
//       name: 'migrationRules',
//       of: 'foam.core.dao.MigrationRule',
//     },
    {
      /** Turn on to activate synchronization with a server. Specify serverUri
        and syncProperty as well. */
      class: 'Boolean',
      name: 'syncWithServer',
      value: false
    },
    {
      /** Setting to true activates polling, periodically checking in with
        the server. If sockets are used, polling is optional as the server
        can push changes to this client. */
      class: 'Boolean',
      name: 'syncPolling',
      value: true
    },
    {
      /** The URI of the server to use. If sockets is true, this will use
        a web socket, otherwise HTTP to contact the server-side DAO. On your
        server, use an EasyDAO with isServer:true to provide the other end
        of the connection. */
      class: 'String',
      name: 'serverUri',
      factory: function() {
        return this.document && this.document.location ?
            this.document.location.origin + '/api' :
            '';
      }
    },
    {
      /** Set to true if you are running this on a server, and clients will
        synchronize with this DAO. */
      class: 'Boolean',
      name: 'isServer',
      value: false
    },
    {
      /** The property to synchronize on. This is typically an integer value
        indicating the version last seen on the remote. */
      name: 'syncProperty'
    },
    {
      /** If true, uses sockets instead of HTTP for server communication. */
      name: 'sockets',
      value: false
    },
    'testData'
  ],

  methods: [
    function init() {
      /**

On initialization, the EasyDAO creates an appropriate chain of
        internal EasyDAO instances based on the EasyDAO
        property settings.

This process is transparent to the developer, and you can use your
        EasyDAO like any other DAO.

      */
      this.SUPER(args);

      var daoType = typeof this.daoType === 'string' ?
        this.ALIASES[this.daoType] || this.daoType :
        this.daoType;

      var daoModel = typeof daoType === 'string' ?
        this.lookup(daoType) || global[daoType] :
        daoType;

      var params = { of: this.of };

      if ( ! daoModel ) {
        this.warn(
          "EasyDAO: Unknown DAO Type.  Add '" + daoType + "' to requires: list."
        );
      }

      if ( this.name && daoModel.getAxiomByName('name')  ) params.name = this.name;
      if ( daoModel.getAxiomByName('autoIndex') ) params.autoIndex = this.autoIndex;
      //if ( this.seqNo || this.guid ) params.property = this.seqProperty;

      var dao = daoModel.create(params, this.__subContext__);

      if ( this.MDAO.isInstance(dao) ) {
        this.mdao = dao;
        if ( this.dedup ) dao = this.DeDupDAO.create({delegate: dao});
      } else {
//         if ( this.migrationRules && this.migrationRules.length ) {
//           dao = this.MigrationDAO.create({
//             delegate: dao,
//             rules: this.migrationRules,
//             name: this.model.id + "_" + daoModel.id + "_" + this.name
//           });
//         }
        if ( this.cache ) {
          this.mdao = this.MDAO.create(params);
          dao = this.CachingDAO.create({
            cache: this.dedup ?
              this.mdao :
              this.DeDupDAO.create({delegate: this.mdao}),
            src: dao,
            of: this.model});
        }
      }

      if ( this.seqNo && this.guid ) throw "EasyDAO 'seqNo' and 'guid' features are mutually exclusive.";

      if ( this.seqNo ) {
        var args = {__proto__: params, delegate: dao, of: this.of};
        if ( this.seqProperty ) args.property = this.seqProperty;
        dao = this.SequenceNumberDAO.create(args);
      }

      if ( this.guid ) {
        var args = {__proto__: params, delegate: dao, of: this.of};
        if ( this.seqProperty ) args.property = this.seqProperty;
        dao = this.GUIDDAO.create(args);
      }

      var cls = this.of$cls;

      if ( this.syncWithServer && this.isServer ) throw "isServer and syncWithServer are mutually exclusive.";

      if ( this.syncWithServer || this.isServer ) {
        if ( ! this.syncProperty ) {
          this.syncProperty = cls.SYNC_PROPERTY;
          if ( ! this.syncProperty ) {
            throw "EasyDAO sync with class " + cls.id + " invalid. Sync requires a sync property be set, or be of a class including a property 'sync_property'.";
          }
        }
      }

      if ( this.syncWithServer ) {
        var boxContext = this.Context.create();
        var boxSender = ( this.sockets ) ?
          this.SocketBox.create({
            address: this.serverUri
          }, boxContext) :
          this.HTTPBox.create({
             url: this.serverUri,
             method: 'POST'
          }, boxContext); // TODO: retry?
          //TODO: EasyClientDAO

        dao = this.SyncDAO.create({
          remoteDAO: this.ClientDAO.create({
              name: this.name,
              delegate: boxSender
          }, boxContext),
          syncProperty: this.syncProperty,
          delegate: dao,
          pollingFrequency: 1000
        });
        dao.syncRecordDAO = foam.dao.EasyDAO.create({
          of: dao.SyncRecord,
          cache: true,
          daoType: this.daoType,
          name: this.name + '_SyncRecords'
        });
      }

//       if ( this.isServer ) {
//         dao = this.VersionNoDAO.create({
//           delegate: dao,
//           property: this.syncProperty,
//           version: 2
//         });
//       }

      if ( this.contextualize ) {
        dao = this.ContextualizingDAO.create({delegate: dao});
      }

      if ( this.journal ) {
        dao = this.JournalDAO.create({
          delegate: dao,
          journal: foam.dao.EasyDAO.create({
            of:      foam.dao.JournalEntry,
            daoType: this.daoType,
            seqNo:   true,
            name:    this.name + '_Journal'
          })
        });
      }

      if ( this.timing  ) {
        dao = this.TimingDAO.create({ name: this.name + 'DAO', delegate: dao });
      }

      if ( this.logging ) {
        dao = this.LoggingDAO.create({ delegate: dao });
      }

      this.delegate = dao;

      if ( this.testData ) {
        var self = this;
        this.select(this.COUNT()).then(function(c) {
          // Only load testData if DAO is empty
          if ( c.value ) return;

          foam.json.parse(self.testData, self.of).forEach(
            function(o) { self.put(o); }
          );
        });
      }
    },

    /** Only relevant if cache is true or if daoType
       was set to MDAO, but harmless otherwise. Generates an index
       for a query over all specified properties together.
       @arg var_args specify any number of Properties to be indexed.
    */
    function addIndex(/* foam.core.Property* */ var_args) {
      this.mdao && this.mdao.addIndex.apply(this.mdao, arguments);
      return this;
    },

    /** Only relevant if cache is true or if daoType
      was set to MDAO, but harmless otherwise. Adds an existing index
      to the MDAO.
      @arg index The index to add.
    */
    function addRawIndex(/* foam.dao.index.Index */ index) {
      this.mdao && this.mdao.addRawIndex.apply(this.mdao, arguments);
      return this;
    }
  ]
});
<commit_msg>Formatting.<commit_after>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**

If you don't know which DAO implementation to choose, EasyDAO is
    ready to help. Simply require foam.dao.EasyDAO and create() with the flags
    to indicate what behavior you're looking for. Under the hood, EasyDAO
    will create one or more DAO instances to service your requirements.

Since EasyDAO is a proxy, just use it like you would any other
    DAO, without worrying about the internal DAO doing the work.

*/
foam.CLASS({
  package: 'foam.dao',
  name: 'EasyDAO',
  extends: 'foam.dao.ProxyDAO',

  implements: [ 'foam.mlang.Expressions' ],

  requires: [
    'foam.dao.MDAO',
    'foam.dao.JournalDAO',
    'foam.dao.GUIDDAO',
    'foam.dao.IDBDAO',
    'foam.dao.SequenceNumberDAO',
    'foam.dao.CachingDAO',
    'foam.dao.SyncDAO',
    'foam.dao.ContextualizingDAO',
    'foam.dao.DeDupDAO',
    'foam.dao.ClientDAO',
    'foam.box.Context',
    'foam.box.HTTPBox',
    'foam.box.SocketBox',
    //'foam.core.dao.MergeDAO',
    //'foam.core.dao.MigrationDAO',
    //'foam.core.dao.VersionNoDAO',
    //'foam.dao.EasyClientDAO',
    'foam.dao.LoggingDAO',
    'foam.dao.TimingDAO'
  ],

  imports: [ 'document' ],

  constants: {
    // Aliases for daoType
    ALIASES: {
      ARRAY: 'foam.dao.ArrayDAO',
      MDAO:  'foam.dao.MDAO',
      IDB:   'foam.dao.IDBDAO',
      LOCAL: 'foam.dao.LocalStorageDAO'
    }
  },

  properties: [
    {
      /** The developer-friendly name for this EasyDAO. */
      name: 'name',
      factory: function() { return this.of.id; }
    },
    {
      /** This is set automatically when you create an EasyDAO.
        @private */
      name: 'delegate'
    },
    {
      /** Have EasyDAO use a sequence number to index items. Note that
        .seqNo and .guid features are mutually
        exclusive. */
      class: 'Boolean',
      name: 'seqNo',
      value: false
    },
    {
      /** Have EasyDAO generate guids to index items. Note that .seqNo and .guid features are mutually exclusive. */
      class: 'Boolean',
      name: 'guid',
      label: 'GUID',
      value: false
    },
    {
      /** The property on your items to use to store the sequence number or guid. This is required for .seqNo or .guid mode. */
      name: 'seqProperty',
      class: 'Property'
    },
    {
      /** Enable local in-memory caching of the DAO. */
      class: 'Boolean',
      name: 'cache',
      value: false
    },
    {
      /** Enable value de-duplication to save memory when caching. */
      class: 'Boolean',
      name: 'dedup',
      value: false,
    },
    {
      /** Keep a history of all state changes to the DAO. */
      name: 'journal',
      value: false
    },
    {
      /** Enable logging on the DAO. */
      class: 'Boolean',
      name: 'logging',
      value: false,
    },
    {
      /** Enable time tracking for concurrent DAO operations. */
      class: 'Boolean',
      name: 'timing',
      value: false
    },
    {
      /** Contextualize objects on .find, re-creating them with this EasyDAO's
        exports, as if they were children of this EasyDAO. */
      class: 'Boolean',
      name: 'contextualize',
      value: false
    },
//     {
//       class: 'Boolean',
//       name: 'cloning',
//       value: false,
//       //documentation: "True to clone results on select"
//     },
    {
      /**

Selects the basic functionality this EasyDAO should provide.
        You can specify an instance of a DAO model definition such as
        MDAO, or a constant indicating your requirements.

Choices are:

IDB: Use IndexDB for storage.

LOCAL: Use local storage.

MDAO: Use non-persistent in-memory storage.

      */
      name: 'daoType',
      value: 'foam.dao.IDBDAO'
    },
    {
      /** Automatically generate indexes as necessary, if using an MDAO or cache. */
      class: 'Boolean',
      name: 'autoIndex',
      value: false
    },
//     {
//       /** Creates an internal MigrationDAO and applies the given array of MigrationRule. */
//       class: 'FObjectArray',
//       name: 'migrationRules',
//       of: 'foam.core.dao.MigrationRule',
//     },
    {
      /** Turn on to activate synchronization with a server. Specify serverUri
        and syncProperty as well. */
      class: 'Boolean',
      name: 'syncWithServer',
      value: false
    },
    {
      /** Setting to true activates polling, periodically checking in with
        the server. If sockets are used, polling is optional as the server
        can push changes to this client. */
      class: 'Boolean',
      name: 'syncPolling',
      value: true
    },
    {
      /** The URI of the server to use. If sockets is true, this will use
        a web socket, otherwise HTTP to contact the server-side DAO. On your
        server, use an EasyDAO with isServer:true to provide the other end
        of the connection. */
      class: 'String',
      name: 'serverUri',
      factory: function() {
        return this.document && this.document.location ?
            this.document.location.origin + '/api' :
            '';
      }
    },
    {
      /** Set to true if you are running this on a server, and clients will
        synchronize with this DAO. */
      class: 'Boolean',
      name: 'isServer',
      value: false
    },
    {
      /** The property to synchronize on. This is typically an integer value
        indicating the version last seen on the remote. */
      name: 'syncProperty'
    },
    {
      /** If true, uses sockets instead of HTTP for server communication. */
      name: 'sockets',
      value: false
    },
    'testData'
  ],

  methods: [
    function init() {
      /**

On initialization, the EasyDAO creates an appropriate chain of
        internal EasyDAO instances based on the EasyDAO
        property settings.

This process is transparent to the developer, and you can use your
        EasyDAO like any other DAO.

      */
      this.SUPER(args);

      var daoType = typeof this.daoType === 'string' ?
        this.ALIASES[this.daoType] || this.daoType :
        this.daoType;

      var daoModel = typeof daoType === 'string' ?
        this.lookup(daoType) || global[daoType] :
        daoType;

      var params = { of: this.of };

      if ( ! daoModel ) {
        this.warn(
          "EasyDAO: Unknown DAO Type.  Add '" + daoType + "' to requires: list."
        );
      }

      if ( this.name && daoModel.getAxiomByName('name')  ) params.name = this.name;
      if ( daoModel.getAxiomByName('autoIndex') ) params.autoIndex = this.autoIndex;
      //if ( this.seqNo || this.guid ) params.property = this.seqProperty;

      var dao = daoModel.create(params, this.__subContext__);

      if ( this.MDAO.isInstance(dao) ) {
        this.mdao = dao;
        if ( this.dedup ) dao = this.DeDupDAO.create({delegate: dao});
      } else {
//         if ( this.migrationRules && this.migrationRules.length ) {
//           dao = this.MigrationDAO.create({
//             delegate: dao,
//             rules: this.migrationRules,
//             name: this.model.id + "_" + daoModel.id + "_" + this.name
//           });
//         }
        if ( this.cache ) {
          this.mdao = this.MDAO.create(params);
          dao = this.CachingDAO.create({
            cache: this.dedup ?
              this.mdao :
              this.DeDupDAO.create({delegate: this.mdao}),
            src: dao,
            of: this.model});
        }
      }

      if ( this.seqNo && this.guid ) throw "EasyDAO 'seqNo' and 'guid' features are mutually exclusive.";

      if ( this.seqNo ) {
        var args = {__proto__: params, delegate: dao, of: this.of};
        if ( this.seqProperty ) args.property = this.seqProperty;
        dao = this.SequenceNumberDAO.create(args);
      }

      if ( this.guid ) {
        var args = {__proto__: params, delegate: dao, of: this.of};
        if ( this.seqProperty ) args.property = this.seqProperty;
        dao = this.GUIDDAO.create(args);
      }

      var cls = this.of$cls;

      if ( this.syncWithServer && this.isServer ) throw "isServer and syncWithServer are mutually exclusive.";

      if ( this.syncWithServer || this.isServer ) {
        if ( ! this.syncProperty ) {
          this.syncProperty = cls.SYNC_PROPERTY;
          if ( ! this.syncProperty ) {
            throw "EasyDAO sync with class " + cls.id + " invalid. Sync requires a sync property be set, or be of a class including a property 'sync_property'.";
          }
        }
      }

      if ( this.syncWithServer ) {
        var boxContext = this.Context.create();
        var boxSender = ( this.sockets ) ?
          this.SocketBox.create({
            address: this.serverUri
          }, boxContext) :
          this.HTTPBox.create({
             url: this.serverUri,
             method: 'POST'
          }, boxContext); // TODO: retry?
          //TODO: EasyClientDAO

        dao = this.SyncDAO.create({
          remoteDAO: this.ClientDAO.create({
              name: this.name,
              delegate: boxSender
          }, boxContext),
          syncProperty: this.syncProperty,
          delegate: dao,
          pollingFrequency: 1000
        });
        dao.syncRecordDAO = foam.dao.EasyDAO.create({
          of: dao.SyncRecord,
          cache: true,
          daoType: this.daoType,
          name: this.name + '_SyncRecords'
        });
      }

//       if ( this.isServer ) {
//         dao = this.VersionNoDAO.create({
//           delegate: dao,
//           property: this.syncProperty,
//           version: 2
//         });
//       }

      if ( this.contextualize ) {
        dao = this.ContextualizingDAO.create({delegate: dao});
      }

      if ( this.journal ) {
        dao = this.JournalDAO.create({
          delegate: dao,
          journal: foam.dao.EasyDAO.create({
            of:      foam.dao.JournalEntry,
            daoType: this.daoType,
            seqNo:   true,
            name:    this.name + '_Journal'
          })
        });
      }

      if ( this.timing  ) {
        dao = this.TimingDAO.create({ name: this.name + 'DAO', delegate: dao });
      }

      if ( this.logging ) {
        dao = this.LoggingDAO.create({ delegate: dao });
      }

      this.delegate = dao;

      if ( this.testData ) {
        var self = this;
        this.select(this.COUNT()).then(function(c) {
          // Only load testData if DAO is empty
          if ( c.value ) return;

          foam.json.parse(self.testData, self.of).forEach(
            function(o) { self.put(o); }
          );
        });
      }
    },

    /** Only relevant if cache is true or if daoType
       was set to MDAO, but harmless otherwise. Generates an index
       for a query over all specified properties together.
       @arg var_args specify any number of Properties to be indexed.
    */
    function addIndex(/* foam.core.Property* */ var_args) {
      this.mdao && this.mdao.addIndex.apply(this.mdao, arguments);
      return this;
    },

    /** Only relevant if cache is true or if daoType
      was set to MDAO, but harmless otherwise. Adds an existing index
      to the MDAO.
      @arg index The index to add.
    */
    function addRawIndex(/* foam.dao.index.Index */ index) {
      this.mdao && this.mdao.addRawIndex.apply(this.mdao, arguments);
      return this;
    }
  ]
});
<|endoftext|>


<commit_before>(function() {
  'use strict';

  define(['promizzes', 'chai'], function(promizzes, chai) {
    var expect = chai.expect;
    var promise =  promizzes.promise;
    var fulfill =  promizzes.fulfill;
    var depend =   promizzes.depend;

    describe('a promises system', function() {
      it('builds a simple chain', function(done) {

        var sidePromise = promise();
        var makeArea = function(sideVal) {
          var result = promise();
          fulfill(result, sideVal^2);
          return result;
        }
        var printStuff = function(stuffVal) {
          var result = promise();
          fulfill(result, console.log(stuffVal));
          return result;
        }
        var beDone = function(_) {
          var result = promise();
          fulfill(result, done());
          return result;
        }

        // depend(promise, expression) // famb
        var areaPromise = depend(sidePromise, makeArea);
        var printPromise = depend(areaPromise, printStuff);
        var donePromise = depend(printPromise, beDone);

        // side
        //  .then(makeArea)
        //  .then(printStuff)
        //  .then(beDone);

        fulfill(sidePromise, 5);
      });
    });
  });

})();<commit_msg>minor stuff<commit_after>(function() {
  'use strict';

  define(['promizzes', 'chai'], function(promizzes, chai) {
    var expect = chai.expect;
    var promise =  promizzes.promise;
    var fulfill =  promizzes.fulfill;
    var depend =   promizzes.depend;

    describe('a promises system', function() {
      it('builds a simple chain', function(done) {

        var sidePromise = promise();
        var makeArea = function(sideVal) {
          var result = promise();
          fulfill(result, sideVal * sideVal);
          return result;
        }
        var printStuff = function(stuffVal) {
          var result = promise();
          fulfill(result, console.log(stuffVal));
          return result;
        }
        var beDone = function(_) {
          var result = promise();
          fulfill(result, done());
          return result;
        }

        // depend(promise, expression) // famb
        var areaPromise = depend(sidePromise, makeArea);
        var printPromise = depend(areaPromise, printStuff);
        var donePromise = depend(printPromise, beDone);

        // side
        //  .then(makeArea)
        //  .then(printStuff)
        //  .then(beDone);

        fulfill(sidePromise, 5);
      });
    });
  });

})();<|endoftext|>


<commit_before>import { get } from 'ember-metal/property_get';
import ControllerMixin from 'ember-runtime/mixins/controller';

/**
@module ember
@submodule ember-routing
*/

ControllerMixin.reopen({
  concatenatedProperties: ['queryParams'],

  /**
    Defines which query parameters the controller accepts.
    If you give the names `['category','page']` it will bind
    the values of these query parameters to the variables
    `this.category` and `this.page`

    @property queryParams
    @public
  */
  queryParams: null,

  /**
    @property _qpDelegate
    @private
  */
  _qpDelegate: null, // set by route

  /**
    @method _qpChanged
    @private
  */
  _qpChanged(controller, _prop) {
    var prop = _prop.substr(0, _prop.length - 3);

    var delegate = controller._qpDelegate;
    var value = get(controller, prop);
    delegate(prop, value);
  },

  /**
    Transition the application into another route. The route may
    be either a single route or route path:

    ```javascript
    aController.transitionToRoute('blogPosts');
    aController.transitionToRoute('blogPosts.recentEntries');
    ```

    Optionally supply a model for the route in question. The model
    will be serialized into the URL using the `serialize` hook of
    the route:

    ```javascript
    aController.transitionToRoute('blogPost', aPost);
    ```

    If a literal is passed (such as a number or a string), it will
    be treated as an identifier instead. In this case, the `model`
    hook of the route will be triggered:

    ```javascript
    aController.transitionToRoute('blogPost', 1);
    ```

    Multiple models will be applied last to first recursively up the
    route tree.

    ```javascript
    App.Router.map(function() {
      this.route('blogPost', { path: ':blogPostId' }, function() {
        this.route('blogComment', { path: ':blogCommentId', resetNamespace: true });
      });
    });

    aController.transitionToRoute('blogComment', aPost, aComment);
    aController.transitionToRoute('blogComment', 1, 13);
    ```

    It is also possible to pass a URL (a string that starts with a
    `/`). This is intended for testing and debugging purposes and
    should rarely be used in production code.

    ```javascript
    aController.transitionToRoute('/');
    aController.transitionToRoute('/blog/post/1/comment/13');
    aController.transitionToRoute('/blog/posts?sort=title');
    ```

    An options hash with a `queryParams` property may be provided as
    the final argument to add query parameters to the destination URL.

    ```javascript
    aController.transitionToRoute('blogPost', 1, {
      queryParams: {showComments: 'true'}
    });

    // if you just want to transition the query parameters without changing the route
    aController.transitionToRoute({queryParams: {sort: 'date'}});
    ```

    See also [replaceRoute](/api/classes/Ember.ControllerMixin.html#method_replaceRoute).

    @param {String} name the name of the route or a URL
    @param {...Object} models the model(s) or identifier(s) to be used
      while transitioning to the route.
    @param {Object} [options] optional hash with a queryParams property
      containing a mapping of query parameters
    @for Ember.ControllerMixin
    @method transitionToRoute
    @public
  */
  transitionToRoute() {
    // target may be either another controller or a router
    var target = get(this, 'target');
    var method = target.transitionToRoute || target.transitionTo;
    return method.apply(target, arguments);
  },

  /**
    Transition into another route while replacing the current URL, if possible.
    This will replace the current history entry instead of adding a new one.
    Beside that, it is identical to `transitionToRoute` in all other respects.

    ```javascript
    aController.replaceRoute('blogPosts');
    aController.replaceRoute('blogPosts.recentEntries');
    ```

    Optionally supply a model for the route in question. The model
    will be serialized into the URL using the `serialize` hook of
    the route:

    ```javascript
    aController.replaceRoute('blogPost', aPost);
    ```

    If a literal is passed (such as a number or a string), it will
    be treated as an identifier instead. In this case, the `model`
    hook of the route will be triggered:

    ```javascript
    aController.replaceRoute('blogPost', 1);
    ```

    Multiple models will be applied last to first recursively up the
    route tree.

    ```javascript
    App.Router.map(function() {
      this.route('blogPost', { path: ':blogPostId' }, function() {
        this.route('blogComment', { path: ':blogCommentId', resetNamespace: true });
      });
    });

    aController.replaceRoute('blogComment', aPost, aComment);
    aController.replaceRoute('blogComment', 1, 13);
    ```

    It is also possible to pass a URL (a string that starts with a
    `/`). This is intended for testing and debugging purposes and
    should rarely be used in production code.

    ```javascript
    aController.replaceRoute('/');
    aController.replaceRoute('/blog/post/1/comment/13');
    ```

    @param {String} name the name of the route or a URL
    @param {...Object} models the model(s) or identifier(s) to be used
    while transitioning to the route.
    @for Ember.ControllerMixin
    @method replaceRoute
    @private
  */
  replaceRoute() {
    // target may be either another controller or a router
    var target = get(this, 'target');
    var method = target.replaceRoute || target.replaceWith;
    return method.apply(target, arguments);
  }
});

export default ControllerMixin;
<commit_msg>Controller#replaceRoute should be public<commit_after>import { get } from 'ember-metal/property_get';
import ControllerMixin from 'ember-runtime/mixins/controller';

/**
@module ember
@submodule ember-routing
*/

ControllerMixin.reopen({
  concatenatedProperties: ['queryParams'],

  /**
    Defines which query parameters the controller accepts.
    If you give the names `['category','page']` it will bind
    the values of these query parameters to the variables
    `this.category` and `this.page`

    @property queryParams
    @public
  */
  queryParams: null,

  /**
    @property _qpDelegate
    @private
  */
  _qpDelegate: null, // set by route

  /**
    @method _qpChanged
    @private
  */
  _qpChanged(controller, _prop) {
    var prop = _prop.substr(0, _prop.length - 3);

    var delegate = controller._qpDelegate;
    var value = get(controller, prop);
    delegate(prop, value);
  },

  /**
    Transition the application into another route. The route may
    be either a single route or route path:

    ```javascript
    aController.transitionToRoute('blogPosts');
    aController.transitionToRoute('blogPosts.recentEntries');
    ```

    Optionally supply a model for the route in question. The model
    will be serialized into the URL using the `serialize` hook of
    the route:

    ```javascript
    aController.transitionToRoute('blogPost', aPost);
    ```

    If a literal is passed (such as a number or a string), it will
    be treated as an identifier instead. In this case, the `model`
    hook of the route will be triggered:

    ```javascript
    aController.transitionToRoute('blogPost', 1);
    ```

    Multiple models will be applied last to first recursively up the
    route tree.

    ```javascript
    App.Router.map(function() {
      this.route('blogPost', { path: ':blogPostId' }, function() {
        this.route('blogComment', { path: ':blogCommentId', resetNamespace: true });
      });
    });

    aController.transitionToRoute('blogComment', aPost, aComment);
    aController.transitionToRoute('blogComment', 1, 13);
    ```

    It is also possible to pass a URL (a string that starts with a
    `/`). This is intended for testing and debugging purposes and
    should rarely be used in production code.

    ```javascript
    aController.transitionToRoute('/');
    aController.transitionToRoute('/blog/post/1/comment/13');
    aController.transitionToRoute('/blog/posts?sort=title');
    ```

    An options hash with a `queryParams` property may be provided as
    the final argument to add query parameters to the destination URL.

    ```javascript
    aController.transitionToRoute('blogPost', 1, {
      queryParams: {showComments: 'true'}
    });

    // if you just want to transition the query parameters without changing the route
    aController.transitionToRoute({queryParams: {sort: 'date'}});
    ```

    See also [replaceRoute](/api/classes/Ember.ControllerMixin.html#method_replaceRoute).

    @param {String} name the name of the route or a URL
    @param {...Object} models the model(s) or identifier(s) to be used
      while transitioning to the route.
    @param {Object} [options] optional hash with a queryParams property
      containing a mapping of query parameters
    @for Ember.ControllerMixin
    @method transitionToRoute
    @public
  */
  transitionToRoute() {
    // target may be either another controller or a router
    var target = get(this, 'target');
    var method = target.transitionToRoute || target.transitionTo;
    return method.apply(target, arguments);
  },

  /**
    Transition into another route while replacing the current URL, if possible.
    This will replace the current history entry instead of adding a new one.
    Beside that, it is identical to `transitionToRoute` in all other respects.

    ```javascript
    aController.replaceRoute('blogPosts');
    aController.replaceRoute('blogPosts.recentEntries');
    ```

    Optionally supply a model for the route in question. The model
    will be serialized into the URL using the `serialize` hook of
    the route:

    ```javascript
    aController.replaceRoute('blogPost', aPost);
    ```

    If a literal is passed (such as a number or a string), it will
    be treated as an identifier instead. In this case, the `model`
    hook of the route will be triggered:

    ```javascript
    aController.replaceRoute('blogPost', 1);
    ```

    Multiple models will be applied last to first recursively up the
    route tree.

    ```javascript
    App.Router.map(function() {
      this.route('blogPost', { path: ':blogPostId' }, function() {
        this.route('blogComment', { path: ':blogCommentId', resetNamespace: true });
      });
    });

    aController.replaceRoute('blogComment', aPost, aComment);
    aController.replaceRoute('blogComment', 1, 13);
    ```

    It is also possible to pass a URL (a string that starts with a
    `/`). This is intended for testing and debugging purposes and
    should rarely be used in production code.

    ```javascript
    aController.replaceRoute('/');
    aController.replaceRoute('/blog/post/1/comment/13');
    ```

    @param {String} name the name of the route or a URL
    @param {...Object} models the model(s) or identifier(s) to be used
    while transitioning to the route.
    @for Ember.ControllerMixin
    @method replaceRoute
    @public
  */
  replaceRoute() {
    // target may be either another controller or a router
    var target = get(this, 'target');
    var method = target.replaceRoute || target.replaceWith;
    return method.apply(target, arguments);
  }
});

export default ControllerMixin;
<|endoftext|>


<commit_before>var _ = require('underscore');
var cdb = require('cartodb.js');
var Router = require('../../common/router');
var RouterModel = require('./router_model');
var GroupHeaderView = require('./group_header_view');
var GroupsIndexView = require('./groups_index_view');
var CreateGroupView = require('./create_group_view');
var GroupUsersView = require('./group_users_view');
var EditGroupView = require('./edit_group_view');
var ViewFactory = require('../../common/view_factory');
var PaginationModel = require('../../common/views/pagination/model');

/**
 *  Backbone router for organization groups urls.
 */
module.exports = Router.extend({

  routes: Router.supportTrailingSlashes({
    '': 'routeToGroupsIndex',
    'new': 'routeToCreateGroup',
    ':id': 'renderGroupUsers',
    ':id/edit': 'routeToEditGroup',

    // If URL is lacking the trailing slash (e.g. 'http://username.cartodb.com/organization/groups'), treat it like index
    '*prefix/groups': 'routeToGroupsIndex'
  }),

  initialize: function(opts) {
    _.each(['rootUrl', 'groups', 'user'], function(name) {
      if (!opts[name]) throw new Error(name + ' is required');
    }, this);

    this.model = new RouterModel();
    this.user = opts.user;
    this.groups = opts.groups;
    this.rootUrl = opts.rootUrl;
    this.rootPath = this.rootUrl.pathname.bind(this.rootUrl);
    this.model.createLoadingView('Loading view'); // Until router's history is started
  },

  normalizeFragmentOrUrl: function(fragmentOrUrl) {
    return fragmentOrUrl ? fragmentOrUrl.toString().replace(this.rootUrl.toString(), '') : '';
  },

  isWithinCurrentRoutes: function(url) {
    return url.indexOf(this.rootUrl.pathname()) !== -1;
  },

  routeToGroupsIndex: function() {
    this.model.set('view',
      new GroupsIndexView({
        newGroupUrl: this._groupUrl.bind(this),
        groups: this.groups,
        router: this
      })
    );
  },

  routeToCreateGroup: function() {
    var group = this.groups.newGroupById();
    var self = this;
    this.model.set('view',
      ViewFactory.createByList([
        self._createGroupHeader(group),
        new CreateGroupView({
          group: group,
          onCreated: self._navigateToGroup.bind(self, group)
        })
      ])
    );
  },

  renderGroupUsers: function(id) {
    var self = this;
    this.model.createGroupView(this.groups, id, function(group) {
      return ViewFactory.createByList([
        self._createGroupHeader(group, 'group_users'),
        new GroupUsersView({
          group: group,
          orgUsers: self.user.organization.users
        })
      ]);
    });
  },

  routeToEditGroup: function(id) {
    var self = this;
    this.model.createGroupView(this.groups, id, function(group) {
      return ViewFactory.createByList([
        self._createGroupHeader(group, 'edit_group'),
        new EditGroupView({
          group: group,
          onSaved: self._navigateToGroup.bind(self, group),
          onDeleted: self.navigate.bind(self, self.rootUrl, { trigger: true })
        })
      ])
    });
  },

  _navigateToGroup: function(group) {
    this.navigate(this.rootUrl.urlToPath(group.id), { trigger: true });
  },

  _groupUrl: function(group, subpath) {
    var path = group.id;
    if (subpath) {
      path += '/' + subpath
    }
    return this.rootUrl.urlToPath(path)
  },

  _createGroupHeader: function(group, current) {
    var urls = {
      root: this.rootUrl,
      users: this._groupUrl(group),
      edit: this._groupUrl(group, 'edit'),
    };
    urls.users.isCurrent = current === 'group_users';
    urls.edit.isCurrent = current === 'edit_group';

    return new GroupHeaderView({
      group: group,
      urls: urls
    });
  }

});
<commit_msg>Rename methods to clarify intents<commit_after>var _ = require('underscore');
var cdb = require('cartodb.js');
var Router = require('../../common/router');
var RouterModel = require('./router_model');
var GroupHeaderView = require('./group_header_view');
var GroupsIndexView = require('./groups_index_view');
var CreateGroupView = require('./create_group_view');
var GroupUsersView = require('./group_users_view');
var EditGroupView = require('./edit_group_view');
var ViewFactory = require('../../common/view_factory');
var PaginationModel = require('../../common/views/pagination/model');

/**
 *  Backbone router for organization groups urls.
 */
module.exports = Router.extend({

  routes: Router.supportTrailingSlashes({
    '': 'renderGroupsIndex',
    'new': 'renderCreateGroup',
    ':id': 'renderGroupUsers',
    ':id/edit': 'renderEditGroup',

    // If URL is lacking the trailing slash (e.g. 'http://username.cartodb.com/organization/groups'), treat it like index
    '*prefix/groups': 'renderGroupsIndex'
  }),

  initialize: function(opts) {
    _.each(['rootUrl', 'groups', 'user'], function(name) {
      if (!opts[name]) throw new Error(name + ' is required');
    }, this);

    this.model = new RouterModel();
    this.user = opts.user;
    this.groups = opts.groups;
    this.rootUrl = opts.rootUrl;
    this.rootPath = this.rootUrl.pathname.bind(this.rootUrl);
    this.model.createLoadingView('Loading view'); // Until router's history is started
  },

  normalizeFragmentOrUrl: function(fragmentOrUrl) {
    return fragmentOrUrl ? fragmentOrUrl.toString().replace(this.rootUrl.toString(), '') : '';
  },

  isWithinCurrentRoutes: function(url) {
    return url.indexOf(this.rootUrl.pathname()) !== -1;
  },

  renderGroupsIndex: function() {
    this.model.set('view',
      new GroupsIndexView({
        newGroupUrl: this._groupUrl.bind(this),
        groups: this.groups,
        router: this
      })
    );
  },

  renderCreateGroup: function() {
    var group = this.groups.newGroupById();
    var self = this;
    this.model.set('view',
      ViewFactory.createByList([
        self._createGroupHeader(group),
        new CreateGroupView({
          group: group,
          onCreated: self._navigateToGroup.bind(self, group)
        })
      ])
    );
  },

  renderGroupUsers: function(id) {
    var self = this;
    this.model.createGroupView(this.groups, id, function(group) {
      return ViewFactory.createByList([
        self._createGroupHeader(group, 'group_users'),
        new GroupUsersView({
          group: group,
          orgUsers: self.user.organization.users
        })
      ]);
    });
  },

  renderEditGroup: function(id) {
    var self = this;
    this.model.createGroupView(this.groups, id, function(group) {
      return ViewFactory.createByList([
        self._createGroupHeader(group, 'edit_group'),
        new EditGroupView({
          group: group,
          onSaved: self._navigateToGroup.bind(self, group),
          onDeleted: self.navigate.bind(self, self.rootUrl, { trigger: true })
        })
      ])
    });
  },

  _navigateToGroup: function(group) {
    this.navigate(this.rootUrl.urlToPath(group.id), { trigger: true });
  },

  _groupUrl: function(group, subpath) {
    var path = group.id;
    if (subpath) {
      path += '/' + subpath
    }
    return this.rootUrl.urlToPath(path)
  },

  _createGroupHeader: function(group, current) {
    var urls = {
      root: this.rootUrl,
      users: this._groupUrl(group),
      edit: this._groupUrl(group, 'edit'),
    };
    urls.users.isCurrent = current === 'group_users';
    urls.edit.isCurrent = current === 'edit_group';

    return new GroupHeaderView({
      group: group,
      urls: urls
    });
  }

});
<|endoftext|>


<commit_before>$.fn.hoverClass = function(c) {
	return this.each(function(){
		$(this).hover(
			function() { $(this).addClass(c);  },
			function() { $(this).removeClass(c); }
		);
	});
};

String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }

$(document).ready(function(){
	// Alternate the rows' styling.
    $("table").each(function(){
	  $("tr:odd", this).not(".even").addClass("odd");
	  $("tr:even", this).not(".odd").addClass("even");
	});

	$("#menu .menu-item").hover(
		function(){ $("ul", this).fadeIn("fast"); },
		function() { }
	);

	if (document.all) {
		$("#menu .menu-item").hoverClass("sfHover");
	}

	// Prevent all checkboxes to be unchecked.
	$(".search_field").click(function(){
		if($(".search_field:checked").size() == 0 && !$(this).attr('checked')) {
			return false;
		}
	});

	// Toggle default text of inputs on the publish page.
	$('label.incontent').each(function(){
		var ctl = '#' + $(this).attr('for');
		if($(ctl).val() == '') {
			$(ctl).val($(this).html().trim())
				.addClass('islabeled');
		}
		$(this).hide();
	});

	$('.islabeled').focus(function(){
		$(this).filter('.islabeled')
			.val('')
			.removeClass('islabeled');
	}).blur(function(){
		if ($(this).val() == '') {
			$(this)
				.addClass('islabeled')
				.val($('label.incontent[for=' + $(this).attr('id') + ']').html().trim())
		}
	});

	$('.islabeled').parents('form').submit(function(){
		$('.islabeled').val('');
	});

	// Convert these links into buttons
	$('a.link_as_button').each(function(){
		$(this).after('<button onclick="location.href=\'' + $(this).attr('href') + '\';return false;">' + $(this).html() + '</button>').hide();
	});

	/* Resizable Textareas */
	$('textarea.resizable').each(function() {
		var textarea = $(this);
		var offset = null;
		var grip = $('

').mousedown(function(ev){
			offset = textarea.height() - (ev.clientY + document.documentElement.scrollTop)
			$(document).mousemove(doDrag).mouseup(endDrag);
		}).mouseup(endDrag);
		var resizer = $('

').css('margin-bottom',$(this).css('margin-bottom'));
		$(this).css('margin-bottom', '0px').wrap(resizer).parent().append(grip);

		function doDrag(ev){
			textarea.height(Math.max(offset + ev.clientY + document.documentElement.scrollTop, 60) + 'px');
			return false;
		}

		function endDrag(ev){
			$(document).unbind('mousemove', doDrag).unbind('mouseup', endDrag);
			textarea.css('opacity', 1.0);
		}

	});

	/* Tabs, using jQuery UI Tabs */
	$('.tabs').tabs({ fxShow: { height: 'show', opacity: 'show' }, fxHide: { height: 'hide', opacity: 'hide' }, unselected: true })

// ah, the magic of jquery...
$('#tag-list li').click(function() {
	// here we set the current text of #tags to current for later
	//examination
	var current = $('#tags').val();
	// check to see if the tag item we clicked has been clicked before...
	if( $( this ).attr( 'class' )== 'clicked' ) {
		// do nothing.
	} else {
		// if it hasn't been clicked, go ahead and add the clicked class
		$(this).addClass( 'clicked' );
		// check to see if current is the default text
		if( current == 'Tags, separated by, commas' ) {
			// and if it is, replace it with whatever we clicked
			$( '#tags' ).val( $( this ).text() );
		} else {
			// else if we already have tag content, just append the new tag
			if( $('#tags' ).val() != '' ) {
				$( '#tags' ).val( current + "," + $( this ).text() );
			} else {
				$( '#tags' ).val( $( this ).text() );
			}
		}
	}
});

// sometimes, you just want to start over
$( '#clear' ).click( function() {
	// so we nuke all the tags in the tag text field
	$( '#tags' ).val( '' );
	// and remove the clicked class from the tags in the manager
	$( '#tag-list li' ).removeClass( 'clicked' );
});
});<commit_msg>Some tweaks to the tag js to make it properly keep the tags that you click on when you focus the tag text input.  Also, it now highlights the tags in the tag area as you type tags, and prevents the addition of tags you have already added manually, and some other nice common-sense stuff.<commit_after>$.fn.hoverClass = function(c) {
	return this.each(function(){
		$(this).hover(
			function() { $(this).addClass(c);  },
			function() { $(this).removeClass(c); }
		);
	});
};

String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }

var tagskeyup;

$(document).ready(function(){
	// Alternate the rows' styling.
    $("table").each(function(){
	  $("tr:odd", this).not(".even").addClass("odd");
	  $("tr:even", this).not(".odd").addClass("even");
	});

	$("#menu .menu-item").hover(
		function(){ $("ul", this).fadeIn("fast"); },
		function() { }
	);

	if (document.all) {
		$("#menu .menu-item").hoverClass("sfHover");
	}

	// Prevent all checkboxes to be unchecked.
	$(".search_field").click(function(){
		if($(".search_field:checked").size() == 0 && !$(this).attr('checked')) {
			return false;
		}
	});

	// Toggle default text of inputs on the publish page.
	$('label.incontent').each(function(){
		var ctl = '#' + $(this).attr('for');
		if($(ctl).val() == '') {
			$(ctl).val($(this).html().trim())
				.addClass('islabeled');
		}
		$(this).hide();
	});

	$('.islabeled').focus(function(){
		$(this).filter('.islabeled')
			.val('')
			.removeClass('islabeled');
	}).blur(function(){
		if ($(this).val() == '') {
			$(this)
				.addClass('islabeled')
				.val($('label.incontent[for=' + $(this).attr('id') + ']').html().trim())
		}
	});

	$('.islabeled').parents('form').submit(function(){
		$('.islabeled').val('');
	});

	// Convert these links into buttons
	$('a.link_as_button').each(function(){
		$(this).after('<button onclick="location.href=\'' + $(this).attr('href') + '\';return false;">' + $(this).html() + '</button>').hide();
	});

	/* Resizable Textareas */
	$('textarea.resizable').each(function() {
		var textarea = $(this);
		var offset = null;
		var grip = $('

').mousedown(function(ev){
			offset = textarea.height() - (ev.clientY + document.documentElement.scrollTop)
			$(document).mousemove(doDrag).mouseup(endDrag);
		}).mouseup(endDrag);
		var resizer = $('

').css('margin-bottom',$(this).css('margin-bottom'));
		$(this).css('margin-bottom', '0px').wrap(resizer).parent().append(grip);

		function doDrag(ev){
			textarea.height(Math.max(offset + ev.clientY + document.documentElement.scrollTop, 60) + 'px');
			return false;
		}

		function endDrag(ev){
			$(document).unbind('mousemove', doDrag).unbind('mouseup', endDrag);
			textarea.css('opacity', 1.0);
		}

	});

	/* Tabs, using jQuery UI Tabs */
	$('.tabs').tabs({ fxShow: { height: 'show', opacity: 'show' }, fxHide: { height: 'hide', opacity: 'hide' }, unselected: true })

	// Tag Drawer: Add tag via click
	$('#tag-list li').click(function() {
		// here we set the current text of #tags to current for later examination
		var current = $('#tags').val();

		// create a regex that finds the clicked tag in the input field
		var replstr = new RegExp('\\s*"?' + $( this ).text() + '"?\\s*', "gi");

		// check to see if the tag item we clicked has been clicked before...
		if( $( this ).attr( 'class' )== 'clicked' ) {
			// remove that tag from the input field
			$( '#tags' ).val( current.replace(replstr, '') );
			// unhighlight that tag
			$(this).removeClass( 'clicked' );
		}
		else {
			// if it hasn't been clicked, go ahead and add the clicked class
			$(this).addClass( 'clicked' );
			// be sure that the option wasn't already in the input field
			if(!current.match(replstr) || $( '#tags.islabeled' ).size() > 0) {
				// check to see if current is the default text
				if( $( '#tags.islabeled' ).size() > 0 ) {
					// and if it is, replace it with whatever we clicked
					$( '#tags' ).removeClass('islabeled').val( $( this ).text() );
				} else {
					// else if we already have tag content, just append the new tag
					if( $('#tags' ).val() != '' ) {
						$( '#tags' ).val( current + "," + $( this ).text() );
					} else {
						$( '#tags' ).val( $( this ).text() );
					}
				}
			}
		}

		// replace unneccessary commas
		$( '#tags' ).val( $( '#tags' ).val().replace(new RegExp('^\\s*,\\s*|\\s*,\\s*$', "gi"), ''));
		$( '#tags' ).val( $( '#tags' ).val().replace(new RegExp('\\s*,(\\s*,)+\\s*', "gi"), ','));

	});

	$( '#tags' ).keyup(function(){
		clearTimeout(tagskeyup);
		tagskeyup = setTimeout(resetTags, 500);
	});

	// Tag Drawer: Remove all tags.
	$( '#clear' ).click( function() {
		// so we nuke all the tags in the tag text field
		$( '#tags' ).val( 'Tags, separated by, commas' ).addClass('islabeled');
		// and remove the clicked class from the tags in the manager
		$( '#tag-list li' ).removeClass( 'clicked' );
	});
});

function resetTags() {
	var current = $('#tags').val();

	$('#tag-list li').each(function(){
		replstr = new RegExp('\\s*"?' + $( this ).text() + '"?\\s*', "gi");
		if(current.match(replstr)) {
			$(this).addClass('clicked');
		}
		else {
			$(this).removeClass('clicked');
		}
	});

}<|endoftext|>


<commit_before>var
    jQuery = jQuery,
    $ = jQuery,
    DO = DO;

(function ($namespace) {

    var
        showContentOnload = function (jqObj, selectedClassName) {
            var
                parent = jqObj.closest(".do-ui-tabs-wrapper"),
                list = parent.find("nav ul"),
                items = list.find("li"),
                container = parent.find("ul.do-ui-tabs-container"),
                URLHash = location.hash,
                handle = null,
                id = null;

            container.find("li").hide();

            if (URLHash.isSet()) {
                handle = items.findAnchorByHref(URLHash);
            } else {
                handle = items.find("a." + selectedClassName);
            }
            id = handle.attr("href");

            container.find("li" + id).show();

            // selected = container.find("li.#{tabAnchor.attr("class")}")
            // selected = container.find("li#{tabAnchor.attr("href")}")
            //  if selected.length == 0
            //    url = $(this).data("url")
            //    if url
            //      content = $("

").addClass tabAnchor.attr("class")
            //      container.append content

            //      format = $(this).data("format")
            //      if !format
            //        format = "js"

            //      $.get url, {format: format},
            //        (data, status, xhr)->
            //          if format == "html"
            //            content.html data
        },

        selectTabOnload = function (jqObj, selectedClassName) {
            var
                // parent = jqObj.closest(".tabs-wrapper"),
                parent = jqObj.closest(".do-ui-tabs-wrapper"),
                list = parent.find("nav ul"),
                items = list.find("li"),
                selectedDefault = items.find("a." + selectedClassName),
                URLHash = location.hash,
                URLPath,
                anchorSearch;

            if (URLHash.isSet()) {
                selectTab(list.findAnchorByHref(URLHash), selectedClassName);
            } else {
                URLPath = location.pathname.match(/[a-z\/_-]+[a-z]+/);
                anchorSearch = items.findAnchorByHref(URLPath);

                if (anchorSearch.length !== 0) {
                    selectTab(anchorSearch, selectedClassName);
                } else {
                    selectedDefault.removeAttr("href");
                }

                anchorSearch.removeAttr("href");
            }
        },

        showContent = function (handle) {
            var
                parentContainer = $(handle.target)
                    .closest(".do-ui-tabs-wrapper")
                        .find("ul.do-ui-tabs-container"),
                id = $(handle.target).attr("href");

            parentContainer.find("li").hide();
            parentContainer.find("li" + id).show();
        },

        selectTab = function (handle, selectedClassName) {
            if (handle instanceof jQuery.Event) {
                handle = $(handle.target);
            } else {
                if (handle instanceof jQuery) {
                    handle = $(handle);
                }
            }

            var parent  = handle.closest("nav ul");
            parent.find("a").removeClass(selectedClassName);

            handle.addClass(selectedClassName);
        };

    $namespace.tabsNav = function (jqObj) {

        this.initWithClass = function (selectedClassName) {
            selectTabOnload(jqObj, selectedClassName);
            showContentOnload(jqObj, selectedClassName);

            jqObj.click(function (e) {
                selectTab(e, selectedClassName);

                //aqui deverá vir a lógica de como carregar o conteúdo,
                // se via ajax, por exemplo

                showContent(e);
            });
        };

        return this;

    };

}(DO.ui));
<commit_msg>remove comentário<commit_after>var
    jQuery = jQuery,
    $ = jQuery,
    DO = DO;

(function ($namespace) {

    var
        showContentOnload = function (jqObj, selectedClassName) {
            var
                parent = jqObj.closest(".do-ui-tabs-wrapper"),
                list = parent.find("nav ul"),
                items = list.find("li"),
                container = parent.find("ul.do-ui-tabs-container"),
                URLHash = location.hash,
                handle = null,
                id = null;

            container.find("li").hide();

            if (URLHash.isSet()) {
                handle = items.findAnchorByHref(URLHash);
            } else {
                handle = items.find("a." + selectedClassName);
            }
            id = handle.attr("href");

            container.find("li" + id).show();

            // selected = container.find("li.#{tabAnchor.attr("class")}")
            // selected = container.find("li#{tabAnchor.attr("href")}")
            //  if selected.length == 0
            //    url = $(this).data("url")
            //    if url
            //      content = $("

").addClass tabAnchor.attr("class")
            //      container.append content

            //      format = $(this).data("format")
            //      if !format
            //        format = "js"

            //      $.get url, {format: format},
            //        (data, status, xhr)->
            //          if format == "html"
            //            content.html data
        },

        selectTabOnload = function (jqObj, selectedClassName) {
            var
                parent = jqObj.closest(".do-ui-tabs-wrapper"),
                list = parent.find("nav ul"),
                items = list.find("li"),
                selectedDefault = items.find("a." + selectedClassName),
                URLHash = location.hash,
                URLPath,
                anchorSearch;

            if (URLHash.isSet()) {
                selectTab(list.findAnchorByHref(URLHash), selectedClassName);
            } else {
                URLPath = location.pathname.match(/[a-z\/_-]+[a-z]+/);
                anchorSearch = items.findAnchorByHref(URLPath);

                if (anchorSearch.length !== 0) {
                    selectTab(anchorSearch, selectedClassName);
                } else {
                    selectedDefault.removeAttr("href");
                }

                anchorSearch.removeAttr("href");
            }
        },

        showContent = function (handle) {
            var
                parentContainer = $(handle.target)
                    .closest(".do-ui-tabs-wrapper")
                        .find("ul.do-ui-tabs-container"),
                id = $(handle.target).attr("href");

            parentContainer.find("li").hide();
            parentContainer.find("li" + id).show();
        },

        selectTab = function (handle, selectedClassName) {
            if (handle instanceof jQuery.Event) {
                handle = $(handle.target);
            } else {
                if (handle instanceof jQuery) {
                    handle = $(handle);
                }
            }

            var parent  = handle.closest("nav ul");
            parent.find("a").removeClass(selectedClassName);

            handle.addClass(selectedClassName);
        };

    $namespace.tabsNav = function (jqObj) {

        this.initWithClass = function (selectedClassName) {
            selectTabOnload(jqObj, selectedClassName);
            showContentOnload(jqObj, selectedClassName);

            jqObj.click(function (e) {
                selectTab(e, selectedClassName);

                //aqui deverá vir a lógica de como carregar o conteúdo,
                // se via ajax, por exemplo

                showContent(e);
            });
        };

        return this;

    };

}(DO.ui));
<|endoftext|>


<commit_before>(function() {

  var REFERENCE_RECT = {
    'type':               'rect',
    'originX':            'left',
    'originY':            'top',
    'left':               0,
    'top':                0,
    'width':              0,
    'height':             0,
    'fill':               'rgb(0,0,0)',
    'stroke':             null,
    'strokeWidth':        1,
    'strokeDashArray':    null,
    'strokeLineCap':      'butt',
    'strokeLineJoin':     'miter',
    'strokeMiterLimit':   10,
    'scaleX':             1,
    'scaleY':             1,
    'angle':              0,
    'flipX':              false,
    'flipY':              false,
    'opacity':            1,
    'shadow':             null,
    'visible':            true,
    'backgroundColor':    '',
    'clipTo':             null,
    'rx':                 0,
    'ry':                 0,
    'x':                  0,
    'y':                  0
  };

  QUnit.module('fabric.Rect');

  test('constructor', function(){
    ok(fabric.Rect);

    var rect = new fabric.Rect();

    ok(rect instanceof fabric.Rect);
    ok(rect instanceof fabric.Object);

    deepEqual(rect.get('type'), 'rect');
  });

  test('complexity', function() {
    var rect = new fabric.Rect();

    ok(typeof rect.complexity == 'function');
  });

  test('toObject', function() {
    var rect = new fabric.Rect();
    ok(typeof rect.toObject == 'function');

    var object = rect.toObject();
    deepEqual(object, REFERENCE_RECT);
  });

  test('fabric.Rect.fromObject', function() {
    ok(typeof fabric.Rect.fromObject == 'function');

    var rect = fabric.Rect.fromObject(REFERENCE_RECT);
    ok(rect instanceof fabric.Rect);
    deepEqual(rect.toObject(), REFERENCE_RECT);
  });

  test('fabric.Rect.fromElement', function() {
    ok(typeof fabric.Rect.fromElement == 'function');

    var elRect = fabric.document.createElement('rect');
    var rect = fabric.Rect.fromElement(elRect);

    ok(rect instanceof fabric.Rect);
    deepEqual(rect.toObject(), REFERENCE_RECT);
  });

  test('fabric.Rect.fromElement with custom attributes', function() {
    var elRectWithAttrs = fabric.document.createElement('rect');

    elRectWithAttrs.setAttribute('x', 10);
    elRectWithAttrs.setAttribute('y', 20);
    elRectWithAttrs.setAttribute('width', 222);
    elRectWithAttrs.setAttribute('height', 333);
    elRectWithAttrs.setAttribute('rx', 11);
    elRectWithAttrs.setAttribute('ry', 12);
    elRectWithAttrs.setAttribute('fill', 'rgb(255,255,255)');
    elRectWithAttrs.setAttribute('opacity', 0.45);
    elRectWithAttrs.setAttribute('stroke', 'blue');
    elRectWithAttrs.setAttribute('stroke-width', 3);
    elRectWithAttrs.setAttribute('stroke-dasharray', '5, 2');
    elRectWithAttrs.setAttribute('stroke-linecap', 'round');
    elRectWithAttrs.setAttribute('stroke-linejoin', 'bevil');
    elRectWithAttrs.setAttribute('stroke-miterlimit', 5);
    //elRectWithAttrs.setAttribute('transform', 'translate(-10,-20) scale(2) rotate(45) translate(5,10)');

    var rectWithAttrs = fabric.Rect.fromElement(elRectWithAttrs);
    ok(rectWithAttrs instanceof fabric.Rect);

    var expectedObject = fabric.util.object.extend(REFERENCE_RECT, {
      left:             121,
      top:              186.5,
      width:            222,
      height:           333,
      fill:             'rgb(255,255,255)',
      opacity:          0.45,
      stroke:           'blue',
      strokeWidth:      3,
      strokeDashArray:  [5, 2],
      strokeLineCap:    'round',
      strokeLineJoin:   'bevil',
      strokeMiterLimit: 5,
      rx:               11,
      ry:               12,
      x:                10,
      y:                20
    });
    deepEqual(rectWithAttrs.toObject(), expectedObject);
  });

  test('empty fromElement', function() {
    ok(fabric.Rect.fromElement() === null);
  });

  test('clone with rounded corners', function() {
    var rect = new fabric.Rect({ width: 100, height: 100, rx: 20, ry: 30 });
    var clone = rect.clone();

    equal(clone.get('rx'), rect.get('rx'));
    equal(clone.get('ry'), rect.get('ry'));
  });

  test('toSVG with rounded corners', function() {
    var rect = new fabric.Rect({ width: 100, height: 100, rx: 20, ry: 30 });
    var svg = rect.toSVG();

    equal(svg, '<rect x="-50" y="-50" rx="20" ry="30" width="100" height="100" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); opacity: 1;" transform="translate(50 50)"/>');
  });

  test('toObject without default values', function() {
    var options = { type: 'rect', width: 69, height: 50, left: 10, top: 20 };
    var rect = new fabric.Rect(options);
    rect.includeDefaultValues = false;
    deepEqual(rect.toObject(), options);
  });
})();
<commit_msg>Fixes tests for rects<commit_after>(function() {

  var REFERENCE_RECT = {
    'type':               'rect',
    'originX':            'left',
    'originY':            'top',
    'left':               0,
    'top':                0,
    'width':              0,
    'height':             0,
    'fill':               'rgb(0,0,0)',
    'stroke':             null,
    'strokeWidth':        1,
    'strokeDashArray':    null,
    'strokeLineCap':      'butt',
    'strokeLineJoin':     'miter',
    'strokeMiterLimit':   10,
    'scaleX':             1,
    'scaleY':             1,
    'angle':              0,
    'flipX':              false,
    'flipY':              false,
    'opacity':            1,
    'shadow':             null,
    'visible':            true,
    'backgroundColor':    '',
    'clipTo':             null,
    'rx':                 0,
    'ry':                 0,
  };

  QUnit.module('fabric.Rect');

  test('constructor', function(){
    ok(fabric.Rect);

    var rect = new fabric.Rect();

    ok(rect instanceof fabric.Rect);
    ok(rect instanceof fabric.Object);

    deepEqual(rect.get('type'), 'rect');
  });

  test('complexity', function() {
    var rect = new fabric.Rect();

    ok(typeof rect.complexity == 'function');
  });

  test('toObject', function() {
    var rect = new fabric.Rect();
    ok(typeof rect.toObject == 'function');

    var object = rect.toObject();
    deepEqual(object, REFERENCE_RECT);
  });

  test('fabric.Rect.fromObject', function() {
    ok(typeof fabric.Rect.fromObject == 'function');

    var rect = fabric.Rect.fromObject(REFERENCE_RECT);
    ok(rect instanceof fabric.Rect);
    deepEqual(rect.toObject(), REFERENCE_RECT);
  });

  test('fabric.Rect.fromElement', function() {
    ok(typeof fabric.Rect.fromElement == 'function');

    var elRect = fabric.document.createElement('rect');
    var rect = fabric.Rect.fromElement(elRect);

    ok(rect instanceof fabric.Rect);
    deepEqual(rect.toObject(), REFERENCE_RECT);
  });

  test('fabric.Rect.fromElement with custom attributes', function() {
    var elRectWithAttrs = fabric.document.createElement('rect');

    elRectWithAttrs.setAttribute('x', 10);
    elRectWithAttrs.setAttribute('y', 20);
    elRectWithAttrs.setAttribute('width', 222);
    elRectWithAttrs.setAttribute('height', 333);
    elRectWithAttrs.setAttribute('rx', 11);
    elRectWithAttrs.setAttribute('ry', 12);
    elRectWithAttrs.setAttribute('fill', 'rgb(255,255,255)');
    elRectWithAttrs.setAttribute('opacity', 0.45);
    elRectWithAttrs.setAttribute('stroke', 'blue');
    elRectWithAttrs.setAttribute('stroke-width', 3);
    elRectWithAttrs.setAttribute('stroke-dasharray', '5, 2');
    elRectWithAttrs.setAttribute('stroke-linecap', 'round');
    elRectWithAttrs.setAttribute('stroke-linejoin', 'bevil');
    elRectWithAttrs.setAttribute('stroke-miterlimit', 5);
    //elRectWithAttrs.setAttribute('transform', 'translate(-10,-20) scale(2) rotate(45) translate(5,10)');

    var rectWithAttrs = fabric.Rect.fromElement(elRectWithAttrs);
    ok(rectWithAttrs instanceof fabric.Rect);

    var expectedObject = fabric.util.object.extend(REFERENCE_RECT, {
      left:             10,
      top:              20,
      width:            222,
      height:           333,
      fill:             'rgb(255,255,255)',
      opacity:          0.45,
      stroke:           'blue',
      strokeWidth:      3,
      strokeDashArray:  [5, 2],
      strokeLineCap:    'round',
      strokeLineJoin:   'bevil',
      strokeMiterLimit: 5,
      rx:               11,
      ry:               12
    });
    deepEqual(rectWithAttrs.toObject(), expectedObject);
  });

  test('empty fromElement', function() {
    ok(fabric.Rect.fromElement() === null);
  });

  test('clone with rounded corners', function() {
    var rect = new fabric.Rect({ width: 100, height: 100, rx: 20, ry: 30 });
    var clone = rect.clone();

    equal(clone.get('rx'), rect.get('rx'));
    equal(clone.get('ry'), rect.get('ry'));
  });

  test('toSVG with rounded corners', function() {
    var rect = new fabric.Rect({ width: 100, height: 100, rx: 20, ry: 30 });
    var svg = rect.toSVG();

    equal(svg, '<rect x="-50" y="-50" rx="20" ry="30" width="100" height="100" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform="translate(50 50)"/>\n');
  });

  test('toObject without default values', function() {
    var options = { type: 'rect', width: 69, height: 50, left: 10, top: 20 };
    var rect = new fabric.Rect(options);
    rect.includeDefaultValues = false;
    deepEqual(rect.toObject(), options);
  });
})();
<|endoftext|>


<commit_before>'use strict';

var dependencies = [
    'ngRoute',
    'ngSanitize',
];

var app = angular.module('searchResults', dependencies);

var template = '

'
 +'
'
       +'

'
      +'

'
 +'
';

app.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/', {
        template: template,
        controller: 'SearchCtrl',
        controllerAs: 'SearchCtrl'
    }).otherwise({redirectTo: '/'});
}]);

app.controller('SearchCtrl', ['$location', '$http', function($location, $http) {
    var controller = this
    controller.searchPhrase = $location.search().q;
    controller.hits = [];
    if (controller.searchPhrase) {
        var postBody = {
            query: {
                match_phrase: {
                    content: controller.searchPhrase
                }
            },
            highlight: {
                fields: {
                    content: {}
                }
            }
        }
        $http.post('/lines/api/sitemap/_search', postBody).success(
            function(response) {
                controller.hits = response.data.hits.hits;
            }
        );
    }
}]);
<commit_msg>debugging angular<commit_after>'use strict';

var dependencies = [
    'ngRoute',
    'ngSanitize',
];

var app = angular.module('searchResults', dependencies);

var template = '

'
 +'
'
       +'

'
      +'

'
 +'
';

app.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/', {
        template: template,
        controller: 'SearchCtrl',
        controllerAs: 'SearchCtrl'
    }).otherwise({redirectTo: '/'});
}]);

app.controller('SearchCtrl', ['$location', '$http', function($location, $http) {
    var controller = this
    controller.searchPhrase = $location.search().q;
    controller.hits = [];
    if (controller.searchPhrase) {
        var postBody = {
            query: {
                match_phrase: {
                    content: controller.searchPhrase
                }
            },
            highlight: {
                fields: {
                    content: {}
                }
            }
        }
        $http.post('/lines/api/sitemap/_search', postBody).then(
            function(response) {
                console.log(response);
                controller.hits = response.data.hits.hits;
            }
        );
    }
}]);
<|endoftext|>


<commit_before>'use strict';

module.exports = function(app) {
  app.controller('CommentController', ['$rootScope', '$scope', 'CommentService', 'GameService', 'UserService', function($rs, $scope, commentService, gameService, userService) {

    let ctrl = this;
    ctrl.user = userService.data.user;

    ctrl.create = function(gameId, content) {
      commentService.create(gameId, ctrl.user._id, ctrl.user.fullName, content)
        .then((newComment) => {
          let updateOptions = {
            $addToSet: { comments: newComment._id },
          };
          gameService.updateById(gameId, updateOptions)
            .then(gameService.getAllById(ctrl.user.gameIds))
            .catch((err) => {
              console.log('Error updating game.');
            });

          userService.updateByEmailOrUsername(ctrl.user.email, updateOptions)
            .catch((err) => {
              console.log('Error updating user.');
            });
        })
        .catch((err) => {
          console.log('Error posting comment.');
        });
    };

    ctrl.update = function(publicId, content) {
      commentService.updateByPublicId(publicId, content)
        .then(() => {
          gameService.getAllById(ctrl.user.gameIds);
          ctrl.editing = false;
        })
        .catch((err) => {
          console.log('Error updating comment.');
        });
    };

    ctrl.remove = function(publicId, commentId, gameId) {
      let updateOptions = {
        $pull: { comments: commentId },
      };

      commentService.removeByPublicId(publicId);
      userService.updateByEmailOrUsername(ctrl.user.email, updateOptions);
      gameService.updateById(gameId, updateOptions)
        .then(() => {
          gameService.getAllById(ctrl.user.gameIds);
        });
    };
  }]);
};
<commit_msg>Refactor callbacks.<commit_after>'use strict';

module.exports = function(app) {
  app.controller('CommentController', ['$rootScope', '$scope', 'CommentService', 'GameService', 'UserService', function($rs, $scope, commentService, gameService, userService) {

    let ctrl = this;
    ctrl.user = userService.data.user;

    ctrl.create = function(gameId, content) {
      commentService.create(gameId, ctrl.user._id, ctrl.user.fullName, content)
        .then((newComment) => {
          let updateOptions = {
            $addToSet: { comments: newComment._id },
          };
          userService.updateByEmailOrUsername(ctrl.user.email, updateOptions)
            .then(gameService.updateById(gameId, updateOptions))
              .then(gameService.getAllById(ctrl.user.gameIds));
        })
        .catch((err) => {
          console.log('Error posting comment.');
        });
    };

    ctrl.update = function(publicId, content) {
      commentService.updateByPublicId(publicId, content)
        .then(() => {
          gameService.getAllById(ctrl.user.gameIds);
          ctrl.editing = false;
        })
        .catch((err) => {
          console.log('Error updating comment.');
        });
    };

    ctrl.remove = function(publicId, commentId, gameId) {
      let updateOptions = {
        $pull: { comments: commentId },
      };
      commentService.removeByPublicId(publicId)
        .then(userService.updateByEmailOrUsername(ctrl.user.email, updateOptions))
          .then(gameService.updateById(gameId, updateOptions))
            .then(gameService.getAllById(ctrl.user.gameIds));
    };
  }]);
};
<|endoftext|>


<commit_before>import localStorageFakePromise from '../';

describe('engine', () => {
    describe('load', () => {
        beforeEach(() => {
            global.localStorage = {
                getItem: sinon.stub().returns(null)
            };
        });

        afterEach(() => {
            delete global.localStorage;
        });

        it('should return a promise-like object', () => {
            const engine = localStorageFakePromise('key');

            const prom = engine.load();
            prom.should.be.a.object;
            prom.should.have.a.property('then');
            prom.should.have.a.property('catch');
            prom.then.should.be.a.func;
            prom.catch.should.be.a.func;
        });

        it('should call the cb on then', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();

            engine.load().then(spy);

            spy.should.have.been.called;
        });

        it('should pass the resolved object on load', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();
            localStorage.getItem.returns('{"key":"value"}');

            engine.load().then(spy);

            spy.should.have.been.calledWith({ key: 'value' });
        });

        it('should pass the result of .then forward', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();

            engine.load().then(() => 'nextValue').then(spy);

            spy.should.have.been.calledWith('nextValue');
        });

        it('should support then without a callback', () => {
            const engine = localStorageFakePromise('foo');

            engine.load().then();
        });

        it('should be chainable like normal promises', () => {
            const engine = localStorageFakePromise('foo');

            engine.load().then().catch().then().catch();
            engine.load().then().then();
            engine.load().catch().catch();
        });

        it('should load with the right key', () => {
            const engine = localStorageFakePromise('foo');

            engine.load().then();

            localStorage.getItem.should.have.been.calledWith('foo');
        });

        it('should return a empty object if nothing stored', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();
            localStorage.getItem.returns(null);

            engine.load().then(spy);

            spy.should.have.been.calledWith({});
        });

        it('should NOT call the cb on catch', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();

            engine.load().catch(spy);

            spy.should.not.have.been.called;
        });
    });

    describe('save', () => {
        let oldLocalStorage;
        beforeEach(() => {
            oldLocalStorage = root.localStorage;
            global.localStorage = {
                setItem: sinon.spy()
            };
        });

        afterEach(() => {
            global.localStorage = oldLocalStorage;
            oldLocalStorage = null;
        });

        it('should return a promise-like object', () => {
            const engine = localStorageFakePromise('key');

            const prom = engine.save();
            prom.should.be.a.object;
            prom.should.have.a.property('then');
            prom.should.have.a.property('catch');
            prom.then.should.be.a.func;
            prom.catch.should.be.a.func;
        });

        it('should call the cb on then', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();

            engine.save().then(spy);

            spy.should.have.been.called;
        });

        it('should support then without a callback', () => {
            const engine = localStorageFakePromise('foo');

            engine.save().then();
        });

        it('should be chainable like normal promises', () => {
            const engine = localStorageFakePromise('foo');

            engine.save().then().catch().then().catch();
            engine.save().then().then();
            engine.save().catch().catch();
        });

        it('should call localStorage.setItem with the json string', () => {
            const engine = localStorageFakePromise('key');

            engine.save({ key: 'value' });

            localStorage.setItem
                .should.have.been.calledWith(sinon.match.any, '{"key":"value"}');
        });

        it('should call localStorage.setItem with the right key', () => {
            const engine = localStorageFakePromise('foo');

            engine.save({});

            localStorage.setItem
                .should.have.been.calledWith('foo');
        });

        it('should NOT call the cb on catch', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();

            engine.save().catch(spy);

            spy.should.not.have.been.called;
        });
    });
});
<commit_msg>fix depraction warning<commit_after>import localStorageFakePromise from '../';

describe('engine', () => {
    describe('load', () => {
        beforeEach(() => {
            global.localStorage = {
                getItem: sinon.stub().returns(null)
            };
        });

        afterEach(() => {
            delete global.localStorage;
        });

        it('should return a promise-like object', () => {
            const engine = localStorageFakePromise('key');

            const prom = engine.load();
            prom.should.be.a.object;
            prom.should.have.a.property('then');
            prom.should.have.a.property('catch');
            prom.then.should.be.a.func;
            prom.catch.should.be.a.func;
        });

        it('should call the cb on then', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();

            engine.load().then(spy);

            spy.should.have.been.called;
        });

        it('should pass the resolved object on load', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();
            localStorage.getItem.returns('{"key":"value"}');

            engine.load().then(spy);

            spy.should.have.been.calledWith({ key: 'value' });
        });

        it('should pass the result of .then forward', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();

            engine.load().then(() => 'nextValue').then(spy);

            spy.should.have.been.calledWith('nextValue');
        });

        it('should support then without a callback', () => {
            const engine = localStorageFakePromise('foo');

            engine.load().then();
        });

        it('should be chainable like normal promises', () => {
            const engine = localStorageFakePromise('foo');

            engine.load().then().catch().then().catch();
            engine.load().then().then();
            engine.load().catch().catch();
        });

        it('should load with the right key', () => {
            const engine = localStorageFakePromise('foo');

            engine.load().then();

            localStorage.getItem.should.have.been.calledWith('foo');
        });

        it('should return a empty object if nothing stored', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();
            localStorage.getItem.returns(null);

            engine.load().then(spy);

            spy.should.have.been.calledWith({});
        });

        it('should NOT call the cb on catch', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();

            engine.load().catch(spy);

            spy.should.not.have.been.called;
        });
    });

    describe('save', () => {
        let oldLocalStorage;
        beforeEach(() => {
            oldLocalStorage = global.localStorage;
            global.localStorage = {
                setItem: sinon.spy()
            };
        });

        afterEach(() => {
            global.localStorage = oldLocalStorage;
            oldLocalStorage = null;
        });

        it('should return a promise-like object', () => {
            const engine = localStorageFakePromise('key');

            const prom = engine.save();
            prom.should.be.a.object;
            prom.should.have.a.property('then');
            prom.should.have.a.property('catch');
            prom.then.should.be.a.func;
            prom.catch.should.be.a.func;
        });

        it('should call the cb on then', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();

            engine.save().then(spy);

            spy.should.have.been.called;
        });

        it('should support then without a callback', () => {
            const engine = localStorageFakePromise('foo');

            engine.save().then();
        });

        it('should be chainable like normal promises', () => {
            const engine = localStorageFakePromise('foo');

            engine.save().then().catch().then().catch();
            engine.save().then().then();
            engine.save().catch().catch();
        });

        it('should call localStorage.setItem with the json string', () => {
            const engine = localStorageFakePromise('key');

            engine.save({ key: 'value' });

            localStorage.setItem
                .should.have.been.calledWith(sinon.match.any, '{"key":"value"}');
        });

        it('should call localStorage.setItem with the right key', () => {
            const engine = localStorageFakePromise('foo');

            engine.save({});

            localStorage.setItem
                .should.have.been.calledWith('foo');
        });

        it('should NOT call the cb on catch', () => {
            const engine = localStorageFakePromise('key');
            const spy = sinon.spy();

            engine.save().catch(spy);

            spy.should.not.have.been.called;
        });
    });
});
<|endoftext|>


<commit_before>import * as utils from 'src/utils';
import { registerBidder } from 'src/adapters/bidderFactory';
import {config} from 'src/config';

const BIDDER_CODE = 'districtmDMX';

const DMXURI = 'https://dmx.districtm.io/b/v1';

export const spec = {
  code: BIDDER_CODE,
  supportedFormat: ['banner'],
  isBidRequestValid(bid) {
    return !!(bid.params.dmxid && bid.params.memberid);
  },
  interpretResponse(response, bidRequest) {
    response = response.body || {};
    if (response.seatbid) {
      if (utils.isArray(response.seatbid)) {
        const {seatbid} = response;
        let winners = seatbid.reduce((bid, ads) => {
          let ad = ads.bid.reduce(function(oBid, nBid) {
            if (oBid.price < nBid.price) {
              const bid = matchRequest(nBid.impid, bidRequest);
              const {width, height} = defaultSize(bid);
              nBid.cpm = nBid.price;
              nBid.bidId = nBid.impid;
              nBid.requestId = nBid.impid;
              nBid.width = nBid.w || width;
              nBid.height = nBid.h || height;
              nBid.ad = nBid.adm;
              nBid.netRevenue = true;
              nBid.creativeId = nBid.crid;
              nBid.currency = 'USD';
              nBid.ttl = 60;

              return nBid;
            } else {
              oBid.cpm = oBid.price;
              return oBid;
            }
          }, {price: 0});
          if (ad.adm) {
            bid.push(ad)
          }
          return bid;
        }, [])
        let winnersClean = winners.filter(w => {
          if (w.bidId) {
            return true;
          }
          return false;
        });
        return winnersClean;
      } else {
        return [];
      }
    } else {
      return [];
    }
  },
  buildRequests(bidRequest, bidderRequest) {
    let timeout = config.getConfig('bidderTimeout');
    let dmxRequest = {
      id: utils.generateUUID(),
      cur: ['USD'],
      tmax: (timeout - 300),
      test: this.test() || 0,
      site: {
        publisher: { id: String(bidRequest[0].params.memberid) || null }
      }
    }
    if (!dmxRequest.test) {
      delete dmxRequest.test;
    }
    if (bidderRequest.gdprConsent) {
      dmxRequest.regs = {};
      dmxRequest.regs.ext = {};
      dmxRequest.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies === true ? 1 : 0;
      dmxRequest.user = {};
      dmxRequest.user.ext = {};
      dmxRequest.user.ext.consent = bidderRequest.gdprConsent.consentString;
    }
    let tosendtags = bidRequest.map(dmx => {
      var obj = {};
      obj.id = dmx.bidId;
      obj.tagid = String(dmx.params.dmxid);
      obj.secure = window.location.protocol === 'https:' ? 1 : 0;
      obj.banner = {
        topframe: 1,
        w: dmx.sizes[0][0] || 0,
        h: dmx.sizes[0][1] || 0,
        format: dmx.sizes.map(s => {
          return {w: s[0], h: s[1]};
        }).filter(obj => typeof obj.w === 'number' && typeof obj.h === 'number')
      };
      return obj;
    });
    dmxRequest.imp = tosendtags;
    return {
      method: 'POST',
      url: DMXURI,
      data: JSON.stringify(dmxRequest),
      options: {
        contentType: 'application/json',
        withCredentials: true
      },
      bidderRequest
    }
  },
  test() {
    return window.location.href.indexOf('dmTest=true') !== -1 ? 1 : 0;
  },
  getUserSyncs(optionsType) {
    if (optionsType.iframeEnabled) {
      return [{
        type: 'iframe',
        url: 'https://cdn.districtm.io/ids/index.html'
      }];
    }
  }
}

/**
 * Function matchRequest(id: string, BidRequest: object)
 * @param id
 * @type string
 * @param bidRequest
 * @type Object
 * @returns Object
 *
 */
export function matchRequest(id, bidRequest) {
  const {bids} = bidRequest.bidderRequest;
  const [returnValue] = bids.filter(bid => bid.bidId === id);
  return returnValue;
}
export function checkDeepArray(Arr) {
  if (Array.isArray(Arr)) {
    if (Array.isArray(Arr[0])) {
      return Arr[0];
    } else {
      return Arr;
    }
  } else {
    return Arr;
  }
}
export function defaultSize(thebidObj) {
  const {sizes} = thebidObj;
  const returnObject = {};
  returnObject.width = checkDeepArray(sizes)[0];
  returnObject.height = checkDeepArray(sizes)[1];
  return returnObject;
}
registerBidder(spec);
<commit_msg>changed default ttl<commit_after>import * as utils from 'src/utils';
import { registerBidder } from 'src/adapters/bidderFactory';
import {config} from 'src/config';

const BIDDER_CODE = 'districtmDMX';

const DMXURI = 'https://dmx.districtm.io/b/v1';

export const spec = {
  code: BIDDER_CODE,
  supportedFormat: ['banner'],
  isBidRequestValid(bid) {
    return !!(bid.params.dmxid && bid.params.memberid);
  },
  interpretResponse(response, bidRequest) {
    response = response.body || {};
    if (response.seatbid) {
      if (utils.isArray(response.seatbid)) {
        const {seatbid} = response;
        let winners = seatbid.reduce((bid, ads) => {
          let ad = ads.bid.reduce(function(oBid, nBid) {
            if (oBid.price < nBid.price) {
              const bid = matchRequest(nBid.impid, bidRequest);
              const {width, height} = defaultSize(bid);
              nBid.cpm = nBid.price;
              nBid.bidId = nBid.impid;
              nBid.requestId = nBid.impid;
              nBid.width = nBid.w || width;
              nBid.height = nBid.h || height;
              nBid.ad = nBid.adm;
              nBid.netRevenue = true;
              nBid.creativeId = nBid.crid;
              nBid.currency = 'USD';
              nBid.ttl = 300;

              return nBid;
            } else {
              oBid.cpm = oBid.price;
              return oBid;
            }
          }, {price: 0});
          if (ad.adm) {
            bid.push(ad)
          }
          return bid;
        }, [])
        let winnersClean = winners.filter(w => {
          if (w.bidId) {
            return true;
          }
          return false;
        });
        return winnersClean;
      } else {
        return [];
      }
    } else {
      return [];
    }
  },
  buildRequests(bidRequest, bidderRequest) {
    let timeout = config.getConfig('bidderTimeout');
    let dmxRequest = {
      id: utils.generateUUID(),
      cur: ['USD'],
      tmax: (timeout - 300),
      test: this.test() || 0,
      site: {
        publisher: { id: String(bidRequest[0].params.memberid) || null }
      }
    }
    if (!dmxRequest.test) {
      delete dmxRequest.test;
    }
    if (bidderRequest.gdprConsent) {
      dmxRequest.regs = {};
      dmxRequest.regs.ext = {};
      dmxRequest.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies === true ? 1 : 0;
      dmxRequest.user = {};
      dmxRequest.user.ext = {};
      dmxRequest.user.ext.consent = bidderRequest.gdprConsent.consentString;
    }
    let tosendtags = bidRequest.map(dmx => {
      var obj = {};
      obj.id = dmx.bidId;
      obj.tagid = String(dmx.params.dmxid);
      obj.secure = window.location.protocol === 'https:' ? 1 : 0;
      obj.banner = {
        topframe: 1,
        w: dmx.sizes[0][0] || 0,
        h: dmx.sizes[0][1] || 0,
        format: dmx.sizes.map(s => {
          return {w: s[0], h: s[1]};
        }).filter(obj => typeof obj.w === 'number' && typeof obj.h === 'number')
      };
      return obj;
    });
    dmxRequest.imp = tosendtags;
    return {
      method: 'POST',
      url: DMXURI,
      data: JSON.stringify(dmxRequest),
      options: {
        contentType: 'application/json',
        withCredentials: true
      },
      bidderRequest
    }
  },
  test() {
    return window.location.href.indexOf('dmTest=true') !== -1 ? 1 : 0;
  },
  getUserSyncs(optionsType) {
    if (optionsType.iframeEnabled) {
      return [{
        type: 'iframe',
        url: 'https://cdn.districtm.io/ids/index.html'
      }];
    }
  }
}

/**
 * Function matchRequest(id: string, BidRequest: object)
 * @param id
 * @type string
 * @param bidRequest
 * @type Object
 * @returns Object
 *
 */
export function matchRequest(id, bidRequest) {
  const {bids} = bidRequest.bidderRequest;
  const [returnValue] = bids.filter(bid => bid.bidId === id);
  return returnValue;
}
export function checkDeepArray(Arr) {
  if (Array.isArray(Arr)) {
    if (Array.isArray(Arr[0])) {
      return Arr[0];
    } else {
      return Arr;
    }
  } else {
    return Arr;
  }
}
export function defaultSize(thebidObj) {
  const {sizes} = thebidObj;
  const returnObject = {};
  returnObject.width = checkDeepArray(sizes)[0];
  returnObject.height = checkDeepArray(sizes)[1];
  return returnObject;
}
registerBidder(spec);
<|endoftext|>