<commit_before>var expect = require('expect.js'),
    moment = require('moment'),
    Sinon = require('sinon'),
    async = require('async'),
    _ = require('lodash'),
    testHelper = require('../helper');

describe('Jobs', function() {
  var dbs = [];
  var jobs;
  beforeEach(function(done) {
    async.times(2, testHelper.connectToDB, function(err, results) {
      if (err) return done(err);
      dbs = results;
      jobs = require('../../lib/jobs')({ db: process.env.DATABASE_URL });
      jobsModelTest = require('../../models/jobs_test');
      done();
    });
  });

  afterEach(function() {
    // Apparently this should not be called:
    // https://github.com/brianc/node-postgres/wiki/Client#wiki-method-end
    // _.invoke(dbs, 'end');
  });

  function lockJob(db, jobId, callback) {
    // Note: In this test we use the job_snapshot id not the job_id as
    // that is how the locking is done in the query that grabs the next
    // job.
    // This only locks one row (not all the snapshots for the job). It
    // probably would be fine to lock on the job_id, but it is not
    // necessary as we should only ever have one snapshot of a given job
    // that is up for processing...
    db.query('select pg_advisory_lock(id) from job_snapshots ' +
      'where job_id = $1 and processed IS NULL', [jobId], callback);
  }

  function unlockJob(db, jobId, callback) {
    db.query('select pg_advisory_unlock(id) from job_snapshots ' +
      'where job_id = $1', [jobId], callback);
  }

  describe('#create', function() {
    beforeEach(function(done) {
      dbs[1].query('delete from job_snapshots', done);
    });

    it('creates a job with given initial state, time ' +
      'to process in and payload, giving id of created job.',
      function(done) {
        var now = moment();

        jobs.create({state: 'waiting', date: 'some'}, 100 * 1000, expectations);

        function expectations(err, id) {
          if (err) return done(err);
          expect(id).to.be.a('number');

          jobsModelTest.getJobs(dbs[1], function(err, result) {
            if (err) return done(err);

            expect(result.length).to.equal(1);
            expect(result[0]).to.have.property('process_at');
            expect(moment(result[0].process_at).diff(now.add('seconds',
                100), 'seconds')).to.equal(0);
            expect(result[0].data).to.have.property('state', 'waiting');
            done();
          });
        }
      });
  });

  /** Consume jobs waiting for service.
   * process(callback) calls the supplied callback on each job that is
   * awaiting service.
   * Callback must be of the form: callback(id, job, done).
   * When callback is finished providing service to the job, done should be
   * called with the updated job data, like so:
   * done(error, newJobData, msUntilNextProcessing);
   * If error is non-null then no updates will be made to the job.
   * If msUntilNextProcessing is null, then the job will not be serviced again.
   * See below for an example callback (jobIterator).
   */
  describe('#process', function() {
    var jobIterator = function(id, job, jobDone) {
      function error() {
        // We just always error for this test.
        return true;
      }

      if (error() && job.retriesRemaining > 0 ) {
        return jobDone(null, {
          state: 'pendingRetry',
          log: 'it failed!, retrying soon',
          retriesRemaining: --job.retriesRemaining
        }, 0);
      } else if (error()) {
        return jobDone(null, {
          state : 'permanentlyFailed',
          log: 'it failed and we are stopping here!',
          retriesRemaining: 0
        }, null);
      } else {
        return jobDone(null, {
          state : 'complete',
          log: 'it worked!',
          retriesRemaining: 0
        }, null);
      }
    };

    var maybeServiceJobCount, jobUpdatedCount;

    var maybeServiceJob = function() {
      maybeServiceJobCount++;
    };

    var jobUpdated = function() {
      jobUpdatedCount++;
    };

    beforeEach(function() {
      maybeServiceJobCount = 0;
      jobUpdatedCount = 0;

      jobs.eventEmitter.removeAllListeners();
      jobs.eventEmitter.on('jobUpdated', jobUpdated);
      jobs.eventEmitter.on('maybeServiceJob', maybeServiceJob);
    });

    afterEach(function() {
      jobs.stopProcessing();
    });

    it('re-schedules a job iff a non-null serviceNextIn property is provided',
          function(done) {
      // Set up jobs data
      jobsModelTest.setJobs(dbs[1], [{
        data: {
          retriesRemaining: 3
        },
        process_at: moment().add('milliseconds', 1)
      }, {
        data: {
          retriesRemaining: 2
        },
        process_at: moment().add('milliseconds', 4)
      }, {
        data: {
          retriesRemaining: 2
        },
        process_at: moment().add('milliseconds', 5)
      }]);

      // Set up our condition
      jobs.eventEmitter.on('processCommitted', function() {
        // Should need 10 iterations for all jobs to retried out of existence.
        // Each job will be provided service until no retries remaining, then
        // once more to figure out that we need to permanently fail it (and
        // hence not requeue.)
        // I.e. retries remaining for each job + no. jobs initially == 10
        if (jobUpdatedCount == 10) {
          jobsModelTest.scheduledJobs(dbs[1], function(err, result) {
            expect(result.length, 'length of job queue').to.equal(0);
          });
        }
      });
      jobs.eventEmitter.on('drain', function() {
        jobs.stopProcessing();
        done();
      });

      // Run the test
      jobs.process(jobIterator);
    });

    it('reschedules a job to have the correct execution time', function(done) {
      // Set up jobs data
      jobsModelTest.setJobs(dbs[1], [{
        data: {
          retriesRemaining: 3
        },
        process_at: moment().toDate()
      }]);

      var iterator = function(id, job, cb) {
        cb(null, {}, moment.duration(10, 'days').asMilliseconds());
      };

      // Set up our condition
      jobs.eventEmitter.on('processCommitted', function() {
        jobsModelTest.scheduledJobs(dbs[1], function(err, result) {
          if (err) return done(err);
          expect(result.length, 'length of job queue').to.equal(1);
          // Expect the process_at time to be in 10 days.
          // Add 5 minutes to allow for processing lag.
          expect(moment(result[0].process_at).add(5, 'minutes').
            // We use hours not days, as moment.js assumes
            // you want to keep the hour same when adding a unit of days
            // across daylight savings boundaries. This means the test will
            // still pass (as it should) if the test is run within ten
            // days of going off daylight savings time.
            diff(moment(), 'hours')).to.equal(10 * 24);
        });
      });

      jobs.eventEmitter.on('drain', function() {
        jobs.stopProcessing();
        done();
      });

      // Run the test
      jobs.process(iterator);
    });

    it('will re-execute when processIn is non-null', function(done) {
      jobs.create({}, null, created);

      function created(err, jobId) {
        if (err) return done(err);
        jobs.processNow(jobId, iterator);
      }

      function iterator(jobId, data, callback) {
        jobs.process(function(jobId, jobData, callback) {
          jobs.stopProcessing();
          done();
        });
        setTimeout(function() {callback(null, {}, 1);}, 1);
      }
    });

    it('will re-execute when processIn is zero', function(done) {
      jobs.create({}, null, created);

      function created(err, jobId) {
        if (err) return done(err);
        jobs.processNow(jobId, iterator);
      }

      function iterator(jobId, data, callback) {
        jobs.process(function(jobId, jobData, callback) {
          jobs.stopProcessing();
          done();
        });
        setTimeout(function() {callback(null, {}, 0);}, 1);
      }
    });

    // Just binds test and prep together.
    describe('with a delayed job', function() {
      beforeEach(function(done) {
        // Set up some jobs.
        jobsModelTest.setJobs(dbs[1], [{
          data: {
            retriesRemaining: 1,
            wee: 'wah'
          },
          process_at: moment().add('milliseconds', 1)
        }, {
          data: {
            retriesRemaining: 5,
            boo: 'hoo'
          },
          process_at: moment().add('milliseconds', 40000)
        }], done);
      });

      it('provides service only when correct number of ms have elapsed.',
          function(done) {
        async.parallel([
          jobs.create.bind(null, {name: 'jobOne'}, 20),
          jobs.create.bind(null, {name: 'jobTwo'}, 10)
        ], created);

        function created(err) {
          if (err) return done(err);

          jobs.process(iterator);
        }

        function iterator(jobId, jobData, cb) {
          if (jobData.name === 'jobOne') throw new Error('should not service jobOne before jobTwo');
          cb();
          done();
        }
      });
    });

    // Just binds test and setup together.
    describe('with a locked job', function() {
      beforeEach(function(done) {
        jobsModelTest.setJobs(dbs[1], [{
          job_id: 1,
          data: {
            retriesRemaining: 1
          },
          process_at: moment().add('milliseconds', 0)
        }, {
          data: {
            retriesRemaining: 5
          },
          process_at: moment().add('milliseconds', 0)
        }, {
          data: {
            retriesRemaining: 5
          },
          process_at: moment().add('milliseconds', 10000)
        }], lockFirstJob);

        function lockFirstJob() {
          lockJob(dbs[1], 1, done);
        }
      });
    });

    //bind setup and test together
    describe('when called on a job', function() {
      beforeEach(function(done) {
        jobsModelTest.setJobs(dbs[1], [{
          job_id: 123,
          data: [],
          process_at: moment()
        }], done);
      });

      it('saves the new job data given to the db, in a non-destructive way',
          function(done) {
        var iterator = function(id, job, cb) {
          return cb(null, {
            state: 'complete',
            name: "tim"
          }, null);
        };

        jobs.eventEmitter.on('processCommitted', function() {
          jobsModelTest.getJobs(dbs[1], function(err, result) {
            if (err) return done(err);
            expect(result).to.have.length(2);
          });
        });

        jobs.eventEmitter.on('drain', function() {
          jobs.stopProcessing();
          done();
        });

        jobs.process(iterator);
      });

      it('provides the job id to the iterator', function(done) {
        function iterator(id, job, cb) {
          expect(id).to.equal(123);
          jobs.stopProcessing();
          cb(null, {}, null);
        }
        jobs.eventEmitter.on('stopProcess', done);
        jobs.process(iterator);
      });
    });
    it('calls the callback when finished', function(done) {
      jobs.process(function() {}, done);
      jobs.stopProcessing();
    });
  });
  describe('#processNow', function() {
    describe('when called on a not locked job', function() {
      beforeEach(function(done) {
        // Set up some jobs.
        jobsModelTest.setJobs(dbs[1], [{
          job_id: 1,
          data: {
            retriesRemaining: 1
          },
          process_at: moment().add('milliseconds', 10000).toDate()
        }, {
          job_id: 2,
          data: {
            retriesRemaining: 5
          },
          process_at: moment().add('milliseconds', 40000).toDate()
        }], done);
      });

      it('immediately runs the callback on the requested job, updating it',
          function(done) {
        var iterator = function(id, job, cb) {
          expect(id).to.be(1);
          expect(job).to.have.property('retriesRemaining', 1);
          job.retriesRemaining = 2;
          return cb(null, job, 200);
        };

        // Set up condition
        var checkConditions =  function() {
          jobs.processNow(1, function(jobId, jobData, cb) {
            expect(jobData.retriesRemaining).to.be(2);
            cb(null, {}, null);
          }, done);
        };

        // Run the test.
        jobs.processNow(1, iterator, checkConditions);
      });
    });

    describe('when called on a locked job', function() {
      beforeEach(function(done) {
        jobsModelTest.setJobs(dbs[1], [{
          job_id: 1,
          data: {
            retriesRemaining: 1
          },
          process_at: moment().add('milliseconds', 10000).toDate()
        }], lockFirstJob);

        function lockFirstJob(err) {
          if (err) return done(err);
          lockJob(dbs[1], 1, done);
        }
      });

      it('waits until the lock is ceeded',
          function(done) {
        var secondOne = false;

        var iterator = function(err, job, cb) {

          // This second call should not get service right now
          jobs.processNow(1, function(jobId, jobData, cb) {
            secondOne = true;
            cb();
          });
          expect(secondOne).to.be(false);
          cb();
        };

        // Run the test.
        jobs.processNow(1, iterator, done);
      });
    });
    describe('when called on a job that does not exist', function() {
      it('calls the callback with an error', function(done) {
        var iterator = Sinon.stub().throws(new Error('I should not be called.'));

        jobs.processNow(9999999, iterator, expectations);
        function expectations(err) {
          expect(err).to.be.ok();
          done();
        }
      });
    });
    describe('when the worker calls done() with an error', function() {
      it('calls the callback with an error', function(done) {
        var iterator = Sinon.stub().callsArgWith(2, 'yo');
        jobs.processNow(1, iterator, expectations);
        function expectations(err) {
          expect(err).to.equal('yo');
          done();
        }
      });
    });
    it('always gives waiting processNow() requests fresh data', function(done) {
      jobs.create({}, null, created);

      function created(err, jobId) {
        if (err) return done(err);
        jobs.processNow(jobId, iterator);
      }

      function iterator(jobId, data, callback) {
        data.marked = true;
        jobs.processNow(jobId, expectations);
        setTimeout(function() {callback(null, data, null);}, 1);
      }

      function expectations(jobId, data, callback) {
        expect(data.marked).to.be(true);
        done();
      }
    });
    describe('when two calls for the same jobs conflict', function() {
      it('make sure the second one will see the first call\'s changes', function(done) {
        this.timeout(10000);
        jobs.create({}, null, function(err, jobId) {
          if (err) return done(err);

          jobs.processNow(jobId, firstIterator);

          function firstIterator(jobId, jobData, cb) {
            jobData.changed = true;
            jobs.processNow(jobId, secondIterator, done);
            cb(null, jobData);
          }

          function secondIterator(jobId, jobData, cb) {
            expect(jobData.changed).to.be(true);
            cb(null, jobData);
          }
        });
      });
    });
  });
});

// vim: set et sw=2 ts=2 colorcolumn=80:
<commit_msg>Remove superceded test.<commit_after>var expect = require('expect.js'),
    moment = require('moment'),
    Sinon = require('sinon'),
    async = require('async'),
    _ = require('lodash'),
    testHelper = require('../helper');

describe('Jobs', function() {
  var dbs = [];
  var jobs;
  beforeEach(function(done) {
    async.times(2, testHelper.connectToDB, function(err, results) {
      if (err) return done(err);
      dbs = results;
      jobs = require('../../lib/jobs')({ db: process.env.DATABASE_URL });
      jobsModelTest = require('../../models/jobs_test');
      done();
    });
  });

  afterEach(function() {
    // Apparently this should not be called:
    // https://github.com/brianc/node-postgres/wiki/Client#wiki-method-end
    // _.invoke(dbs, 'end');
  });

  function lockJob(db, jobId, callback) {
    // Note: In this test we use the job_snapshot id not the job_id as
    // that is how the locking is done in the query that grabs the next
    // job.
    // This only locks one row (not all the snapshots for the job). It
    // probably would be fine to lock on the job_id, but it is not
    // necessary as we should only ever have one snapshot of a given job
    // that is up for processing...
    db.query('select pg_advisory_lock(id) from job_snapshots ' +
      'where job_id = $1 and processed IS NULL', [jobId], callback);
  }

  function unlockJob(db, jobId, callback) {
    db.query('select pg_advisory_unlock(id) from job_snapshots ' +
      'where job_id = $1', [jobId], callback);
  }

  describe('#create', function() {
    beforeEach(function(done) {
      dbs[1].query('delete from job_snapshots', done);
    });

    it('creates a job with given initial state, time ' +
      'to process in and payload, giving id of created job.',
      function(done) {
        var now = moment();

        jobs.create({state: 'waiting', date: 'some'}, 100 * 1000, expectations);

        function expectations(err, id) {
          if (err) return done(err);
          expect(id).to.be.a('number');

          jobsModelTest.getJobs(dbs[1], function(err, result) {
            if (err) return done(err);

            expect(result.length).to.equal(1);
            expect(result[0]).to.have.property('process_at');
            expect(moment(result[0].process_at).diff(now.add('seconds',
                100), 'seconds')).to.equal(0);
            expect(result[0].data).to.have.property('state', 'waiting');
            done();
          });
        }
      });
  });

  /** Consume jobs waiting for service.
   * process(callback) calls the supplied callback on each job that is
   * awaiting service.
   * Callback must be of the form: callback(id, job, done).
   * When callback is finished providing service to the job, done should be
   * called with the updated job data, like so:
   * done(error, newJobData, msUntilNextProcessing);
   * If error is non-null then no updates will be made to the job.
   * If msUntilNextProcessing is null, then the job will not be serviced again.
   * See below for an example callback (jobIterator).
   */
  describe('#process', function() {
    var jobIterator = function(id, job, jobDone) {
      function error() {
        // We just always error for this test.
        return true;
      }

      if (error() && job.retriesRemaining > 0 ) {
        return jobDone(null, {
          state: 'pendingRetry',
          log: 'it failed!, retrying soon',
          retriesRemaining: --job.retriesRemaining
        }, 0);
      } else if (error()) {
        return jobDone(null, {
          state : 'permanentlyFailed',
          log: 'it failed and we are stopping here!',
          retriesRemaining: 0
        }, null);
      } else {
        return jobDone(null, {
          state : 'complete',
          log: 'it worked!',
          retriesRemaining: 0
        }, null);
      }
    };

    var maybeServiceJobCount, jobUpdatedCount;

    var maybeServiceJob = function() {
      maybeServiceJobCount++;
    };

    var jobUpdated = function() {
      jobUpdatedCount++;
    };

    beforeEach(function() {
      maybeServiceJobCount = 0;
      jobUpdatedCount = 0;

      jobs.eventEmitter.removeAllListeners();
      jobs.eventEmitter.on('jobUpdated', jobUpdated);
      jobs.eventEmitter.on('maybeServiceJob', maybeServiceJob);
    });

    afterEach(function() {
      jobs.stopProcessing();
    });

    it('reschedules a job to have the correct execution time', function(done) {
      // Set up jobs data
      jobsModelTest.setJobs(dbs[1], [{
        data: {
          retriesRemaining: 3
        },
        process_at: moment().toDate()
      }]);

      var iterator = function(id, job, cb) {
        cb(null, {}, moment.duration(10, 'days').asMilliseconds());
      };

      // Set up our condition
      jobs.eventEmitter.on('processCommitted', function() {
        jobsModelTest.scheduledJobs(dbs[1], function(err, result) {
          if (err) return done(err);
          expect(result.length, 'length of job queue').to.equal(1);
          // Expect the process_at time to be in 10 days.
          // Add 5 minutes to allow for processing lag.
          expect(moment(result[0].process_at).add(5, 'minutes').
            // We use hours not days, as moment.js assumes
            // you want to keep the hour same when adding a unit of days
            // across daylight savings boundaries. This means the test will
            // still pass (as it should) if the test is run within ten
            // days of going off daylight savings time.
            diff(moment(), 'hours')).to.equal(10 * 24);
        });
      });

      jobs.eventEmitter.on('drain', function() {
        jobs.stopProcessing();
        done();
      });

      // Run the test
      jobs.process(iterator);
    });

    it('will re-execute when processIn is non-null', function(done) {
      jobs.create({}, null, created);

      function created(err, jobId) {
        if (err) return done(err);
        jobs.processNow(jobId, iterator);
      }

      function iterator(jobId, data, callback) {
        jobs.process(function(jobId, jobData, callback) {
          jobs.stopProcessing();
          done();
        });
        setTimeout(function() {callback(null, {}, 1);}, 1);
      }
    });

    it('will re-execute when processIn is zero', function(done) {
      jobs.create({}, null, created);

      function created(err, jobId) {
        if (err) return done(err);
        jobs.processNow(jobId, iterator);
      }

      function iterator(jobId, data, callback) {
        jobs.process(function(jobId, jobData, callback) {
          jobs.stopProcessing();
          done();
        });
        setTimeout(function() {callback(null, {}, 0);}, 1);
      }
    });

    // Just binds test and prep together.
    describe('with a delayed job', function() {
      beforeEach(function(done) {
        // Set up some jobs.
        jobsModelTest.setJobs(dbs[1], [{
          data: {
            retriesRemaining: 1,
            wee: 'wah'
          },
          process_at: moment().add('milliseconds', 1)
        }, {
          data: {
            retriesRemaining: 5,
            boo: 'hoo'
          },
          process_at: moment().add('milliseconds', 40000)
        }], done);
      });

      it('provides service only when correct number of ms have elapsed.',
          function(done) {
        async.parallel([
          jobs.create.bind(null, {name: 'jobOne'}, 20),
          jobs.create.bind(null, {name: 'jobTwo'}, 10)
        ], created);

        function created(err) {
          if (err) return done(err);

          jobs.process(iterator);
        }

        function iterator(jobId, jobData, cb) {
          if (jobData.name === 'jobOne') throw new Error('should not service jobOne before jobTwo');
          cb();
          done();
        }
      });
    });

    // Just binds test and setup together.
    describe('with a locked job', function() {
      beforeEach(function(done) {
        jobsModelTest.setJobs(dbs[1], [{
          job_id: 1,
          data: {
            retriesRemaining: 1
          },
          process_at: moment().add('milliseconds', 0)
        }, {
          data: {
            retriesRemaining: 5
          },
          process_at: moment().add('milliseconds', 0)
        }, {
          data: {
            retriesRemaining: 5
          },
          process_at: moment().add('milliseconds', 10000)
        }], lockFirstJob);

        function lockFirstJob() {
          lockJob(dbs[1], 1, done);
        }
      });
    });

    //bind setup and test together
    describe('when called on a job', function() {
      beforeEach(function(done) {
        jobsModelTest.setJobs(dbs[1], [{
          job_id: 123,
          data: [],
          process_at: moment()
        }], done);
      });

      it('saves the new job data given to the db, in a non-destructive way',
          function(done) {
        var iterator = function(id, job, cb) {
          return cb(null, {
            state: 'complete',
            name: "tim"
          }, null);
        };

        jobs.eventEmitter.on('processCommitted', function() {
          jobsModelTest.getJobs(dbs[1], function(err, result) {
            if (err) return done(err);
            expect(result).to.have.length(2);
          });
        });

        jobs.eventEmitter.on('drain', function() {
          jobs.stopProcessing();
          done();
        });

        jobs.process(iterator);
      });

      it('provides the job id to the iterator', function(done) {
        function iterator(id, job, cb) {
          expect(id).to.equal(123);
          jobs.stopProcessing();
          cb(null, {}, null);
        }
        jobs.eventEmitter.on('stopProcess', done);
        jobs.process(iterator);
      });
    });
    it('calls the callback when finished', function(done) {
      jobs.process(function() {}, done);
      jobs.stopProcessing();
    });
  });
  describe('#processNow', function() {
    describe('when called on a not locked job', function() {
      beforeEach(function(done) {
        // Set up some jobs.
        jobsModelTest.setJobs(dbs[1], [{
          job_id: 1,
          data: {
            retriesRemaining: 1
          },
          process_at: moment().add('milliseconds', 10000).toDate()
        }, {
          job_id: 2,
          data: {
            retriesRemaining: 5
          },
          process_at: moment().add('milliseconds', 40000).toDate()
        }], done);
      });

      it('immediately runs the callback on the requested job, updating it',
          function(done) {
        var iterator = function(id, job, cb) {
          expect(id).to.be(1);
          expect(job).to.have.property('retriesRemaining', 1);
          job.retriesRemaining = 2;
          return cb(null, job, 200);
        };

        // Set up condition
        var checkConditions =  function() {
          jobs.processNow(1, function(jobId, jobData, cb) {
            expect(jobData.retriesRemaining).to.be(2);
            cb(null, {}, null);
          }, done);
        };

        // Run the test.
        jobs.processNow(1, iterator, checkConditions);
      });
    });

    describe('when called on a locked job', function() {
      beforeEach(function(done) {
        jobsModelTest.setJobs(dbs[1], [{
          job_id: 1,
          data: {
            retriesRemaining: 1
          },
          process_at: moment().add('milliseconds', 10000).toDate()
        }], lockFirstJob);

        function lockFirstJob(err) {
          if (err) return done(err);
          lockJob(dbs[1], 1, done);
        }
      });

      it('waits until the lock is ceeded',
          function(done) {
        var secondOne = false;

        var iterator = function(err, job, cb) {

          // This second call should not get service right now
          jobs.processNow(1, function(jobId, jobData, cb) {
            secondOne = true;
            cb();
          });
          expect(secondOne).to.be(false);
          cb();
        };

        // Run the test.
        jobs.processNow(1, iterator, done);
      });
    });
    describe('when called on a job that does not exist', function() {
      it('calls the callback with an error', function(done) {
        var iterator = Sinon.stub().throws(new Error('I should not be called.'));

        jobs.processNow(9999999, iterator, expectations);
        function expectations(err) {
          expect(err).to.be.ok();
          done();
        }
      });
    });
    describe('when the worker calls done() with an error', function() {
      it('calls the callback with an error', function(done) {
        var iterator = Sinon.stub().callsArgWith(2, 'yo');
        jobs.processNow(1, iterator, expectations);
        function expectations(err) {
          expect(err).to.equal('yo');
          done();
        }
      });
    });
    it('always gives waiting processNow() requests fresh data', function(done) {
      jobs.create({}, null, created);

      function created(err, jobId) {
        if (err) return done(err);
        jobs.processNow(jobId, iterator);
      }

      function iterator(jobId, data, callback) {
        data.marked = true;
        jobs.processNow(jobId, expectations);
        setTimeout(function() {callback(null, data, null);}, 1);
      }

      function expectations(jobId, data, callback) {
        expect(data.marked).to.be(true);
        done();
      }
    });
    describe('when two calls for the same jobs conflict', function() {
      it('make sure the second one will see the first call\'s changes', function(done) {
        this.timeout(10000);
        jobs.create({}, null, function(err, jobId) {
          if (err) return done(err);

          jobs.processNow(jobId, firstIterator);

          function firstIterator(jobId, jobData, cb) {
            jobData.changed = true;
            jobs.processNow(jobId, secondIterator, done);
            cb(null, jobData);
          }

          function secondIterator(jobId, jobData, cb) {
            expect(jobData.changed).to.be(true);
            cb(null, jobData);
          }
        });
      });
    });
  });
});

// vim: set et sw=2 ts=2 colorcolumn=80:
<|endoftext|>


<commit_before>/* global define, require, jQuery */

/**
 * Carousel3d
 */
(function(factory) {
	'use strict';
	if (typeof define === 'function' && define.amd) {
		define(['jquery', 'modernizr', 'jquery.resize'], factory);
	} else if (typeof exports !== 'undefined') {
		module.exports = factory(require('jquery'), require('modernizr'), require('jquery.resize'));
	} else {
		factory($, Modernizr);
	}
}(function($) {
	'use strict';

	/**
	 * constructor
	 * @param panel
	 * @constructor
	 */
	var Carousel3d = function (panel) {
        this._panel = panel;
        this._leftButton = $(panel).find('[data-carousel-3d-left]')[0];
        this._rightButton = $(panel).find('[data-carousel-3d-right]')[0];
        this._childrenWrapper = $(panel).find('[data-carousel-3d-children]')[0];
        if (this._childrenWrapper) {
            this._children = [];
            $(this._childrenWrapper).children().each(function (index, child) {
                this._children.push(child);
            }.bind(this));
        }

        if (Modernizr.csstransforms3d) {
            $.extend(this, renderer3DTransform);
        }
        else if (Modernizr.csstransforms) {
            $.extend(this, rendererTransform);
        }
        else {
            $.extend(this, rendererTransform);
            this._ieTransform = false;
        }

        if (windowLoaded) {
            this._init();
        } else {
            $(this._panel).css('visibility', 'hidden');
            $(window).load(function () {
                this._init(function () {
                    $(this._panel).css('visibility', 'visible');
                }.bind(this));
            }.bind(this));
        }
	};

    /**
     *
     * @type {number}
     * @private
     */
    Carousel3d.prototype._aspectRatio = 2;

    /**
     *
     * @type {null}
     * @private
     */
    Carousel3d.prototype._panel = null;

    /**
     *
     * @type {null}
     * @private
     */
    Carousel3d.prototype._leftButton = null;

    /**
     *
     * @type {null}
     * @private
     */
    Carousel3d.prototype._rightButton = null;

    /**
     *
     * @type {null}
     * @private
     */
    Carousel3d.prototype._childrenWrapper = null;

    /**
     *
     * @type {null}
     * @private
     */
    Carousel3d.prototype._children = null;

    /**
     *
     * @type {number}
     * @private
     */
    Carousel3d.prototype._currentIndex = 0;

	/**
	 * initializer
	 * @param panel
	 */
	Carousel3d.prototype._init = function (done) {
        var self = this;

        //init panel
        $(this._panel).css('position', 'relative');
        $(this._panel).css('width', '100%');
        $(this._panel).css('height', '100%');

        $(this._leftButton).css('position', 'absolute');
        $(this._rightButton).css('position', 'absolute');
        $(this._leftButton).css('z-index', 1000);
        $(this._rightButton).css('z-index', 1000);
        $(this._leftButton).css('top', '50%');
        $(this._rightButton).css('top', '50%');
        $(this._leftButton).css('left', '0px');
        $(this._rightButton).css('right', '0px');

        $(this._childrenWrapper).css('position', 'absolute');
        $(this._childrenWrapper).css('perspective', '1000px');
        $(this._childrenWrapper).css('list-style-type', 'none');
        $(this._childrenWrapper).css('margin', '0px');
        $(this._childrenWrapper).css('padding', '0px');
        $(this._childrenWrapper).css('width', '100%');
        $(this._childrenWrapper).css('height', '100%');

        $(this._panel).resize(function () {
            var wrapper = this._childrenWrapper;
            var panelWidth = $(this._panel).width();
            var panelHeight = $(this._panel).height();
            var wrapperWidth = Math.min(panelHeight * this._aspectRatio, panelWidth);
            var wrapperHeight = Math.min(wrapperWidth / this._aspectRatio, panelHeight);
            $(wrapper).width(wrapperWidth);
            $(wrapper).height(wrapperHeight);
            $(wrapper).css('left', (panelWidth - wrapperWidth) / 2);
            $(wrapper).css('top', (panelHeight - wrapperHeight) / 2);
            if (this._children) {
                $(this._children).each(function (index, child) {
                    $(child).data('width', $(child).width());
                    $(child).data('height', $(child).height());
                    if ($(child).attr('selected')) {
                        this._currentIndex = index;
                    }
                }.bind(this));
            }
            this._rotateChildren(this._currentIndex);

            if (done) {
                done();
            }
        }.bind(this));

        if (this._children) {
            $(this._children).each(function (index, child) {
                $(child).css('position', 'absolute');
                $(child).css('transition', '1s');
            }.bind(this));
        }

        $(this._leftButton).click(function () {
            this.left();
        }.bind(this));
        $(this._rightButton).click(function () {
            this.right();
        }.bind(this));
	};

    /**
     * make carousel spin left
     */
    Carousel3d.prototype.left = function () {
        this._currentIndex -= 1;
        this._rotateChildren(this._currentIndex);
    };

    /**
     * make carousel spin right
     */
    Carousel3d.prototype.right = function () {
        this._currentIndex += 1;
        this._rotateChildren(this._currentIndex);
    };

    /**
     * Renderer for browsers supporting css transform including ie8
     * @type {{_initChildren: Function, _applyChildZIndex: Function, _rotateChild: Function}}
     */
    var rendererTransform = {
        _ieTransform: false,
        _rotateChildren: function (index) {
            var degree = -index * (360 / this._children.length);
            if (this._children) {
                $(this._children).each(function (index, child) {
                    this._rotateChild(child, index, degree);
                }.bind(this));
            }
        },
        _rotateChild: function (child, index, degree) {
            $(child).css('overflow', 'hidden');
            var baseScale = 1;
            var width = $(child).data('width');
            var height = $(child).data('height');
            var wrapperWidth = $(this._childrenWrapper).width();
            var wrapperHeight = $(this._childrenWrapper).height();
            if ((width / height) > this._aspectRatio) {
                baseScale = wrapperWidth / width;
            } else {
                baseScale = wrapperHeight / height;
            }

            var childDegree = ((360 / this._children.length) * index) + degree;
            $(child).animate({
                '_degree': childDegree
            }, {
                duration: 1000,
                step: function (now, tween) {
                    if (tween.prop === '_degree') {
                        var cos = Math.cos(Math.PI / 180 * now);
                        var dx = Math.sin(Math.PI / 180 * now) * wrapperWidth / 2;
                        var heightScale = baseScale * (cos + 1) / 2;
                        var widthScale = baseScale * Math.abs(cos) * heightScale;
                        if (this._ieTransform) {
                            $(tween.elem).css('filter', 'progid:DXImageTransform.Microsoft.Matrix(M11=' + widthScale + ', M12=0, M21=0, M22=' + heightScale + ', SizingMethod="auto expand")');
                            $(tween.elem).css('-ms-filter', 'progid:DXImageTransform.Microsoft.Matrix(M11=' + widthScale + ', M12=0, M21=0, M22=' + heightScale + ', SizingMethod="auto expand")');
                        } else {
                            $(tween.elem).css('transform-origin', '0px 0px');
                            $(tween.elem).css('transform', 'scale(' + widthScale + ', ' + heightScale + ')');
                        }

                        $(tween.elem).css('z-index', Math.floor((cos + 1) * 10));
                        $(tween.elem).css('top', (wrapperHeight - height * heightScale) / 2 + 'px');
                        $(tween.elem).css('left', ((wrapperWidth - width * widthScale) / 2 + dx) + 'px');
                    }
                }.bind(this)
            });
        }
    };

    /**
     * Renderer for browsers supporting css transform3d
     * @type {{_tz: number, _initChildren: Function, _applyChildZIndex: Function, _rotateChild: Function}}
     */
    var renderer3DTransform = {
        _tz: 0,
        _rotateChildren: function (index) {
            var degree = -index * (360 / this._children.length);
            var wrapperWidth = $(this._childrenWrapper).width();
            this._tz =  (wrapperWidth / 2) / Math.tan(Math.PI / this._children.length);
            if (this._children) {
                $(this._children).each(function (index, child) {
                    this._rotateChild(child, index, degree);
                }.bind(this));
            }
        },
        _rotateChild: function (child, index, degree) {
            degree = degree ? degree : 0;

            //scale, margin
            var scale = 1;
            var width = $(child).width();
            var height = $(child).height();
            var wrapperWidth = $(this._childrenWrapper).width();
            var wrapperHeight = $(this._childrenWrapper).height();
            if ((width / height) > this._aspectRatio) {
                scale = wrapperWidth / width;
            } else {
                scale = wrapperHeight / height;
            }
            var scaledWidth = width * scale;
            var scaledHeight = height * scale;

            $(child).css('left', ((scaledWidth - width) / 2) + ((wrapperWidth - scaledWidth) / 2) + 'px');
            $(child).css('top', ((scaledHeight - height) / 2) + ((wrapperHeight - scaledHeight) / 2) + 'px');

            //rotation
            $(child).data('index', index);
            $(child).data('cssScale', scale);

            var childDegree = ((360 / this._children.length) * index) + degree;
            var transformText = 'scale(' + scale + ')';
            transformText += ' translateZ(' + -this._tz + 'px)';
            transformText += ' rotateY(' + childDegree + 'deg)';
            transformText += ' translateZ(' + this._tz + 'px)';
            $(child).css('transform', transformText);

            $(child).css('z-index', Math.floor((Math.cos(Math.PI / 180 * childDegree) + 1) * 10));
        }
    };

	/**
	 * Exposed to jquery.
	 * @returns {*}
	 */
	$.fn.carousel3d = function() {
		var self = this, opt = arguments[0], args = Array.prototype.slice.call(arguments,1), l = self.length, i = 0, ret;
		for(i; i < l; i += 1) {
			if (typeof opt === 'object' || typeof opt === 'undefined') {
				self[i].carousel3d =  new Carousel3d(self[i], opt);
			}
			else {
				ret = self[i].carousel3d[opt].apply(self[i].carousel3d, args);
			}
			if (ret !== undefined) {
				return ret;
			}
		}
		return self;
	};

	/**
	 * initialize on load
	 */
    $(function () {
        $('[data-carousel-3d]').carousel3d();
    });
    var windowLoaded = false;
    $(window).load(function () {
        windowLoaded = true;
    });

}));<commit_msg>refactoring.<commit_after>/* global define, require, jQuery */

/**
 * Carousel3d
 */
(function(factory) {
	'use strict';
	if (typeof define === 'function' && define.amd) {
		define(['jquery', 'modernizr', 'jquery.resize'], factory);
	} else if (typeof exports !== 'undefined') {
		module.exports = factory(require('jquery'), require('modernizr'), require('jquery.resize'));
	} else {
		factory($, Modernizr);
	}
}(function($) {
	'use strict';

	/**
	 * constructor
	 * @param panel
	 * @constructor
	 */
	var Carousel3d = function (panel) {
        this._panel = panel;
        this._leftButton = $(panel).find('[data-carousel-3d-left]')[0];
        this._rightButton = $(panel).find('[data-carousel-3d-right]')[0];
        this._childrenWrapper = $(panel).find('[data-carousel-3d-children]')[0];
        if (this._childrenWrapper) {
            this._children = [];
            $(this._childrenWrapper).children().each(function (index, child) {
                this._children.push(child);
            }.bind(this));
        }

        if (Modernizr.csstransforms3d) {
            $.extend(this, renderer3DTransform);
        }
        else if (Modernizr.csstransforms) {
            $.extend(this, rendererTransform);
        }
        else {
            $.extend(this, rendererTransform);
            this._ieTransform = false;
        }

        if (windowLoaded) {
            this._init();
        } else {
            $(this._panel).css('visibility', 'hidden');
            $(window).load(function () {
                this._init(function () {
                    $(this._panel).css('visibility', 'visible');
                }.bind(this));
            }.bind(this));
        }
	};

    /**
     *
     * @type {number}
     * @private
     */
    Carousel3d.prototype._aspectRatio = 2;

    /**
     *
     * @type {null}
     * @private
     */
    Carousel3d.prototype._panel = null;

    /**
     *
     * @type {null}
     * @private
     */
    Carousel3d.prototype._leftButton = null;

    /**
     *
     * @type {null}
     * @private
     */
    Carousel3d.prototype._rightButton = null;

    /**
     *
     * @type {null}
     * @private
     */
    Carousel3d.prototype._childrenWrapper = null;

    /**
     *
     * @type {null}
     * @private
     */
    Carousel3d.prototype._children = null;

    /**
     *
     * @type {number}
     * @private
     */
    Carousel3d.prototype._currentIndex = 0;

	/**
	 * initializer
	 * @param panel
	 */
	Carousel3d.prototype._init = function (done) {
        var self = this;

        //init panel
        $(this._panel).css('position', 'relative');
        $(this._panel).css('width', '100%');
        $(this._panel).css('height', '100%');

        $(this._leftButton).css('position', 'absolute');
        $(this._rightButton).css('position', 'absolute');
        $(this._leftButton).css('z-index', 1000);
        $(this._rightButton).css('z-index', 1000);
        $(this._leftButton).css('top', '50%');
        $(this._rightButton).css('top', '50%');
        $(this._leftButton).css('left', '0px');
        $(this._rightButton).css('right', '0px');

        $(this._childrenWrapper).css('position', 'absolute');
        $(this._childrenWrapper).css('perspective', '1000px');
        $(this._childrenWrapper).css('list-style-type', 'none');
        $(this._childrenWrapper).css('margin', '0px');
        $(this._childrenWrapper).css('padding', '0px');
        $(this._childrenWrapper).css('width', '100%');
        $(this._childrenWrapper).css('height', '100%');

        $(this._panel).resize(function () {
            var wrapper = this._childrenWrapper;
            var panelWidth = $(this._panel).width();
            var panelHeight = $(this._panel).height();
            var wrapperWidth = Math.min(panelHeight * this._aspectRatio, panelWidth);
            var wrapperHeight = Math.min(wrapperWidth / this._aspectRatio, panelHeight);
            $(wrapper).width(wrapperWidth);
            $(wrapper).height(wrapperHeight);
            $(wrapper).css('left', (panelWidth - wrapperWidth) / 2);
            $(wrapper).css('top', (panelHeight - wrapperHeight) / 2);
            if (this._children) {
                $(this._children).each(function (index, child) {
                    $(child).data('width', $(child).width());
                    $(child).data('height', $(child).height());
                    if ($(child).attr('selected')) {
                        this._currentIndex = index;
                    }
                }.bind(this));
            }
            this._rotateChildren(this._currentIndex);

            if (done) {
                done();
            }
        }.bind(this));

        if (this._children) {
            $(this._children).each(function (index, child) {
                $(child).css('position', 'absolute');
                $(child).css('transition', '1s');
            }.bind(this));
        }

        $(this._leftButton).click(function () {
            this.left();
        }.bind(this));
        $(this._rightButton).click(function () {
            this.right();
        }.bind(this));
	};

    /**
     * make carousel spin left
     */
    Carousel3d.prototype.left = function () {
        this._currentIndex -= 1;
        this._rotateChildren(this._currentIndex);
    };

    /**
     * make carousel spin right
     */
    Carousel3d.prototype.right = function () {
        this._currentIndex += 1;
        this._rotateChildren(this._currentIndex);
    };

    /**
     * Renderer for browsers supporting css transform including ie8
     * @type {{_initChildren: Function, _applyChildZIndex: Function, _rotateChild: Function}}
     */
    var rendererTransform = {
        _ieTransform: false,
        _rotateChildren: function (index) {
            var degree = -index * (360 / this._children.length);
            if (this._children) {
                $(this._children).each(function (index, child) {
                    this._rotateChild(child, index, degree);
                }.bind(this));
            }
        },
        _rotateChild: function (child, index, degree) {
            $(child).css('overflow', 'hidden');
            var baseScale = 1;
            var width = $(child).data('width');
            var height = $(child).data('height');
            var wrapperWidth = $(this._childrenWrapper).width();
            var wrapperHeight = $(this._childrenWrapper).height();
            if ((width / height) > this._aspectRatio) {
                baseScale = wrapperWidth / width;
            } else {
                baseScale = wrapperHeight / height;
            }

            var childDegree = ((360 / this._children.length) * index) + degree;
            $(child).animate({
                '_degree': childDegree
            }, {
                duration: 1000,
                step: function (now, tween) {
                    if (tween.prop === '_degree') {
                        var cos = Math.cos(Math.PI / 180 * now);
                        var dx = Math.sin(Math.PI / 180 * now) * wrapperWidth / 2;
                        var heightScale = baseScale * (cos + 1) / 2;
                        var widthScale = baseScale * Math.abs(cos) * heightScale;
                        if (this._ieTransform) {
                            $(tween.elem).css('filter', 'progid:DXImageTransform.Microsoft.Matrix(M11=' + widthScale + ', M12=0, M21=0, M22=' + heightScale + ', SizingMethod="auto expand")');
                            $(tween.elem).css('-ms-filter', 'progid:DXImageTransform.Microsoft.Matrix(M11=' + widthScale + ', M12=0, M21=0, M22=' + heightScale + ', SizingMethod="auto expand")');
                        } else {
                            $(tween.elem).css('transform-origin', '0px 0px');
                            $(tween.elem).css('transform', 'scale(' + widthScale + ', ' + heightScale + ')');
                        }

                        $(tween.elem).css('z-index', Math.floor((cos + 1) * 10));
                        $(tween.elem).css('top', (wrapperHeight - height * heightScale) / 2 + 'px');
                        $(tween.elem).css('left', ((wrapperWidth - width * widthScale) / 2 + dx) + 'px');
                    }
                }.bind(this)
            });
        }
    };

    /**
     * Renderer for browsers supporting css transform3d
     * @type {{_tz: number, _initChildren: Function, _applyChildZIndex: Function, _rotateChild: Function}}
     */
    var renderer3DTransform = {
        _tz: 0,
        _rotateChildren: function (index) {
            var wrapperWidth = $(this._childrenWrapper).width();
            this._tz =  (wrapperWidth / 2) / Math.tan(Math.PI / this._children.length);
            rendererTransform._rotateChildren.call(this, index);
        },
        _rotateChild: function (child, index, degree) {
            degree = degree ? degree : 0;

            //scale, margin
            var scale = 1;
            var width = $(child).width();
            var height = $(child).height();
            var wrapperWidth = $(this._childrenWrapper).width();
            var wrapperHeight = $(this._childrenWrapper).height();
            if ((width / height) > this._aspectRatio) {
                scale = wrapperWidth / width;
            } else {
                scale = wrapperHeight / height;
            }
            var scaledWidth = width * scale;
            var scaledHeight = height * scale;

            $(child).css('left', ((scaledWidth - width) / 2) + ((wrapperWidth - scaledWidth) / 2) + 'px');
            $(child).css('top', ((scaledHeight - height) / 2) + ((wrapperHeight - scaledHeight) / 2) + 'px');

            //rotation
            $(child).data('index', index);
            $(child).data('cssScale', scale);

            var childDegree = ((360 / this._children.length) * index) + degree;
            var transformText = 'scale(' + scale + ')';
            transformText += ' translateZ(' + -this._tz + 'px)';
            transformText += ' rotateY(' + childDegree + 'deg)';
            transformText += ' translateZ(' + this._tz + 'px)';
            $(child).css('transform', transformText);

            $(child).css('z-index', Math.floor((Math.cos(Math.PI / 180 * childDegree) + 1) * 10));
        }
    };

	/**
	 * Exposed to jquery.
	 * @returns {*}
	 */
	$.fn.carousel3d = function() {
		var self = this, opt = arguments[0], args = Array.prototype.slice.call(arguments,1), l = self.length, i = 0, ret;
		for(i; i < l; i += 1) {
			if (typeof opt === 'object' || typeof opt === 'undefined') {
				self[i].carousel3d =  new Carousel3d(self[i], opt);
			}
			else {
				ret = self[i].carousel3d[opt].apply(self[i].carousel3d, args);
			}
			if (ret !== undefined) {
				return ret;
			}
		}
		return self;
	};

	/**
	 * initialize on load
	 */
    $(function () {
        $('[data-carousel-3d]').carousel3d();
    });
    var windowLoaded = false;
    $(window).load(function () {
        windowLoaded = true;
    });

}));<|endoftext|>


<commit_before>/**
 * Copyright (C) 2015 tieba.baidu.com
 * schedule.js
 *
 * changelog
 * 2015-09-19[21:26:06]:revised
 *
 * @author yinyong02@baidu.com
 * @version 0.1.0
 * @since 0.1.0
 */
define([], function () {

    /**
     * 网络请求。
     *
     * @type {Object}
     */
    var Web = {
        request: function (url, cb) {
            $.ajax({
                url: url,
                timeout: 2e3,
                dataType: 'json'
            }).done(function (data) {
                if (200 === data.code && 1 === data.status) {
                    cb(null, data);
                } else {
                    cb(new Error('操作失败'));
                }
            }).fail(function (jqXhr, error, errText) {
                cb(new Error(errText));
            });
        }
    };

    /**
     * 一个会议室预订信息条目。
     *
     * @param {Object} metaData
     * @class
     */
    var Schedule = function (metaData) {
        var id;

        this.get = function (key) {
            return metaData[key];
        };

        this.getId = function () {
            return id || (this.get('操作').match(/\b\d+\b/) || [])[0];
        };
    };

    Schedule.prototype = {
        getDateTimeStr: function () {
            return [this.get('日期'), '(', this.get('星期'), ')', ' ', this.get('开始时间'), '~', this.get(
                '结束时间')].join('');
        },

        getLocationStr: function () {
            return [this.get('地域'), this.get('楼层'), ' ', this.get('会议室描述'), ' ', this.get('会议室名称')].join(
                '');
        },

        getStatus: function () {
            return this.get('当前状态');
        },
        isInPlan: function () {
            return '未开始' === this.getStatus();
        },
        hasCheckedOut: function () {
            return '已签出' === this.getStatus();
        },
        hasFinished: function () {
            return !!~this.getStatus().indexOf('已完成');
        },
        hasOperations: function () {
            return !(this.hasFinished() || this.hasCheckedOut() || this.hasReleased());
        },
        hasReleased: function () {
            return !!~this.getStatus().indexOf('已释放');
        },
        canCheckin: function () {
            return !!~['可签入'].indexOf(this.getStatus());
        },
        canTransfer: function () {
            return !!~['未开始', '可签入'].indexOf(this.getStatus());
        },
        canCancel: function () {
            return this.canTransfer();
        },
        canCheckout: function () {
            return !!~['已签入'].indexOf(this.getStatus());
        },
        checkin: function (cb) {
            Web.request('http://meeting.baidu.com/web/checkIn?scheduleId=' + this.getId(), cb);
        },
        cancel: function (cb) {
            Web.request('http://meeting.baidu.com/web/cancel?scheduleId=' + this.getId(), cb);
        },
        checkout: function (cb) {
            Web.request('http://meeting.baidu.com/web/checkOut?scheduleId=' + this.getId(), cb);
        },
        transfer: function (targetMan, cb) {
            Web.request('http://meeting.baidu.com/web/transferOrderMan?scheduleId=' + this.getId() +
                '&newOrderManEmail=' + encodeURIComponent(targetMan), cb);
        }
    }

    return Schedule;
});<commit_msg>cache id<commit_after>/**
 * Copyright (C) 2015 tieba.baidu.com
 * schedule.js
 *
 * changelog
 * 2015-09-19[21:26:06]:revised
 *
 * @author yinyong02@baidu.com
 * @version 0.1.0
 * @since 0.1.0
 */
define([], function () {

    /**
     * 网络请求。
     *
     * @type {Object}
     */
    var Web = {
        request: function (url, cb) {
            $.ajax({
                url: url,
                timeout: 2e3,
                dataType: 'json'
            }).done(function (data) {
                if (200 === data.code && 1 === data.status) {
                    cb(null, data);
                } else {
                    cb(new Error('操作失败'));
                }
            }).fail(function (jqXhr, error, errText) {
                cb(new Error(errText));
            });
        }
    };

    /**
     * 一个会议室预订信息条目。
     *
     * @param {Object} metaData
     * @class
     */
    var Schedule = function (metaData) {
        var id;

        this.get = function (key) {
            return metaData[key];
        };

        this.getId = function () {
            return id || (id = (this.get('操作').match(/\b\d+\b/) || [])[0]);
        };
    };

    Schedule.prototype = {
        getDateTimeStr: function () {
            return [this.get('日期'), '(', this.get('星期'), ')', ' ', this.get('开始时间'), '~', this.get(
                '结束时间')].join('');
        },

        getLocationStr: function () {
            return [this.get('地域'), this.get('楼层'), ' ', this.get('会议室描述'), ' ', this.get('会议室名称')].join(
                '');
        },

        getStatus: function () {
            return this.get('当前状态');
        },
        isInPlan: function () {
            return '未开始' === this.getStatus();
        },
        hasCheckedOut: function () {
            return '已签出' === this.getStatus();
        },
        hasFinished: function () {
            return !!~this.getStatus().indexOf('已完成');
        },
        hasOperations: function () {
            return !(this.hasFinished() || this.hasCheckedOut() || this.hasReleased());
        },
        hasReleased: function () {
            return !!~this.getStatus().indexOf('已释放');
        },
        canCheckin: function () {
            return !!~['可签入'].indexOf(this.getStatus());
        },
        canTransfer: function () {
            return !!~['未开始', '可签入'].indexOf(this.getStatus());
        },
        canCancel: function () {
            return this.canTransfer();
        },
        canCheckout: function () {
            return !!~['已签入'].indexOf(this.getStatus());
        },
        checkin: function (cb) {
            Web.request('http://meeting.baidu.com/web/checkIn?scheduleId=' + this.getId(), cb);
        },
        cancel: function (cb) {
            Web.request('http://meeting.baidu.com/web/cancel?scheduleId=' + this.getId(), cb);
        },
        checkout: function (cb) {
            Web.request('http://meeting.baidu.com/web/checkOut?scheduleId=' + this.getId(), cb);
        },
        transfer: function (targetMan, cb) {
            Web.request('http://meeting.baidu.com/web/transferOrderMan?scheduleId=' + this.getId() +
                '&newOrderManEmail=' + encodeURIComponent(targetMan), cb);
        }
    }

    return Schedule;
});<|endoftext|>


<commit_before>var config = require('../config');
var request = require('request');
var fs = require('fs');

var telegramAPI = {};

telegramAPI.setupWebhook = function(token, url, callback){
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_SETUP_WEBHOOK.replace(":url", url);
  request(requestUrl, {}, function (err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null);
    } else {
      return callback(new Error("Unable to setup webhook. Code " + res.statusCode));
    }
  });
};

telegramAPI.postMessage = function(token, chatId, message, callback) {
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_MESSAGE;
  var formData = {
    chat_id: chatId,
    text: message
  };
  request.post({url:requestUrl, formData: formData}, function(err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null, res, body);
    } else {
      return callback(new Error("Unable to post message. Code " + res.statusCode));
    }
  });
};

telegramAPI.postImage = function(token, imagePath, chatId, callback) {
  fs.access(imagePath, fs.F_OK, function(err) {
    if (err) {
      callback(err);
    } else {
      var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_IMAGE;
      var formData = {
        chat_id: chatId,
        photo: fs.createReadStream(imagePath)
      };
      request.post({url:requestUrl, formData: formData}, function(err, res, body) {
        fs.unlink(imagePath, function(error) {
          if (error)
            return callback(error);
        });
        if (err) {
          return callback(err);
        } else if (res.statusCode == 200) {
          return callback(null, res, body);
        } else {
          return callback(new Error("Unable to post image. Code " + res.statusCode));
        }
      });
    }
  });
};

telegramAPI.postDocument = function(token, documentPath, chatId, callback) {
  fs.access(documentPath, fs.F_OK, function(err) {
    if (err) {
      callback(err);
    } else {
      var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_DOCUMENT;
      var formData = {
        chat_id: chatId,
        document: fs.createReadStream(documentPath)
      };
      request.post({url:requestUrl, formData: formData}, function(err, res, body) {
        fs.unlink(documentPath, function(error) {
          if (error)
            throw error;
        });
        if (err) {
          return callback(err);
        } else if (res.statusCode == 200) {
          return callback(null, res, body);
        } else {
          return callback(new Error("Unable to post document. Code " + res.statusCode));
        }
      });
    }
  });
};

telegramAPI.answerQueryWithMedia = function(token, queryId, mediaURLs, callback) {
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_ANSWER_QUERY;
  var results = mediaURLs.map(function(url) {
    var fileExtension = url.split('.').pop();
    var fileName = url.split('/').pop().split('.')[0];
    var thumbnailUrl = url.replace('.' + fileExtension, 's.jpg');
    var result = {};
    switch (fileExtension) {
      case 'webm':
      result.type = 'video';
      result.mime_type = 'video/mp4';
      result.video_url = url;
      break;
      case 'gif':
      result.type = 'gif';
      result.gif_url = url;
      break;
      case 'jpeg':
      case 'png':
      case 'jpg':
      result.type = 'photo';
      result.photo_url = url;
      break;
      default:
      result.type = 'article';
      result.url = url;
    }
    // Common properties
    result.id = fileName;
    result.title = fileName;
    result.message_text = url;
    result.thumb_url = thumbnailUrl;
    return result;
  });
  request.post(requestUrl, {form:{inline_query_id:queryId, cache_time:10, results: JSON.stringify(results)}}, function(err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null, res, body);
    } else {
      return callback(new Error(body.error_code));
    }
  });
};

module.exports = telegramAPI;
<commit_msg>Removed message text from results with content.<commit_after>var config = require('../config');
var request = require('request');
var fs = require('fs');

var telegramAPI = {};

telegramAPI.setupWebhook = function(token, url, callback){
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_SETUP_WEBHOOK.replace(":url", url);
  request(requestUrl, {}, function (err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null);
    } else {
      return callback(new Error("Unable to setup webhook. Code " + res.statusCode));
    }
  });
};

telegramAPI.postMessage = function(token, chatId, message, callback) {
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_MESSAGE;
  var formData = {
    chat_id: chatId,
    text: message
  };
  request.post({url:requestUrl, formData: formData}, function(err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null, res, body);
    } else {
      return callback(new Error("Unable to post message. Code " + res.statusCode));
    }
  });
};

telegramAPI.postImage = function(token, imagePath, chatId, callback) {
  fs.access(imagePath, fs.F_OK, function(err) {
    if (err) {
      callback(err);
    } else {
      var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_IMAGE;
      var formData = {
        chat_id: chatId,
        photo: fs.createReadStream(imagePath)
      };
      request.post({url:requestUrl, formData: formData}, function(err, res, body) {
        fs.unlink(imagePath, function(error) {
          if (error)
            return callback(error);
        });
        if (err) {
          return callback(err);
        } else if (res.statusCode == 200) {
          return callback(null, res, body);
        } else {
          return callback(new Error("Unable to post image. Code " + res.statusCode));
        }
      });
    }
  });
};

telegramAPI.postDocument = function(token, documentPath, chatId, callback) {
  fs.access(documentPath, fs.F_OK, function(err) {
    if (err) {
      callback(err);
    } else {
      var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_DOCUMENT;
      var formData = {
        chat_id: chatId,
        document: fs.createReadStream(documentPath)
      };
      request.post({url:requestUrl, formData: formData}, function(err, res, body) {
        fs.unlink(documentPath, function(error) {
          if (error)
            throw error;
        });
        if (err) {
          return callback(err);
        } else if (res.statusCode == 200) {
          return callback(null, res, body);
        } else {
          return callback(new Error("Unable to post document. Code " + res.statusCode));
        }
      });
    }
  });
};

telegramAPI.answerQueryWithMedia = function(token, queryId, mediaURLs, callback) {
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_ANSWER_QUERY;
  var results = mediaURLs.map(function(url) {
    var fileExtension = url.split('.').pop();
    var fileName = url.split('/').pop().split('.')[0];
    var thumbnailUrl = url.replace('.' + fileExtension, 's.jpg');
    var result = {};
    switch (fileExtension) {
      case 'webm':
      result.type = 'video';
      result.mime_type = 'video/mp4';
      result.video_url = url;
      break;
      case 'gif':
      result.type = 'gif';
      result.gif_url = url;
      break;
      case 'jpeg':
      case 'png':
      case 'jpg':
      result.type = 'photo';
      result.photo_url = url;
      break;
      default:
      result.type = 'article';
      result.url = url;
      result.message_text = url;
    }
    // Common properties
    result.id = fileName;
    result.title = fileName;
    result.thumb_url = thumbnailUrl;
    return result;
  });
  request.post(requestUrl, {form:{inline_query_id:queryId, cache_time:10, results: JSON.stringify(results)}}, function(err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null, res, body);
    } else {
      return callback(new Error(body.error_code));
    }
  });
};

module.exports = telegramAPI;
<|endoftext|>


<commit_before>/* global blueimp */
'use strict';
$(document).ready(function() {
  var NBBGallery = function() {

    if (!this instanceof NBBGallery) {
      return new NBBGallery();
    }

    this.up = false;
    this.lastTop = 0;
    this.sel = '[component="post/content"] .img-markdown';  // image selector
    this._gallery = null;                                   // lightbox instance
    this.options = {                                        // lightbox options
      index: 0,
      clearSlides: true
    };                                                      // lightbox options;

    this.insertGalleryElements();
    var self = this;
    document.addEventListener('scroll', function() {
      self.up = (window.pageYOffset < self.lastTop);
      self.lastTop = window.pageYOffset;
    });
    $(window).on('action:topic.loaded', function() {
      var imgs = document.querySelectorAll(self.sel);
      if (imgs.length > 0) {
        self._gallery = null;
        self.addSlides(imgs, false);
      }
    });
    $(window).on('action:posts.loaded', function(evt, posts) {
      var imgs = document.querySelectorAll(self.sel +
                                           ':not([data-gallery-idx])');
      if (imgs.length > 0) {
        self.addSlides(imgs, self.up);
      }
    });
  };
  NBBGallery.prototype.reindexSlideElements = function() {
    var imgs = document.querySelectorAll(this.sel);
    for (var i = 0; i <= imgs.length - 1; i++) {
      imgs.item(i).dataset.galleryIdx = i;
    }
  };
  NBBGallery.prototype.Slide = function(src) {
    return {
      href: src,
      thumbnail: src
    };
  };
  NBBGallery.prototype.addSlides = function(imgs, up) {
    var img;
    var self = this;
    var slides = [];
    for (var i = 0; i <= imgs.length - 1; i++) {
      img = imgs.item(i);
      slides.push(this.Slide(img.src));
      img.addEventListener('click', function(evt) {
        var idx = parseInt(this.dataset.galleryIdx, 10);
        evt.preventDefault();
        self._gallery.initialize();
        self._gallery.slide(idx, 0);
      });
    }
    if (self._gallery) {
      try {
        self._gallery.add(slides);
      } catch (e) {
        // for some reason the internal slides array can't be 'pushed'
        // from time to time - just don't know which ones, yet.
        console.warn('[plugin:gallery] Non critical error in addSlides()');
      }
    } else {
      self._gallery = blueimp.Gallery(slides, self.options);
    }
    this.reindexSlideElements();
  };
  NBBGallery.prototype.insertGalleryElements = function() {
    // replace with templates.js ?
    var basicGallery = '
' +
                          '

' +
                          '

' +
                          '‹' +
                          '›' +
                          '×' +
                          '' +
                          '

' +
                        '
';
    document.body.insertAdjacentHTML('beforeend', basicGallery);
  };
  window.NBBGallery = new NBBGallery();
});
<commit_msg>Don't open overlay on init<commit_after>/* global blueimp */
'use strict';
$(document).ready(function() {
  var NBBGallery = function() {

    if (!this instanceof NBBGallery) {
      return new NBBGallery();
    }

    this.up = false;
    this.lastTop = 0;
    this.sel = '[component="post/content"] .img-markdown';  // image selector
    this._gallery = null;                                   // lightbox instance
    this.options = {                                        // lightbox options
      index: 0,
      clearSlides: true
    };                                                      // lightbox options;

    this.insertGalleryElements();
    var self = this;
    document.addEventListener('scroll', function() {
      self.up = (window.pageYOffset < self.lastTop);
      self.lastTop = window.pageYOffset;
    });
    $(window).on('action:topic.loaded', function() {
      var imgs = document.querySelectorAll(self.sel);
      if (imgs.length > 0) {
        self._gallery = null;
        self.addSlides(imgs, false);
      }
    });
    $(window).on('action:posts.loaded', function(evt, posts) {
      var imgs = document.querySelectorAll(self.sel +
                                           ':not([data-gallery-idx])');
      if (imgs.length > 0) {
        self.addSlides(imgs, self.up);
      }
    });
  };
  NBBGallery.prototype.reindexSlideElements = function() {
    var imgs = document.querySelectorAll(this.sel);
    for (var i = 0; i <= imgs.length - 1; i++) {
      imgs.item(i).dataset.galleryIdx = i;
    }
  };
  NBBGallery.prototype.Slide = function(src) {
    return {
      href: src,
      thumbnail: src
    };
  };
  NBBGallery.prototype.addSlides = function(imgs, up) {
    var img;
    var self = this;
    var slides = [];
    for (var i = 0; i <= imgs.length - 1; i++) {
      img = imgs.item(i);
      slides.push(this.Slide(img.src));
      img.addEventListener('click', function(evt) {
        var idx = parseInt(this.dataset.galleryIdx, 10);
        evt.preventDefault();
        self._gallery.initialize();
        self._gallery.slide(idx, 0);
      });
    }
    if (self._gallery) {
      try {
        self._gallery.add(slides);
      } catch (e) {
        // for some reason the internal slides array can't be 'pushed'
        // from time to time - just don't know which ones, yet.
        console.warn('[plugin:gallery] Non critical error in addSlides()');
      }
    } else {
      self._gallery = blueimp.Gallery(slides, self.options, true);
    }
    this.reindexSlideElements();
  };
  NBBGallery.prototype.insertGalleryElements = function() {
    // replace with templates.js ?
    var basicGallery = '
' +
                          '

' +
                          '

' +
                          '‹' +
                          '›' +
                          '×' +
                          '' +
                          '

' +
                        '
';
    document.body.insertAdjacentHTML('beforeend', basicGallery);
  };
  window.NBBGallery = new NBBGallery();
});
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2015 tieba.baidu.com
 * schedule.js
 *
 * changelog
 * 2015-09-19[21:26:06]:revised
 *
 * @author yinyong02@baidu.com
 * @version 0.1.0
 * @since 0.1.0
 */
define([], function () {

    /**
     * 网络请求。
     *
     * @type {Object}
     */
    var Web = {
        request: function (url, cb) {
            $.ajax({
                url: url,
                timeout: 2e3,
                dataType: 'json'
            }).done(function (data) {
                if (200 === data.code && 1 === data.status) {
                    cb(null, data);
                } else {
                    cb(new Error('操作失败'));
                }
            }).fail(function (jqXhr, error, errText) {
                cb(new Error(errText));
            });
        }
    };

    /**
     * 一个会议室预订信息条目。
     *
     * @param {Object} metaData
     * @class
     */
    var Schedule = function (metaData) {
        var id;

        this.get = function (key) {
            return metaData[key];
        };

        this.getId = function () {
            return id || (this.get('操作').match(/\b\d+\b/) || [])[0];
        };
    };

    Schedule.prototype = {
        getDateTimeStr: function () {
            return [this.get('日期'), '(', this.get('星期'), ')', ' ', this.get('开始时间'), '~', this.get(
                '结束时间')].join('');
        },

        getLocationStr: function () {
            return [this.get('地域'), this.get('楼层'), ' ', this.get('会议室描述'), ' ', this.get('会议室名称')].join(
                '');
        },

        getStatus: function () {
            return this.get('当前状态');
        },
        isInPlan: function () {
            return '未开始' === this.getStatus();
        },
        hasCheckedOut: function () {
            return '已签出' === this.getStatus();
        },
        hasFinished: function () {
            return !!~this.getStatus().indexOf('已完成');
        },
        hasOperations: function () {
            return !(this.hasFinished() || this.hasCheckedOut());
        },
        canCheckin: function () {
            return !!~['可签入'].indexOf(this.getStatus());
        },
        canTransfer: function () {
            return !!~['未开始', '可签入'].indexOf(this.getStatus());
        },
        canCancel: function () {
            return this.canTransfer();
        },
        canCheckout: function () {
            return !!~['已签入'].indexOf(this.getStatus());
        },
        checkin: function (cb) {
            Web.request('http://meeting.baidu.com/web/checkIn?scheduleId=' + this.getId(), cb);
        },
        cancel: function (cb) {
            Web.request('http://meeting.baidu.com/web/cancel?scheduleId=' + this.getId(), cb);
        },
        checkout: function (cb) {
            Web.request('http://meeting.baidu.com/web/checkOut?scheduleId=' + this.getId(), cb);
        },
        transder: function (targetMan, cb) {
            Web.request('http://meeting.baidu.com/web/transferOrderMan?scheduleId=' + this.getId() +
                '&newOrderManEmail=' + encodeURIComponent(targetMan), cb);
        }
    }

    return Schedule;
});<commit_msg>fixed transfer<commit_after>/**
 * Copyright (C) 2015 tieba.baidu.com
 * schedule.js
 *
 * changelog
 * 2015-09-19[21:26:06]:revised
 *
 * @author yinyong02@baidu.com
 * @version 0.1.0
 * @since 0.1.0
 */
define([], function () {

    /**
     * 网络请求。
     *
     * @type {Object}
     */
    var Web = {
        request: function (url, cb) {
            $.ajax({
                url: url,
                timeout: 2e3,
                dataType: 'json'
            }).done(function (data) {
                if (200 === data.code && 1 === data.status) {
                    cb(null, data);
                } else {
                    cb(new Error('操作失败'));
                }
            }).fail(function (jqXhr, error, errText) {
                cb(new Error(errText));
            });
        }
    };

    /**
     * 一个会议室预订信息条目。
     *
     * @param {Object} metaData
     * @class
     */
    var Schedule = function (metaData) {
        var id;

        this.get = function (key) {
            return metaData[key];
        };

        this.getId = function () {
            return id || (this.get('操作').match(/\b\d+\b/) || [])[0];
        };
    };

    Schedule.prototype = {
        getDateTimeStr: function () {
            return [this.get('日期'), '(', this.get('星期'), ')', ' ', this.get('开始时间'), '~', this.get(
                '结束时间')].join('');
        },

        getLocationStr: function () {
            return [this.get('地域'), this.get('楼层'), ' ', this.get('会议室描述'), ' ', this.get('会议室名称')].join(
                '');
        },

        getStatus: function () {
            return this.get('当前状态');
        },
        isInPlan: function () {
            return '未开始' === this.getStatus();
        },
        hasCheckedOut: function () {
            return '已签出' === this.getStatus();
        },
        hasFinished: function () {
            return !!~this.getStatus().indexOf('已完成');
        },
        hasOperations: function () {
            return !(this.hasFinished() || this.hasCheckedOut());
        },
        canCheckin: function () {
            return !!~['可签入'].indexOf(this.getStatus());
        },
        canTransfer: function () {
            return !!~['未开始', '可签入'].indexOf(this.getStatus());
        },
        canCancel: function () {
            return this.canTransfer();
        },
        canCheckout: function () {
            return !!~['已签入'].indexOf(this.getStatus());
        },
        checkin: function (cb) {
            Web.request('http://meeting.baidu.com/web/checkIn?scheduleId=' + this.getId(), cb);
        },
        cancel: function (cb) {
            Web.request('http://meeting.baidu.com/web/cancel?scheduleId=' + this.getId(), cb);
        },
        checkout: function (cb) {
            Web.request('http://meeting.baidu.com/web/checkOut?scheduleId=' + this.getId(), cb);
        },
        transfer: function (targetMan, cb) {
            Web.request('http://meeting.baidu.com/web/transferOrderMan?scheduleId=' + this.getId() +
                '&newOrderManEmail=' + encodeURIComponent(targetMan), cb);
        }
    }

    return Schedule;
});<|endoftext|>


<commit_before>/*
CHECKLIST
_ Mock Companies API return (C&P from actual results)
_ Mock Posts API return (C&P from actual results)

*/

'use strict'

// ex structure
function getArrayOfStrings() {
  return ['check', 'it', 'out'];
}

// returns a generic response object
// always the same, no matter what the input is
function getGenericResponse(context) {
  return {
    "userInput":"verizon",
    "data":{
      "name":"Verizon",
      "contactMethods":{
        "phone":"888-553-1555",
        "email":"",
        "twitter":"@VerizonSupport",
        "web":"http://www22.verizon.com/content/contactus/",
        "chat":"http://www22.verizon.com/content/contactus/",
        "facebook":""
      },
      "posts":[
        {
          "title":"How do I switch number to new phone?",
          "urlId":"A4u"
        },
        {
          "title":"How do I file an insurance claim?",
          "urlId":"7uV"
        },
        {
          "title":"How do I replace my phone?",
          "urlId":"7e1"
        },
        {
          "title":"I want to download backup assistant for Verizon",
          "urlId":"690"
        },
        {
          "title":"How do I start with Verizon Prepaid?",
          "urlId":"Li_"
        }
      ],
      "otherCompanies":[
        "Verizon Business",
        "Verizon DSL",
        "Verizon Smart Rewards",
        "Verizon Wireless Prepay",
        "Verizon Web Support",
        "Verizon Wireless Financial",
        "Verizon Billing",
        "Verizon Repair",
        "Verizon Wireless",
        "Verizon FiOS",
        "Verizon Land Line",
        "Verizon Center for Customers with Disabilities"
      ]
    },
    "type":"standard",
    "context": context
  }
}

function getNormalizedRequestIgnore() {
  return {
    reqType: 'ignore',
    userInput: 'blah',
    context: {}
  };
}
module.exports = {
  getArrayOfStrings: getArrayOfStrings,
  getNormalizedRequestIgnore: getNormalizedRequestIgnore
}
<commit_msg>debug<commit_after>/*
CHECKLIST
_ Mock Companies API return (C&P from actual results)
_ Mock Posts API return (C&P from actual results)

*/

'use strict'

// ex structure
function getArrayOfStrings() {
  return ['check', 'it', 'out'];
}

// returns a generic response object
// always the same, no matter what the input is
function getGenericResponse(context) {
  return {
    "userInput":"verizon",
    "data":{
      "name":"Verizon",
      "contactMethods":{
        "phone":"888-553-2555",
        "email":"",
        "twitter":"@VerizonSupport",
        "web":"http://www22.verizon.com/content/contactus/",
        "chat":"http://www22.verizon.com/content/contactus/",
        "facebook":""
      },
      "posts":[
        {
          "title":"How do I switch number to new phone?",
          "urlId":"A4u"
        },
        {
          "title":"How do I file an insurance claim?",
          "urlId":"7uV"
        },
        {
          "title":"How do I replace my phone?",
          "urlId":"7e1"
        },
        {
          "title":"I want to download backup assistant for Verizon",
          "urlId":"690"
        },
        {
          "title":"How do I start with Verizon Prepaid?",
          "urlId":"Li_"
        }
      ],
      "otherCompanies":[
        "Verizon Business",
        "Verizon DSL",
        "Verizon Smart Rewards",
        "Verizon Wireless Prepay",
        "Verizon Web Support",
        "Verizon Wireless Financial",
        "Verizon Billing",
        "Verizon Repair",
        "Verizon Wireless",
        "Verizon FiOS",
        "Verizon Land Line",
        "Verizon Center for Customers with Disabilities"
      ]
    },
    "type":"standard",
    "context": context
  }
}

function getNormalizedRequestIgnore() {
  return {
    reqType: 'ignore',
    userInput: 'blah',
    context: {}
  };
}
module.exports = {
  getArrayOfStrings: getArrayOfStrings,
  getNormalizedRequestIgnore: getNormalizedRequestIgnore
}
<|endoftext|>


<commit_before>(function (angular, undefined) {
	'use strict'

	angular.module('pokeApp', ['ui.router'])
	.config(function ($stateProvider, $urlRouterProvider) {

		$urlRouterProvider.otherwise('/index')
		$stateProvider.state('home', {
			resolve: {
				getPokemons: function($http){
		           var url = 'http://pokeapi.co/';

		           return {
		           		// GET rattata
			           	rattata: function () {
			           		return $http({
			           			method: 'GET',
			           			url: url + 'api/v1/pokemon/19/'
			           		})
			           	},

			           	// GET charmander
			           	charmander: function () {
			           		return $http({
			           			method: 'GET',
			           			url: url + 'api/v1/pokemon/4/'
			           		})
			           	},

			           	// GET wartortle
			           	wartortle: function () {
			           		return $http({
			           			method: 'GET',
			           			url: url + 'api/v1/pokemon/8/'
			           		})
			           	},
		           }
		        }
			},
			controller: function ($scope, getPokemons) {
				getPokemons.rattata().then(function (data) {
					console.log(data)
					$scope.rattatas = data;
				}, function (error) {
					console.log(error)
				});

				getPokemons.charmander().then(function (data) {
					console.log(data)
					$scope.charmanders = data;
				}, function (error) {
					console.log(error)
				});

				getPokemons.wartortle().then(function (data) {
					console.log(data)
					$scope.wartortles = data;
				}, function (error) {
					console.log(error)
				});
			},
			url: '/index',
			templateUrl: '/views/poke.tpl.html'
		})
	})

})(angular);<commit_msg>update poke-app<commit_after>(function (angular, undefined) {
	'use strict'

	angular.module('pokeApp', ['ui.router'])
	.config(function ($stateProvider, $urlRouterProvider) {
		$urlRouterProvider.otherwise('/index')
		$stateProvider.state('home', {
			resolve: {
				getPokemons: 'pokeFactory'
			},
			controller: 'pokeController',
			url: '/index',
			templateUrl: '/views/poke.tpl.html'
		})
	})
})(angular);<|endoftext|>


<commit_before>/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you 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.
 */

/* global $, Config, opensocial */

/* Include this file in any html page to secure the whole page against non-MyBerkeley Participants. */

require(["jquery","sakai/sakai.api.core","myb/myb.api.core"], function($, sakai, myb) {

    sakai_global.mybsecurepage = function() {

        /**
         * Checks if the user is logged in
         * @return true if the user is logged in, false otherwise
         */
        var isLoggedIn = function(){
            var person = sakai.data.me;
            var uuid = person.user.userid;
            return !(!uuid || person.user.anon);
        };

        /**
        * Function that can be called by pages that don't have the permission to show the content
        * they should be showing because the user in not a CalCentral participant
        */
        var sendToNotAMyBerkeleyParticipantPage = function() {
            document.location = "/";
            return false;
        };

        var doInit = function() {

          //HACK: You can disable redirection to 'not-a-participant' page by setting the global variable allowRedirectToParticipantPage = false
          var useRedirect = true;
          if (typeof(myb.api.security.allowRedirectToParticipantPage) !== 'undefined') {
            useRedirect = myb.api.security.allowRedirectToParticipantPage;
          }

          if (!isLoggedIn()) {
            if ( useRedirect ) {
              sakai.api.Security.sendToLogin();
            }
            return;
          }

          // If the user is a member of Berkeley's College of Environmental Design, but not a participant of CalCentral project,
          // redirect him to the participation explanation page
          if (!myb.api.security.isMyBerkeleyParticipant() && useRedirect) {
            sendToNotAMyBerkeleyParticipantPage();
          }

        };

      doInit();
    };

    sakai.api.Widgets.Container.registerForLoad("mybsecurepage");

});
<commit_msg>MYB-914 Allow anonymous cowards to see stuff in CalCentral<commit_after>/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you 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.
 */

/* global $, Config, opensocial */

/* Include this file in any html page to secure the whole page against non-MyBerkeley Participants. */

require(["jquery","sakai/sakai.api.core","myb/myb.api.core"], function($, sakai, myb) {

    sakai_global.mybsecurepage = function() {

        /**
         * Checks if the user is logged in
         * @return true if the user is logged in, false otherwise
         */
        var isLoggedIn = function(){
            var person = sakai.data.me;
            var uuid = person.user.userid;
            return !(!uuid || person.user.anon);
        };

        /**
        * Function that can be called by pages that don't have the permission to show the content
        * they should be showing because the user in not a CalCentral participant
        */
        var sendToNotAMyBerkeleyParticipantPage = function() {
            document.location = "/";
            return false;
        };

        var doInit = function() {

          //HACK: You can disable redirection to 'not-a-participant' page by setting the global variable allowRedirectToParticipantPage = false
          var useRedirect = true;
          if (typeof(myb.api.security.allowRedirectToParticipantPage) !== 'undefined') {
            useRedirect = myb.api.security.allowRedirectToParticipantPage;
          }

          // If the user is a member of Berkeley's College of Environmental Design, but not a participant of CalCentral project,
          // redirect him to the participation explanation page
          if (isLoggedIn() && !myb.api.security.isMyBerkeleyParticipant() && useRedirect) {
            sendToNotAMyBerkeleyParticipantPage();
          }

        };

      doInit();
    };

    sakai.api.Widgets.Container.registerForLoad("mybsecurepage");

});
<|endoftext|>