<commit_before>import Ember from 'ember';
import moment from 'moment';
import { validator, buildValidations } from 'ember-cp-validations';
import ValidationErrorDisplay from 'ilios/mixins/validation-error-display';
import { task, timeout } from 'ember-concurrency';

const { Component, inject, computed, RSVP, isPresent, isEmpty } = Ember;
const { service } = inject;
const { Promise, map, filter, hash } = RSVP;

const Validations = buildValidations({
  room: [
    validator('presence', {
      presence: true
    }),
    validator('length', {
      max: 255
    }),
  ],
  numberOfWeeks: {
    dependentKeys: ['makeRecurring'],
    disabled(){
      return !this.get('model.makeRecurring');
    },
    validators: [
      validator('presence', {
        presence: true
      }),
      validator('number', {
        allowString: true,
        integer: true,
        gt: 0,
      }),
    ]
  },
  durationHours: [
    validator('number', {
      allowString: true,
      integer: true,
      gte: 0
    })
  ],
  durationMinutes: [
    validator('number', {
      allowString: true,
      integer: true,
      gte: 0,
      lte: 60
    })
  ],
  learnerGroups: {
    dependentKeys: ['smallGroupMode'],
    disabled(){
      return !this.get('model.smallGroupMode');
    },
    validators: [
      validator('length', {
        min: 1,
        messageKey: 'general.smallGroupMessage'
      })
    ]
  },

});

export default Component.extend(ValidationErrorDisplay, Validations, {
  currentUser: service(),
  didReceiveAttrs(){
    this._super(...arguments);
    const offering = this.get('offering');
    if (isPresent(offering)) {
      this.get('loadAttrsFromOffering').perform(offering);
    } else {
      this.loadDefaultAttrs();
    }
  },
  classNames: ['offering-form'],
  startDate: null,
  endDate: null,
  room: 'TBD',
  cohorts: null,
  learnerGroups: null,
  showRoom: false,
  showMakeRecurring: false,
  showInstructors: false,
  makeRecurring: false,
  recurringDays: null,
  numberOfWeeks: 1,
  instructors: null,
  instructorGroups: null,
  courseStartDate: null,
  courseEndDate: null,
  smallGroupMode: false,
  offeringsToSave: 0,
  savedOfferings: 0,
  recurringDayOptions: [
    {day: '0', t: 'general.sunday'},
    {day: '1', t: 'general.monday'},
    {day: '2', t: 'general.tuesday'},
    {day: '3', t: 'general.wednesday'},
    {day: '4', t: 'general.thursday'},
    {day: '5', t: 'general.friday'},
    {day: '6', t: 'general.saturday'},
  ],
  associatedSchools: computed('cohorts.[]', function(){
    return new Promise(resolve => {
      const cohorts = this.get('cohorts');
      if (isEmpty(cohorts)) {
        resolve([]);
      } else {
        map(cohorts.toArray(), cohort => {
          return cohort.get('school');
        }).then(cohortSchools => {
          let schools = [];
          schools.pushObjects(cohortSchools);
          resolve(schools.uniq().toArray());
        });
      }
    });
  }),
  availableInstructorGroups: computed('associatedSchools.[]', function(){
    return new Promise(resolve => {
      this.get('associatedSchools').then(associatedSchools => {
        map(associatedSchools, school => {
          return school.get('instructorGroups');
        }).then(allInstructorGropus => {
          let flat = allInstructorGropus.reduce((flattened, obj) => {
            return flattened.pushObjects(obj.toArray());
          }, []);

          resolve(flat);
        });
      });
    });
  }),
  defaultStartDate: computed('courseStartDate', 'courseEndDate', function(){
    const today = moment();
    const courseStartDate = this.get('courseStartDate');
    const courseEndDate = this.get('courseEndDate');
    let defaultStartDate = today.clone();
    if (isPresent(courseStartDate) && today.isBefore(courseStartDate)) {
      defaultStartDate = moment(courseStartDate);
    }
    if (isPresent(courseEndDate) && today.isAfter(courseEndDate)) {
      defaultStartDate = moment(courseEndDate);
    }

    return defaultStartDate.toDate();
  }),
  durationHours: computed('startDate', 'endDate', function(){
    const startDate = this.get('startDate');
    const endDate = this.get('endDate');

    if (isEmpty(startDate) || isEmpty(endDate)) {
      return 1;
    }
    let mStart = moment(startDate);
    let mEnd = moment(endDate);
    let diffInHours = mEnd.diff(mStart, 'hours');

    return diffInHours;
  }),
  durationMinutes: computed('startDate', 'endDate', function(){
    const startDate = this.get('startDate');
    const endDate = this.get('endDate');

    if (isEmpty(startDate) || isEmpty(endDate)) {
      return 0;
    }
    let mStart = moment(startDate);
    let mEnd = moment(endDate);

    const endHour = mEnd.hour();
    const endMinute = mEnd.minute();

    mStart.hour(endHour);
    const startMinute = mStart.minute();

    let diff = 0;
    if (endMinute > startMinute) {
      diff = endMinute - startMinute;
    } else if (endMinute < startMinute) {
      diff = (60 - startMinute) + endMinute;
    }
    return diff;
  }),
  makeRecurringOfferingObjects: task(function * () {
    const {
      startDate,
      endDate,
      room,
      instructorGroups,
      instructors,
      numberOfWeeks,
      recurringDays
    } = this.getProperties('startDate', 'endDate', 'room', 'instructorGroups', 'instructors', 'numberOfWeeks', 'recurringDays');
    const makeRecurring = this.get('makeRecurring');
    const learnerGroups = yield this.get('lowestLearnerGroupLeaves');
    let offerings = [];
    offerings.push({startDate, endDate, room, learnerGroups, instructorGroups, instructors});
    if (!makeRecurring) {
      return offerings;
    }

    const userPickedDay = moment(startDate).day();
    //convert strings to numbers use parseFloat because parseInt takes a second
    //argument and gets thrown off by map sending that argument as the counter
    const recurringDayInts = recurringDays.map(parseFloat).sort();

    // Add offerings for the rest of first week
    //only days AFTER the initial day are considered
    recurringDayInts.forEach(day => {
      if (day > userPickedDay) {
        let obj = {room, learnerGroups, instructorGroups, instructors};
        obj.startDate = moment(startDate).day(day).toDate();
        obj.endDate = moment(endDate).day(day).toDate();

        offerings.push(obj);
      }
    });
    recurringDayInts.pushObject(userPickedDay);
    recurringDayInts.sort();

    for (let i = 1; i < numberOfWeeks; i++) {
      recurringDayInts.forEach(day => {
        let obj = {room, learnerGroups, instructorGroups, instructors};
        obj.startDate = moment(startDate).day(day).add(i, 'weeks').toDate();
        obj.endDate = moment(endDate).day(day).add(i, 'weeks').toDate();

        offerings.push(obj);
      });
    }

    return offerings;
  }),
  makeSmallGroupOfferingObjects: task(function * (offerings){
    const smallGroupMode = this.get('smallGroupMode');
    if (!smallGroupMode) {
      return offerings;
    }

    let smallGroupOfferings = [];

    for (let i = 0; i < offerings.length; i++) {
      let {startDate, endDate, room, learnerGroups} = offerings[i];
      for (let j = 0; j < learnerGroups.length; j++) {
        let learnerGroup = learnerGroups[j];
        let defaultLocation = learnerGroup.get('location');
        if (isPresent(defaultLocation)) {
          room = defaultLocation;
        }
        let instructors = yield learnerGroup.get('instructors');
        let instructorGroups = yield learnerGroup.get('instructorGroups');
        let offering = {startDate, endDate, room, instructorGroups, instructors};
        offering.learnerGroups = [learnerGroup];

        smallGroupOfferings.pushObject(offering);
      }
    }

    return smallGroupOfferings;
  }),

  loadDefaultAttrs(){
    let startDate = moment(this.get('defaultStartDate')).hour(8).minute(0).second(0).toDate();
    let endDate = moment(this.get('defaultStartDate')).hour(9).minute(0).second(0).toDate();
    const room = 'TBD';
    const learnerGroups = [];
    const recurringDays = [];
    const instructors = [];
    const instructorGroups = [];

    this.setProperties({startDate, endDate, room, learnerGroups, recurringDays, instructors, instructorGroups});
  },

  loadAttrsFromOffering: task(function * (offering) {
    const startDate = offering.get('startDate');
    const endDate = offering.get('endDate');
    const room = offering.get('room');
    const recurringDays = [];
    let obj = yield hash({
      learnerGroups : offering.get('learnerGroups'),
      instructors : offering.get('instructors'),
      instructorGroups : offering.get('instructorGroups'),
    });
    const learnerGroups = obj.learnerGroups.toArray();
    const instructors = obj.instructors.toArray();
    const instructorGroups = obj.instructorGroups.toArray();

    this.setProperties({startDate, endDate, room, learnerGroups, recurringDays, instructors, instructorGroups});
  }),
  saveOffering: task(function * () {
    this.set('offeringsToSave', 0);
    this.set('savedOfferings', 0);
    this.send('addErrorDisplaysFor', ['room', 'numberOfWeeks', 'durationHours', 'durationMinutes', 'learnerGroups']);
    yield timeout(10);
    let {validations} = yield this.validate();

    if (validations.get('isInvalid')) {
      return;
    }
    let offerings = yield this.get('makeRecurringOfferingObjects').perform();
    offerings = yield this.get('makeSmallGroupOfferingObjects').perform(offerings);

    this.set('offeringsToSave', offerings.length);
    //save offerings in sets of 5
    let parts;
    while (offerings.length > 0){
      parts = offerings.splice(0, 5);
      yield map(parts, ({startDate, endDate, room, learnerGroups, instructorGroups, instructors}) => {
        return this.get('save')(startDate, endDate, room, learnerGroups, instructorGroups, instructors);
      });
      this.set('savedOfferings', this.get('savedOfferings') + parts.length);
    }
    this.send('clearErrorDisplay');
    this.get('close')();

  }),
  validateThenSaveOffering: task(function * () {
    this.send('addErrorDisplaysFor', ['room', 'numberOfWeeks', 'durationHours', 'durationMinutes', 'learnerGroups']);
    let {validations} = yield this.validate();

    if (validations.get('isInvalid')) {
      return;
    }

    yield this.get('saveOffering').perform();
  }),
  lowestLearnerGroupLeaves: computed('learnerGroups.[]', function(){
    const learnerGroups = this.get('learnerGroups');
    const ids = learnerGroups.mapBy('id');
    return new Promise(resolve => {
      filter(learnerGroups, group => {
        return new Promise(resolve => {
          group.get('allDescendants').then(children => {
            let selectedChildren = children.filter(child => ids.contains(child.get('id')));
            resolve(selectedChildren.length === 0);
          });
        });
      }).then(lowestLeaves => resolve(lowestLeaves));
    });
  }),
  updateDurationMinutes: task(function * (minutes) {
    let {validations} = yield this.validate();
    this.send('addErrorDisplayFor', 'durationMinutes');

    if (validations.get('durationMinutes.isInvalid')) {
      return;
    }
    const hours = this.get('durationHours');
    const startDate = moment(this.get('startDate'));
    let endDate = startDate.clone().add(hours, 'hours').add(minutes, 'minutes').toDate();
    yield timeout(250);
    this.set('endDate', endDate);
  }).restartable(),
  updateDurationHours: task(function * (hours) {
    let {validations} = yield this.validate();
    this.send('addErrorDisplayFor', 'durationHours');

    if (validations.get('durationHours.isInvalid')) {
      return;
    }
    const minutes = this.get('durationMinutes');
    const startDate = moment(this.get('startDate'));
    let endDate = startDate.clone().add(hours, 'hours').add(minutes, 'minutes').toDate();
    yield timeout(250);
    this.set('endDate', endDate);
  }).restartable(),
  actions: {
    addLearnerGroup: function(learnerGroup){
      let learnerGroups = this.get('learnerGroups').toArray();
      learnerGroups.addObject(learnerGroup);
      learnerGroup.get('allDescendants').then(function(descendants){
        learnerGroups.addObjects(descendants);
      });
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('learnerGroups', learnerGroups);
    },
    removeLearnerGroup: function(learnerGroup){
      let learnerGroups = this.get('learnerGroups').toArray();
      learnerGroups.removeObject(learnerGroup);
      learnerGroup.get('allDescendants').then(function(descendants){
        learnerGroups.removeObjects(descendants);
      });
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('learnerGroups', learnerGroups);
    },
    toggleRecurringDay(day){
      let recurringDays = this.get('recurringDays');
      if (recurringDays.contains(day)) {
        recurringDays.removeObject(day);
      } else {
        recurringDays.pushObject(day);
      }
    },
    addInstructor(user){
      let instructors = this.get('instructors').toArray();
      instructors.addObject(user);
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('instructors', instructors);
    },
    addInstructorGroup(group){
      let instructorGroups = this.get('instructorGroups').toArray();
      instructorGroups.addObject(group);
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('instructorGroups', instructorGroups);
    },
    removeInstructor(user){
      let instructors = this.get('instructors').toArray();
      instructors.removeObject(user);
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('instructors', instructors);
    },
    removeInstructorGroup(group){
      let instructorGroups = this.get('instructorGroups').toArray();
      instructorGroups.removeObject(group);
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('instructorGroups', instructorGroups);
    },
    updateStartTime(value, type) {
      let startDate = moment(this.get('startDate'));

      if (type === 'hour') {
        startDate.hour(value);
      } else {
        startDate.minute(value);
      }
      const minutes = this.get('durationMinutes');
      const hours = this.get('durationHours');
      let endDate = startDate.clone().add(hours, 'hours').add(minutes, 'minutes');

      this.set('startDate', startDate.toDate());
      this.set('endDate', endDate.toDate());
    },
    updateStartDate(date) {
      const minutes = this.get('durationMinutes');
      const hours = this.get('durationHours');
      const currentStartDate = moment(this.get('startDate'));
      let startDate = moment(date).hour(currentStartDate.hour()).minute(currentStartDate.minute()).toDate();
      let endDate = moment(startDate).add(hours, 'hours').add(minutes, 'minutes').toDate();

      this.setProperties({startDate, endDate});
    },
  }
});
<commit_msg>Fix jumpy cursor on offering duration input<commit_after>import Ember from 'ember';
import moment from 'moment';
import { validator, buildValidations } from 'ember-cp-validations';
import ValidationErrorDisplay from 'ilios/mixins/validation-error-display';
import { task, timeout } from 'ember-concurrency';

const { Component, inject, computed, RSVP, isPresent, isEmpty } = Ember;
const { service } = inject;
const { Promise, map, filter, hash } = RSVP;

const Validations = buildValidations({
  room: [
    validator('presence', {
      presence: true
    }),
    validator('length', {
      max: 255
    }),
  ],
  numberOfWeeks: {
    dependentKeys: ['makeRecurring'],
    disabled(){
      return !this.get('model.makeRecurring');
    },
    validators: [
      validator('presence', {
        presence: true
      }),
      validator('number', {
        allowString: true,
        integer: true,
        gt: 0,
      }),
    ]
  },
  durationHours: [
    validator('number', {
      allowString: true,
      integer: true,
      gte: 0
    })
  ],
  durationMinutes: [
    validator('number', {
      allowString: true,
      integer: true,
      gte: 0,
      lte: 59
    })
  ],
  learnerGroups: {
    dependentKeys: ['smallGroupMode'],
    disabled(){
      return !this.get('model.smallGroupMode');
    },
    validators: [
      validator('length', {
        min: 1,
        messageKey: 'general.smallGroupMessage'
      })
    ]
  },

});

export default Component.extend(ValidationErrorDisplay, Validations, {
  currentUser: service(),
  didReceiveAttrs(){
    this._super(...arguments);
    const offering = this.get('offering');
    if (isPresent(offering)) {
      this.get('loadAttrsFromOffering').perform(offering);
    } else {
      this.loadDefaultAttrs();
    }
  },
  classNames: ['offering-form'],
  startDate: null,
  endDate: null,
  room: 'TBD',
  cohorts: null,
  learnerGroups: null,
  showRoom: false,
  showMakeRecurring: false,
  showInstructors: false,
  makeRecurring: false,
  recurringDays: null,
  numberOfWeeks: 1,
  instructors: null,
  instructorGroups: null,
  courseStartDate: null,
  courseEndDate: null,
  smallGroupMode: false,
  offeringsToSave: 0,
  savedOfferings: 0,
  recurringDayOptions: [
    {day: '0', t: 'general.sunday'},
    {day: '1', t: 'general.monday'},
    {day: '2', t: 'general.tuesday'},
    {day: '3', t: 'general.wednesday'},
    {day: '4', t: 'general.thursday'},
    {day: '5', t: 'general.friday'},
    {day: '6', t: 'general.saturday'},
  ],
  associatedSchools: computed('cohorts.[]', function(){
    return new Promise(resolve => {
      const cohorts = this.get('cohorts');
      if (isEmpty(cohorts)) {
        resolve([]);
      } else {
        map(cohorts.toArray(), cohort => {
          return cohort.get('school');
        }).then(cohortSchools => {
          let schools = [];
          schools.pushObjects(cohortSchools);
          resolve(schools.uniq().toArray());
        });
      }
    });
  }),
  availableInstructorGroups: computed('associatedSchools.[]', function(){
    return new Promise(resolve => {
      this.get('associatedSchools').then(associatedSchools => {
        map(associatedSchools, school => {
          return school.get('instructorGroups');
        }).then(allInstructorGropus => {
          let flat = allInstructorGropus.reduce((flattened, obj) => {
            return flattened.pushObjects(obj.toArray());
          }, []);

          resolve(flat);
        });
      });
    });
  }),
  defaultStartDate: computed('courseStartDate', 'courseEndDate', function(){
    const today = moment();
    const courseStartDate = this.get('courseStartDate');
    const courseEndDate = this.get('courseEndDate');
    let defaultStartDate = today.clone();
    if (isPresent(courseStartDate) && today.isBefore(courseStartDate)) {
      defaultStartDate = moment(courseStartDate);
    }
    if (isPresent(courseEndDate) && today.isAfter(courseEndDate)) {
      defaultStartDate = moment(courseEndDate);
    }

    return defaultStartDate.toDate();
  }),
  durationHours: computed('startDate', 'endDate', function(){
    const startDate = this.get('startDate');
    const endDate = this.get('endDate');

    if (isEmpty(startDate) || isEmpty(endDate)) {
      return 1;
    }
    let mStart = moment(startDate);
    let mEnd = moment(endDate);
    let diffInHours = mEnd.diff(mStart, 'hours');

    return diffInHours;
  }),
  durationMinutes: computed('startDate', 'endDate', function(){
    const startDate = this.get('startDate');
    const endDate = this.get('endDate');

    if (isEmpty(startDate) || isEmpty(endDate)) {
      return 0;
    }
    let mStart = moment(startDate);
    let mEnd = moment(endDate);

    const endHour = mEnd.hour();
    const endMinute = mEnd.minute();

    mStart.hour(endHour);
    const startMinute = mStart.minute();

    let diff = 0;
    if (endMinute > startMinute) {
      diff = endMinute - startMinute;
    } else if (endMinute < startMinute) {
      diff = (60 - startMinute) + endMinute;
    }
    return diff;
  }),
  makeRecurringOfferingObjects: task(function * () {
    const {
      startDate,
      endDate,
      room,
      instructorGroups,
      instructors,
      numberOfWeeks,
      recurringDays
    } = this.getProperties('startDate', 'endDate', 'room', 'instructorGroups', 'instructors', 'numberOfWeeks', 'recurringDays');
    const makeRecurring = this.get('makeRecurring');
    const learnerGroups = yield this.get('lowestLearnerGroupLeaves');
    let offerings = [];
    offerings.push({startDate, endDate, room, learnerGroups, instructorGroups, instructors});
    if (!makeRecurring) {
      return offerings;
    }

    const userPickedDay = moment(startDate).day();
    //convert strings to numbers use parseFloat because parseInt takes a second
    //argument and gets thrown off by map sending that argument as the counter
    const recurringDayInts = recurringDays.map(parseFloat).sort();

    // Add offerings for the rest of first week
    //only days AFTER the initial day are considered
    recurringDayInts.forEach(day => {
      if (day > userPickedDay) {
        let obj = {room, learnerGroups, instructorGroups, instructors};
        obj.startDate = moment(startDate).day(day).toDate();
        obj.endDate = moment(endDate).day(day).toDate();

        offerings.push(obj);
      }
    });
    recurringDayInts.pushObject(userPickedDay);
    recurringDayInts.sort();

    for (let i = 1; i < numberOfWeeks; i++) {
      recurringDayInts.forEach(day => {
        let obj = {room, learnerGroups, instructorGroups, instructors};
        obj.startDate = moment(startDate).day(day).add(i, 'weeks').toDate();
        obj.endDate = moment(endDate).day(day).add(i, 'weeks').toDate();

        offerings.push(obj);
      });
    }

    return offerings;
  }),
  makeSmallGroupOfferingObjects: task(function * (offerings){
    const smallGroupMode = this.get('smallGroupMode');
    if (!smallGroupMode) {
      return offerings;
    }

    let smallGroupOfferings = [];

    for (let i = 0; i < offerings.length; i++) {
      let {startDate, endDate, room, learnerGroups} = offerings[i];
      for (let j = 0; j < learnerGroups.length; j++) {
        let learnerGroup = learnerGroups[j];
        let defaultLocation = learnerGroup.get('location');
        if (isPresent(defaultLocation)) {
          room = defaultLocation;
        }
        let instructors = yield learnerGroup.get('instructors');
        let instructorGroups = yield learnerGroup.get('instructorGroups');
        let offering = {startDate, endDate, room, instructorGroups, instructors};
        offering.learnerGroups = [learnerGroup];

        smallGroupOfferings.pushObject(offering);
      }
    }

    return smallGroupOfferings;
  }),

  loadDefaultAttrs(){
    let startDate = moment(this.get('defaultStartDate')).hour(8).minute(0).second(0).toDate();
    let endDate = moment(this.get('defaultStartDate')).hour(9).minute(0).second(0).toDate();
    const room = 'TBD';
    const learnerGroups = [];
    const recurringDays = [];
    const instructors = [];
    const instructorGroups = [];

    this.setProperties({startDate, endDate, room, learnerGroups, recurringDays, instructors, instructorGroups});
  },

  loadAttrsFromOffering: task(function * (offering) {
    const startDate = offering.get('startDate');
    const endDate = offering.get('endDate');
    const room = offering.get('room');
    const recurringDays = [];
    let obj = yield hash({
      learnerGroups : offering.get('learnerGroups'),
      instructors : offering.get('instructors'),
      instructorGroups : offering.get('instructorGroups'),
    });
    const learnerGroups = obj.learnerGroups.toArray();
    const instructors = obj.instructors.toArray();
    const instructorGroups = obj.instructorGroups.toArray();

    this.setProperties({startDate, endDate, room, learnerGroups, recurringDays, instructors, instructorGroups});
  }),
  saveOffering: task(function * () {
    this.set('offeringsToSave', 0);
    this.set('savedOfferings', 0);
    this.send('addErrorDisplaysFor', ['room', 'numberOfWeeks', 'durationHours', 'durationMinutes', 'learnerGroups']);
    yield timeout(10);
    let {validations} = yield this.validate();

    if (validations.get('isInvalid')) {
      return;
    }
    let offerings = yield this.get('makeRecurringOfferingObjects').perform();
    offerings = yield this.get('makeSmallGroupOfferingObjects').perform(offerings);

    this.set('offeringsToSave', offerings.length);
    //save offerings in sets of 5
    let parts;
    while (offerings.length > 0){
      parts = offerings.splice(0, 5);
      yield map(parts, ({startDate, endDate, room, learnerGroups, instructorGroups, instructors}) => {
        return this.get('save')(startDate, endDate, room, learnerGroups, instructorGroups, instructors);
      });
      this.set('savedOfferings', this.get('savedOfferings') + parts.length);
    }
    this.send('clearErrorDisplay');
    this.get('close')();

  }),
  validateThenSaveOffering: task(function * () {
    this.send('addErrorDisplaysFor', ['room', 'numberOfWeeks', 'durationHours', 'durationMinutes', 'learnerGroups']);
    let {validations} = yield this.validate();

    if (validations.get('isInvalid')) {
      return;
    }

    yield this.get('saveOffering').perform();
  }),
  lowestLearnerGroupLeaves: computed('learnerGroups.[]', function(){
    const learnerGroups = this.get('learnerGroups');
    const ids = learnerGroups.mapBy('id');
    return new Promise(resolve => {
      filter(learnerGroups, group => {
        return new Promise(resolve => {
          group.get('allDescendants').then(children => {
            let selectedChildren = children.filter(child => ids.contains(child.get('id')));
            resolve(selectedChildren.length === 0);
          });
        });
      }).then(lowestLeaves => resolve(lowestLeaves));
    });
  }),
  updateDurationMinutes: task(function * (minutes) {
    let {validations} = yield this.validate();
    this.send('addErrorDisplayFor', 'durationMinutes');

    if (validations.get('durationMinutes.isInvalid')) {
      return;
    }
    const hours = this.get('durationHours');
    const startDate = moment(this.get('startDate'));
    let endDate = startDate.clone().add(hours, 'hours').add(minutes, 'minutes').toDate();
    this.set('endDate', endDate);
  }).restartable(),
  updateDurationHours: task(function * (hours) {
    let {validations} = yield this.validate();
    this.send('addErrorDisplayFor', 'durationHours');

    if (validations.get('durationHours.isInvalid')) {
      return;
    }
    const minutes = this.get('durationMinutes');
    const startDate = moment(this.get('startDate'));
    let endDate = startDate.clone().add(hours, 'hours').add(minutes, 'minutes').toDate();
    this.set('endDate', endDate);
  }).restartable(),
  actions: {
    addLearnerGroup: function(learnerGroup){
      let learnerGroups = this.get('learnerGroups').toArray();
      learnerGroups.addObject(learnerGroup);
      learnerGroup.get('allDescendants').then(function(descendants){
        learnerGroups.addObjects(descendants);
      });
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('learnerGroups', learnerGroups);
    },
    removeLearnerGroup: function(learnerGroup){
      let learnerGroups = this.get('learnerGroups').toArray();
      learnerGroups.removeObject(learnerGroup);
      learnerGroup.get('allDescendants').then(function(descendants){
        learnerGroups.removeObjects(descendants);
      });
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('learnerGroups', learnerGroups);
    },
    toggleRecurringDay(day){
      let recurringDays = this.get('recurringDays');
      if (recurringDays.contains(day)) {
        recurringDays.removeObject(day);
      } else {
        recurringDays.pushObject(day);
      }
    },
    addInstructor(user){
      let instructors = this.get('instructors').toArray();
      instructors.addObject(user);
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('instructors', instructors);
    },
    addInstructorGroup(group){
      let instructorGroups = this.get('instructorGroups').toArray();
      instructorGroups.addObject(group);
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('instructorGroups', instructorGroups);
    },
    removeInstructor(user){
      let instructors = this.get('instructors').toArray();
      instructors.removeObject(user);
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('instructors', instructors);
    },
    removeInstructorGroup(group){
      let instructorGroups = this.get('instructorGroups').toArray();
      instructorGroups.removeObject(group);
      //re-create the object so we trigger downstream didReceiveAttrs
      this.set('instructorGroups', instructorGroups);
    },
    updateStartTime(value, type) {
      let startDate = moment(this.get('startDate'));

      if (type === 'hour') {
        startDate.hour(value);
      } else {
        startDate.minute(value);
      }
      const minutes = this.get('durationMinutes');
      const hours = this.get('durationHours');
      let endDate = startDate.clone().add(hours, 'hours').add(minutes, 'minutes');

      this.set('startDate', startDate.toDate());
      this.set('endDate', endDate.toDate());
    },
    updateStartDate(date) {
      const minutes = this.get('durationMinutes');
      const hours = this.get('durationHours');
      const currentStartDate = moment(this.get('startDate'));
      let startDate = moment(date).hour(currentStartDate.hour()).minute(currentStartDate.minute()).toDate();
      let endDate = moment(startDate).add(hours, 'hours').add(minutes, 'minutes').toDate();

      this.setProperties({startDate, endDate});
    },
  }
});
<|endoftext|>


<commit_before>module.exports = {
    workers: 2,
    application: {
        basePath: __dirname + '/..',
        language: 'en',
        sourceLanguage: 'ru',
        components: {
            urlManager: {
                className: 'Jii.urlManager.UrlManager',
                rules: {
                    '': 'site/index',
                    'guide': 'site/guide',
                    'guide/<page>': 'site/guide',
                    'development': 'site/development',
                    'development/<page>': 'site/development',
                    'contact': 'site/contact'
                }
            },
            http: {
                className: 'Jii.httpServer.HttpServer',
                staticDirs: __dirname + '/../web/'
            },
            view: {
                className: 'Jii.view.ServerWebView'
            }
        }
    }
};<commit_msg>className is optional<commit_after>module.exports = {
    workers: 2,
    application: {
        basePath: __dirname + '/..',
        language: 'en',
        sourceLanguage: 'ru',
        components: {
            urlManager: {
                rules: {
                    '': 'site/index',
                    'guide': 'site/guide',
                    'guide/<page>': 'site/guide',
                    'development': 'site/development',
                    'development/<page>': 'site/development',
                    'contact': 'site/contact'
                }
            },
            http: {
                staticDirs: __dirname + '/../web/'
            },
            view: {}
        }
    }
};<|endoftext|>


<commit_before>/* global Pyjamas */

describe ('Pyjamas.register', function () {
    'use strict';

    var myObj;
    var MyClass = (function () {
        function MyClass() {
            this.value = 0;
            this.name = 'test';
        }

        MyClass.prototype.add = function () {
            this.value++;
        };

        return MyClass;
    })();

    beforeEach(function () {
        myObj = new MyClass();
    });

    it('Returns a pyjamas instance with the correct fields', function () {
        var instance = Pyjamas.register(MyClass, '0.1.0', {
            value : Number,
            name : String
        });
        expect(instance.version).toEqual('0.1.0');
        expect(instance.defines).toEqual({
            value : Number,
            name : String
        });
    });

    it('Adds the class constructor and pyjamas instance to the DB', function () {
        Pyjamas.register(MyClass, '0.1.0',{
            value : Number,
            name : String
        });
        expect(Pyjamas.DB.fetch(myObj.constructor).version).toEqual('0.1.0');
        expect(Pyjamas.DB.fetch(myObj.constructor).defines).toEqual({
            value : Number,
            name : String
        });
    });
});

<commit_msg>[Fixed #39] Moved Parens Inside IIFE<commit_after>/* global Pyjamas */

describe ('Pyjamas.register', function () {
    'use strict';

    var myObj;
    var MyClass = (function () {
        function MyClass() {
            this.value = 0;
            this.name = 'test';
        }

        MyClass.prototype.add = function () {
            this.value++;
        };

        return MyClass;
    }());

    beforeEach(function () {
        myObj = new MyClass();
    });

    it('Returns a pyjamas instance with the correct fields', function () {
        var instance = Pyjamas.register(MyClass, '0.1.0', {
            value : Number,
            name : String
        });
        expect(instance.version).toEqual('0.1.0');
        expect(instance.defines).toEqual({
            value : Number,
            name : String
        });
    });

    it('Adds the class constructor and pyjamas instance to the DB', function () {
        Pyjamas.register(MyClass, '0.1.0',{
            value : Number,
            name : String
        });
        expect(Pyjamas.DB.fetch(myObj.constructor).version).toEqual('0.1.0');
        expect(Pyjamas.DB.fetch(myObj.constructor).defines).toEqual({
            value : Number,
            name : String
        });
    });
});

<|endoftext|>


<commit_before>
var fs = require('fs');

var selectorNotFoundPath = 'capture/resources/selectorNotFound_noun_47569.png'
var genConfigPath = 'capture/config.json'

var configJSON = fs.read(genConfigPath);
var config = JSON.parse(configJSON);
if (!config.paths) {
  config.paths = {};
}

var bitmaps_reference = config.paths.bitmaps_reference || 'bitmaps_reference';
var bitmaps_test = config.paths.bitmaps_test || 'bitmaps_test';
var compareConfigFileName = config.paths.compare_data || 'compare/config.json';
var viewports = config.viewports;
var scenarios = config.scenarios||config.grabConfigs;

var compareConfig = {testPairs:[]};

var casper = require("casper").create({
  // clientScripts: ["jquery.js"] //lets try not to use this it's 2014 already people...
});

casper.on('resource.received', function(resource) {
    //casper.echo(resource.url);
});

casper.on("page.error", function(msg, trace) {
  // this.echo("Remote Error >    " + msg, "error");
  // this.echo("file:     " + trace[0].file, "WARNING");
  // this.echo("line:     " + trace[0].line, "WARNING");
  // this.echo("function: " + trace[0]["function"], "WARNING");
});

casper.on('remote.message', function(message) {
  this.echo('remote console > ' + message);
});

casper.on('resource.received', function(resource) {
  var status = resource.status;
  if(status >= 400) {
    casper.log('remote error > ' + resource.url + ' failed to load (' + status + ')', 'error');
  }
});

function capturePageSelectors(url,scenarios,viewports,bitmaps_reference,bitmaps_test,isReference){

  var
    screenshotNow = new Date(),
    screenshotDateTime = screenshotNow.getFullYear() + pad(screenshotNow.getMonth() + 1) + pad(screenshotNow.getDate()) + '-' + pad(screenshotNow.getHours()) + pad(screenshotNow.getMinutes()) + pad(screenshotNow.getSeconds());

  var consoleBuffer = '';
  var scriptTimeout = 20000;

  casper.on('remote.message', function(message) {
      this.echo(message);
      consoleBuffer = consoleBuffer + '\n' + message;
  });

  casper.start();
  // casper.viewport(1280,1024);

  casper.each(scenarios,function(casper, scenario, scenario_index){

    if (scenario.cookiesJsonFile && fs.isFile(scenario.cookiesJsonFile)) {
      var cookiesJson = fs.read(scenario.cookiesJsonFile);
      var cookies = JSON.parse(cookiesJson);
      for (var i = 0; i < cookies.length; i++) {
        phantom.addCookie(cookies[i]);
      }
    }

    casper.each(viewports, function(casper, vp, viewport_index) {
      this.then(function() {
        this.viewport(vp.width||vp.viewport.width, vp.height||vp.viewport.height);
      });
      this.thenOpen(scenario.url, function() {

        casper.waitFor(
          function(){ //test
            if(!scenario.readyEvent)return true;
            var regExReadyFlag = new RegExp(scenario.readyEvent,'i');
            return consoleBuffer.search(regExReadyFlag)>=0;
          }
          ,function(){//on done
            consoleBuffer = '';
            casper.echo('Ready event received.');
          }
          ,function(){casper.echo('ERROR: casper timeout.')} //on timeout
          ,scriptTimeout
        );
        casper.wait(scenario.delay||1);

      });
      casper.then(function() {
        this.echo('Current location is ' + scenario.url, 'info');

        //var src = this.evaluate(function() {return document.body.outerHTML; });
        //this.echo(src);
      });

      this.then(function(){

        this.echo('Screenshots for ' + vp.name + ' (' + (vp.width||vp.viewport.width) + 'x' + (vp.height||vp.viewport.height) + ')', 'info');

        //HIDE SELECTORS WE WANT TO AVOID
            if ( scenario.hasOwnProperty('hideSelectors') ) {
              scenario.hideSelectors.forEach(function(o,i,a){
                casper.evaluate(function(o){
                  Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
                    s.style.visibility='hidden';
                  });
                },o);
              });
            }

        //REMOVE UNWANTED SELECTORS FROM RENDER TREE
            if ( scenario.hasOwnProperty('removeSelectors') ) {
              scenario.removeSelectors.forEach(function(o,i,a){
                casper.evaluate(function(o){
                  Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
                    s.style.display='none';
                  });
                },o);
              });
            }

        //CREATE SCREEN SHOTS AND TEST COMPARE CONFIGURATION (CONFIG FILE WILL BE SAVED WHEN THIS PROCESS RETURNS)
            // If no selectors are provided then set the default 'body'
            if ( !scenario.hasOwnProperty('selectors') ) {
              scenario.selectors = [ 'body' ];
            }
        scenario.selectors.forEach(function(o,i,a){
          var cleanedSelectorName = o.replace(/[^a-zA-Z\d]/,'');//remove anything that's not a letter or a number
          //var cleanedUrl = scenario.url.replace(/[^a-zA-Z\d]/,'');//remove anything that's not a letter or a number
          var fileName = scenario_index + '_' + i + '_' + cleanedSelectorName + '_' + viewport_index + '_' + vp.name + '.png';;

          var reference_FP  = bitmaps_reference + '/' + fileName;
          var test_FP       = bitmaps_test + '/' + screenshotDateTime + '/' + fileName;

          var filePath      = (isReference)?reference_FP:test_FP;

          if (casper.exists(o)) {
            casper.captureSelector(filePath, o);
          } else {
            console.log('SELECTOR NOT FOUND: ' + o);
            // fs.copy(selectorNotFoundPath, "testNotFoundFile.png");

            if (!isReference) {
              compareConfig.testPairs.push({
                reference:reference_FP,
                test:test_FP,
                selector:o,
                fileName:fileName,
                label:scenario.label,
                misMatchThreshold: scenario.misMatchThreshold
              });
            }
          }
          //casper.echo('remote capture to > '+filePath,'info');

        });//end topLevelModules.forEach

      });

    });//end casper.each viewports

  });//end casper.each scenario

}

//========================
//this query should be moved to the prior process
//`isReference` could be better passed as env parameter
var exists = fs.exists(bitmaps_reference);
var isReference = false;
if(!exists){isReference=true; console.log('CREATING NEW REFERENCE FILES')}
//========================

capturePageSelectors(
  'index.html'
  ,scenarios
  ,viewports
  ,bitmaps_reference
  ,bitmaps_test
  ,isReference
);

casper.run(function(){
  complete();
  this.exit();
});

function complete(){
  var configData = JSON.stringify(compareConfig,null,2);
  fs.touch(compareConfigFileName);
  fs.write(compareConfigFileName, configData, 'w');
  console.log(
    'Comparison config file updated.'
    //,configData
  );
}

function pad(number) {
  var r = String(number);
  if ( r.length === 1 ) {
    r = '0' + r;
  }
  return r;
}
<commit_msg>updated missing selector fix<commit_after>
var fs = require('fs');

var selectorNotFoundPath = 'capture/resources/selectorNotFound_noun_47569.png'
var genConfigPath = 'capture/config.json'

var configJSON = fs.read(genConfigPath);
var config = JSON.parse(configJSON);
if (!config.paths) {
  config.paths = {};
}

var bitmaps_reference = config.paths.bitmaps_reference || 'bitmaps_reference';
var bitmaps_test = config.paths.bitmaps_test || 'bitmaps_test';
var compareConfigFileName = config.paths.compare_data || 'compare/config.json';
var viewports = config.viewports;
var scenarios = config.scenarios||config.grabConfigs;

var compareConfig = {testPairs:[]};

var casper = require("casper").create({
  // clientScripts: ["jquery.js"] //lets try not to use this it's 2014 already people...
});

casper.on('resource.received', function(resource) {
    //casper.echo(resource.url);
});

casper.on("page.error", function(msg, trace) {
  // this.echo("Remote Error >    " + msg, "error");
  // this.echo("file:     " + trace[0].file, "WARNING");
  // this.echo("line:     " + trace[0].line, "WARNING");
  // this.echo("function: " + trace[0]["function"], "WARNING");
});

casper.on('remote.message', function(message) {
  this.echo('remote console > ' + message);
});

casper.on('resource.received', function(resource) {
  var status = resource.status;
  if(status >= 400) {
    casper.log('remote error > ' + resource.url + ' failed to load (' + status + ')', 'error');
  }
});

function capturePageSelectors(url,scenarios,viewports,bitmaps_reference,bitmaps_test,isReference){

  var
    screenshotNow = new Date(),
    screenshotDateTime = screenshotNow.getFullYear() + pad(screenshotNow.getMonth() + 1) + pad(screenshotNow.getDate()) + '-' + pad(screenshotNow.getHours()) + pad(screenshotNow.getMinutes()) + pad(screenshotNow.getSeconds());

  var consoleBuffer = '';
  var scriptTimeout = 20000;

  casper.on('remote.message', function(message) {
      this.echo(message);
      consoleBuffer = consoleBuffer + '\n' + message;
  });

  casper.start();
  // casper.viewport(1280,1024);

  casper.each(scenarios,function(casper, scenario, scenario_index){

    if (scenario.cookiesJsonFile && fs.isFile(scenario.cookiesJsonFile)) {
      var cookiesJson = fs.read(scenario.cookiesJsonFile);
      var cookies = JSON.parse(cookiesJson);
      for (var i = 0; i < cookies.length; i++) {
        phantom.addCookie(cookies[i]);
      }
    }

    casper.each(viewports, function(casper, vp, viewport_index) {
      this.then(function() {
        this.viewport(vp.width||vp.viewport.width, vp.height||vp.viewport.height);
      });
      this.thenOpen(scenario.url, function() {

        casper.waitFor(
          function(){ //test
            if(!scenario.readyEvent)return true;
            var regExReadyFlag = new RegExp(scenario.readyEvent,'i');
            return consoleBuffer.search(regExReadyFlag)>=0;
          }
          ,function(){//on done
            consoleBuffer = '';
            casper.echo('Ready event received.');
          }
          ,function(){casper.echo('ERROR: casper timeout.')} //on timeout
          ,scriptTimeout
        );
        casper.wait(scenario.delay||1);

      });
      casper.then(function() {
        this.echo('Current location is ' + scenario.url, 'info');

        //var src = this.evaluate(function() {return document.body.outerHTML; });
        //this.echo(src);
      });

      this.then(function(){

        this.echo('Screenshots for ' + vp.name + ' (' + (vp.width||vp.viewport.width) + 'x' + (vp.height||vp.viewport.height) + ')', 'info');

        //HIDE SELECTORS WE WANT TO AVOID
            if ( scenario.hasOwnProperty('hideSelectors') ) {
              scenario.hideSelectors.forEach(function(o,i,a){
                casper.evaluate(function(o){
                  Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
                    s.style.visibility='hidden';
                  });
                },o);
              });
            }

        //REMOVE UNWANTED SELECTORS FROM RENDER TREE
            if ( scenario.hasOwnProperty('removeSelectors') ) {
              scenario.removeSelectors.forEach(function(o,i,a){
                casper.evaluate(function(o){
                  Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
                    s.style.display='none';
                  });
                },o);
              });
            }

        //CREATE SCREEN SHOTS AND TEST COMPARE CONFIGURATION (CONFIG FILE WILL BE SAVED WHEN THIS PROCESS RETURNS)
            // If no selectors are provided then set the default 'body'
            if ( !scenario.hasOwnProperty('selectors') ) {
              scenario.selectors = [ 'body' ];
            }
        scenario.selectors.forEach(function(o,i,a){
          var cleanedSelectorName = o.replace(/[^a-zA-Z\d]/,'');//remove anything that's not a letter or a number
          //var cleanedUrl = scenario.url.replace(/[^a-zA-Z\d]/,'');//remove anything that's not a letter or a number
          var fileName = scenario_index + '_' + i + '_' + cleanedSelectorName + '_' + viewport_index + '_' + vp.name + '.png';;

          var reference_FP  = bitmaps_reference + '/' + fileName;
          var test_FP       = bitmaps_test + '/' + screenshotDateTime + '/' + fileName;

          var filePath      = (isReference)?reference_FP:test_FP;

          if (casper.exists(o)) {
            casper.captureSelector(filePath, o);
          } else {
            var assetData = fs.read(selectorNotFoundPath, 'b')
            fs.write(filePath, assetData, 'b')
          }

          if (!isReference) {
            compareConfig.testPairs.push({
              reference:reference_FP,
              test:test_FP,
              selector:o,
              fileName:fileName,
              label:scenario.label,
              misMatchThreshold: scenario.misMatchThreshold
            });
          }
          //casper.echo('remote capture to > '+filePath,'info');

        });//end topLevelModules.forEach

      });

    });//end casper.each viewports

  });//end casper.each scenario

}

//========================
//this query should be moved to the prior process
//`isReference` could be better passed as env parameter
var exists = fs.exists(bitmaps_reference);
var isReference = false;
if(!exists){isReference=true; console.log('CREATING NEW REFERENCE FILES')}
//========================

capturePageSelectors(
  'index.html'
  ,scenarios
  ,viewports
  ,bitmaps_reference
  ,bitmaps_test
  ,isReference
);

casper.run(function(){
  complete();
  this.exit();
});

function complete(){
  var configData = JSON.stringify(compareConfig,null,2);
  fs.touch(compareConfigFileName);
  fs.write(compareConfigFileName, configData, 'w');
  console.log(
    'Comparison config file updated.'
    //,configData
  );
}

function pad(number) {
  var r = String(number);
  if ( r.length === 1 ) {
    r = '0' + r;
  }
  return r;
}
<|endoftext|>


<commit_before>/**
 * 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.
 */

goog.provide('org.apache.flex.html.beads.PanelView');

/**
 * @constructor
 */
org.apache.flex.html.beads.PanelView = function() {
  /**
   * @private
   * @type {boolean}
  */
  this.titleBarAdded_ = false;

};

/**
 * Metadata
 *
 * @type {Object.<string, Array.<Object>>}
 */
org.apache.flex.html.beads.PanelView
  .prototype.FLEXJS_CLASS_INFO =
    { names: [{ name: 'PanelView',
                qName: 'org.apache.flex.html.beads.PanelView'}] };

/**
 * @expose
 * @param {Object} value The new host.
 */
org.apache.flex.html.beads.PanelView.prototype.set_strand =
    function(value) {

  this.strand_ = value;

  this.strand_.titleBar = new org.apache.flex.html.TitleBar();
  this.strand_.titleBar.set_id('titleBar');
  this.strand_.titleBar.set_model(this.strand_.get_model());

  this.strand_.controlBar =
      new org.apache.flex.html.ControlBar();

  this.strand_.addEventListener('childrenAdded',
      goog.bind(this.changeHandler, this));

  // listen for changes to the strand's model so items can be changed
  // in the view
  this.strand_.model.addEventListener('titleChange',
      goog.bind(this.changeHandler, this));

};

/**
 * @param {Object} event The event that triggered this handler.
 */
org.apache.flex.html.beads.PanelView.prototype.changeHandler =
    function(event) {
  if (!this.titleBarAdded_)
  {
    this.titleBarAdded_ = true;
    this.strand_.addElement(this.strand_.titleBar);
    this.strand_.addElement(this.strand_.controlBar);
  }

  if (event.type == 'titleChange') {
    this.strand_.titleBar.set_title(this.strand_.model.get_title());
  }

  this.strand_.dispatchEvent('layoutNeeded');
};
<commit_msg>if explicitly sized, size the children<commit_after>/**
 * 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.
 */

goog.provide('org.apache.flex.html.beads.PanelView');

/**
 * @constructor
 */
org.apache.flex.html.beads.PanelView = function() {
  /**
   * @private
   * @type {boolean}
  */
  this.titleBarAdded_ = false;

};

/**
 * Metadata
 *
 * @type {Object.<string, Array.<Object>>}
 */
org.apache.flex.html.beads.PanelView
  .prototype.FLEXJS_CLASS_INFO =
    { names: [{ name: 'PanelView',
                qName: 'org.apache.flex.html.beads.PanelView'}] };

/**
 * @expose
 * @param {Object} value The new host.
 */
org.apache.flex.html.beads.PanelView.prototype.set_strand =
    function(value) {

  this.strand_ = value;

  this.strand_.titleBar = new org.apache.flex.html.TitleBar();
  this.strand_.titleBar.set_id('titleBar');
  this.strand_.titleBar.set_model(this.strand_.get_model());

  this.strand_.controlBar =
      new org.apache.flex.html.ControlBar();

  this.strand_.addEventListener('childrenAdded',
      goog.bind(this.changeHandler, this));

  // listen for changes to the strand's model so items can be changed
  // in the view
  this.strand_.model.addEventListener('titleChange',
      goog.bind(this.changeHandler, this));

};

/**
 * @param {Object} event The event that triggered this handler.
 */
org.apache.flex.html.beads.PanelView.prototype.changeHandler =
    function(event) {
  var strand = this.strand_;
  if (!this.titleBarAdded_)
  {
    this.titleBarAdded_ = true;
    strand.addElement(strand.titleBar);
    strand.addElement(strand.controlBar);
  }

  if (event.type == 'titleChange') {
    strand.titleBar.set_title(strand.model.get_title());
  }

  var p = this.strand_.positioner;
  if (!strand.isWidthSizedToContent()) {
    var w = strand.get_width();
	w -= p.offsetWidth - p.clientWidth;
    strand.titleBar.setWidth(w);
    strand.contentArea.style.width = w.toString() + "px";
    if (strand.controlBar)
      strand.controlBar.setWidth(w);
  }
  if (!strand.isHeightSizedToContent()) {
    var t = strand.titleBar.get_height();
    var b = 0;
    if (strand.controlBar)
      b = strand.controlBar.get_height();
    strand.contentArea.style.top = t.toString() + "px";
	var h = strand.get_height() - t - b;
	h -= p.offsetHeight - p.clientHeight;
    strand.contentArea.style.height = h.toString() + "px";
  }
  this.strand_.dispatchEvent('layoutNeeded');
};
<|endoftext|>


<commit_before>var path        = require('path');
var _           = require('underscore');
var fs          = require('fs');
var io          = require('indian-ocean');
var execSync    = require('child_process').execSync;
var sh_commands = require('../src/sh-commands.js');
var moment      = require('moment-timezone');
var Promise     = require('promise');

var home_dir = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
var config_path = path.join(home_dir, '.conf', 'kestrel-config.json');
var config = require(config_path);

var PROJECT_PATH = path.resolve('.');
var LOCAL_FOLDER = path.basename(PROJECT_PATH);

function readDeploySettings(){
  var file_path_and_name = path.join(PROJECT_PATH, '.kestrel', 'deploy-settings.json'),
      settings = {};
  if (io.existsSync(file_path_and_name)){
    settings = require(file_path_and_name);
  }
  return settings;
}

function getDirectories(srcpath, opts) {
  return fs.readdirSync(srcpath).filter(function(file) {
    var is_directory = fs.statSync(path.join(srcpath, file)).isDirectory()
    // Test for folders that start with a dot
    if (is_directory && opts.excludeHidden) {
      is_directory = !/^\./.test(file)
    }
    return is_directory;
  });
}

function getLocalDeployDirChoices(){
  var dirs = getDirectories(PROJECT_PATH, {excludeHidden: true})

  // Add repo-name
  var dirs_with_basename = dirs.map(function(dir){
    return ['.', dir].join('/'); // Kestrel server will run with `/` file paths for Linux
  })
  var all_dirs = ['./'].concat(dirs_with_basename);
  // If we have a dir that we deployed to that isn't in our list, then it was a sub-dir, so give that as the first choice
  if (!_.contains(all_dirs, default_deploy.local_path.replace(LOCAL_FOLDER, '.')) ) {
    all_dirs = [default_deploy.local_path.replace(LOCAL_FOLDER, '.')].concat(all_dirs)
  }
  return all_dirs.map(function(dir){ return {name: dir, value: dir.replace('.', LOCAL_FOLDER), short: dir} })

}

function getConfigRemotePath(){
  var remote_path = config.publishing.remote_path
  if (config.publishing.is_moment_template) {
    remote_path = moment().format(remote_path)
  }
  return remote_path
}

var default_deploy = {
  bucket_environment: 'staging',
  trigger_type: 'sync',
  local_path: './',
  remote_path: getConfigRemotePath() + '/' + LOCAL_FOLDER,
  when: 'now'
};

_.extend(default_deploy, readDeploySettings());

var local_deploy_dir_choices = getLocalDeployDirChoices()

var questions = [
  {
    type: 'list',
    name: 'bucket_environment',
    message: 'Deploy to which environment?',
    choices: ['staging', 'prod'],
    default: default_deploy.bucket_environment
  },{
    type: 'list',
    name: 'trigger_type',
    message: 'Deploy method?',
    choices: function(){
      var choices = ['sync']
      if (config.server.hard_deploy.enabled) {
        choices.push('hard')
      }
      return choices
    },
    default: default_deploy.trigger_type
  },{
    type: 'list-input',
    name: 'local_path',
    message: 'Deploy from directory:',
    choices: local_deploy_dir_choices,
    default: default_deploy.local_path
  },{
    type: 'input',
    name: 'remote_path',
    message: 'Deploy to:',
    default: default_deploy.remote_path,
    filter: function(val){
      return val.trim()
    }
  },{
    type: 'input',
    name: 'when',
    message: 'When? e.g. 2015-01-01 14:00',
    default: default_deploy.when,
    filter: function(val){
      return val.trim()
    }
  },{
    type: 'password',
    name: 'trigger',
    message: 'Enter the trigger:'
  }
]

module.exports = questions;<commit_msg>better loading of prior deploy path<commit_after>var path        = require('path');
var _           = require('underscore');
var fs          = require('fs');
var io          = require('indian-ocean');
var execSync    = require('child_process').execSync;
var sh_commands = require('../src/sh-commands.js');
var moment      = require('moment-timezone');
var Promise     = require('promise');

var home_dir = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
var config_path = path.join(home_dir, '.conf', 'kestrel-config.json');
var config = require(config_path);

var PROJECT_PATH = path.resolve('.');
var LOCAL_FOLDER = path.basename(PROJECT_PATH);

function readDeploySettings(){
  var file_path_and_name = path.join(PROJECT_PATH, '.kestrel', 'deploy-settings.json'),
      settings = {};
  if (io.existsSync(file_path_and_name)){
    settings = require(file_path_and_name);
  }
  return settings;
}

function getDirectories(srcpath, opts) {
  return fs.readdirSync(srcpath).filter(function(file) {
    var is_directory = fs.statSync(path.join(srcpath, file)).isDirectory()
    // Test for folders that start with a dot
    if (is_directory && opts.excludeHidden) {
      is_directory = !/^\./.test(file)
    }
    return is_directory;
  });
}

function getLocalDeployDirChoices(){
  var dirs = getDirectories(PROJECT_PATH, {excludeHidden: true})

  // Add repo-name
  var dirs_with_basename = dirs.map(function(dir){
    return ['.', dir].join('/'); // Kestrel server will run with `/` file paths for Linux
  })
  var all_dirs = ['./'].concat(dirs_with_basename);
  // If we have a dir that we deployed to that isn't in our list, then it was a sub-dir, so give that as the first choice
  if (!_.contains(all_dirs, default_deploy.local_path.replace(LOCAL_FOLDER, '.')) ) {
    all_dirs = [default_deploy.local_path.replace(LOCAL_FOLDER, '.')].concat(all_dirs)
  }
  return all_dirs.map(function(dir){ return {name: dir, value: dir.replace('.', LOCAL_FOLDER), short: dir} })

}

function getConfigRemotePath(){
  var remote_path = config.publishing.remote_path
  if (config.publishing.is_moment_template) {
    remote_path = moment().format(remote_path)
  }
  return remote_path
}

var default_deploy = {
  bucket_environment: 'staging',
  trigger_type: 'sync',
  local_path: './',
  remote_path: getConfigRemotePath() + '/' + LOCAL_FOLDER,
  when: 'now'
};

_.extend(default_deploy, readDeploySettings());

var local_deploy_dir_choices = getLocalDeployDirChoices()

var questions = [
  {
    type: 'list',
    name: 'bucket_environment',
    message: 'Deploy to which environment?',
    choices: ['staging', 'prod'],
    default: default_deploy.bucket_environment
  },{
    type: 'list',
    name: 'trigger_type',
    message: 'Deploy method?',
    choices: function(){
      var choices = ['sync']
      if (config.server.hard_deploy.enabled) {
        choices.push('hard')
      }
      return choices
    },
    default: default_deploy.trigger_type
  },{
    type: 'list-input',
    name: 'local_path',
    message: 'Deploy from directory:',
    choices: local_deploy_dir_choices,
    default: default_deploy.local_path.replace('.', LOCAL_FOLDER)
  },{
    type: 'input',
    name: 'remote_path',
    message: 'Deploy to:',
    default: default_deploy.remote_path,
    filter: function(val){
      return val.trim()
    }
  },{
    type: 'input',
    name: 'when',
    message: 'When? e.g. 2015-01-01 14:00',
    default: default_deploy.when,
    filter: function(val){
      return val.trim()
    }
  },{
    type: 'password',
    name: 'trigger',
    message: 'Enter the trigger:'
  }
]

module.exports = questions;<|endoftext|>


<commit_before>import Ember from 'ember';
import LayoutRules from '../mixins/layout-rules';
import StyleSafe from '../mixins/style-safe';

var MdIcon = Ember.Component.extend(LayoutRules, StyleSafe, {
    iconService: Ember.inject.service('icon'),

    tagName: 'md-icon',

    attributeBindings: ['md-menu-origin', 'md-menu-align-target'],

    didInsertElement() {
        this._super(...arguments);
        this.setupIcon();
    },

    iconName: Ember.computed('md-svg-icon', 'md-svg-src', function() {
        return this.get('md-svg-icon') || this.get('md-svg-src') || '';
    }),

    classNameBindings: ['iconClass'],

    fontIcon: Ember.computed('mdFontIcon', function() {
        return 'md-font ' + this.get('mdFontIcon');
    }),

    loadIcon: Ember.observer('iconName', function() {

        var iconName = this.get('iconName'),
            element = this.$();

        if (iconName) {
            var is = this.get('iconService');
            is.getIcon(iconName)
                .then(function(icon) {
                    element.empty().append(icon.svg);;
                });
        }else{
          element.empty();
        }
    }),

    setupIcon() {
        if (!this.get('mdFontIcon')) {
            this.loadIcon();
        }
    }

});

export default MdIcon;
<commit_msg>Update md-icon.js<commit_after>import Ember from 'ember';
import LayoutRules from '../mixins/layout-rules';
import StyleSafe from '../mixins/style-safe';

var MdIcon = Ember.Component.extend(LayoutRules, StyleSafe, {
    iconService: Ember.inject.service('icon'),

    tagName: 'md-icon',

    attributeBindings: ['md-menu-origin', 'md-menu-align-target'],

    didInsertElement() {
        this._super(...arguments);
        this.setupIcon();
    },

    iconName: Ember.computed('md-svg-icon', 'md-svg-src', function() {
        return this.get('md-svg-icon') || this.get('md-svg-src') || '';
    }),

    classNameBindings: ['iconClass'],

    fontIcon: Ember.computed('mdFontIcon', function() {
        return 'md-font ' + this.get('mdFontIcon');
    }),

    loadIcon: Ember.observer('iconName', function() {

        var iconName = this.get('iconName'),
            element = this.$();

        if (iconName) {
            var is = this.get('iconService');
            is.getIcon(iconName)
                .then(function(icon) {
                    element.html(icon.svg);;
                });
        }else{
          element.empty();
        }
    }),

    setupIcon() {
        if (!this.get('mdFontIcon')) {
            this.loadIcon();
        }
    }

});

export default MdIcon;
<|endoftext|>


<commit_before>require('./test_helper');
var step = require('step');
var assert = require('./assert');
var redis = require('redis');
var _ = require('underscore');
var querystring = require('querystring');
var mapnik = require('mapnik');
var Windshaft = require('../../lib/windshaft');
var ServerOptions = require('./server_options');

var DEFAULT_POINT_STYLE = [
    '#layer {',
    '  marker-fill: #FF6600;',
    '  marker-opacity: 1;',
    '  marker-width: 16;',
    '  marker-line-color: white;',
    '  marker-line-width: 3;',
    '  marker-line-opacity: 0.9;',
    '  marker-placement: point;',
    '  marker-type: ellipse;',
    '  marker-allow-overlap: true;',
    '}'
].join('');

module.exports = {
    createLayergroup: createLayergroup,
    withLayergroup: withLayergroup,

    singleLayerMapConfig: singleLayerMapConfig,
    defaultTableMapConfig: defaultTableMapConfig,

    getStaticBbox: getStaticBbox,
    getStaticCenter: getStaticCenter,
    getGrid: getGrid,
    getGridJsonp: getGridJsonp,
    getTorque: getTorque,
    getTile: getTile,
    getTileLayer: getTileLayer
};

var server = new Windshaft.Server(ServerOptions);
server.setMaxListeners(0);
var redisClient = redis.createClient(global.environment.redis.port);

var jsonContentType = 'application/json; charset=utf-8';
var pngContentType = 'image/png';

function createLayergroup(layergroupConfig, options, callback) {
    if (!callback) {
        callback = options;
        options = {
            method: 'POST',
            statusCode: 200
        };
    }

    var expectedResponse = {
        status: options.statusCode || 200,
        headers: {
            'Content-Type': 'application/json; charset=utf-8'
        }
    };

    step(
        function requestLayergroup() {
            var next = this;
            var request = layergroupRequest(layergroupConfig, options.method, options.callbackName, options.params);
            assert.response(serverInstance(options), request, expectedResponse, function (res, err) {
                next(err, res);
            });
        },
        function validateLayergroup(err, res) {
            assert.ok(!err, 'Failed to request layergroup');

            var parsedBody = JSON.parse(res.body);
            var layergroupid = parsedBody.layergroupid;

            if (layergroupid) {
                var redisKey = 'map_cfg|' + layergroupid;
                redisClient.del(redisKey, function (/*delErr*/) {
                    return callback(err, res, parsedBody);
                });
            } else {
                return callback(err, res, parsedBody);
            }
        }
    );
}

function serverInstance(options) {
    if (options.server) {
        return options.server;
    }

    if (options.serverOptions) {
        var otherServer = new Windshaft.Server(options.serverOptions);
        otherServer.setMaxListeners(0);
        return otherServer;
    }

    return server;
}

function layergroupRequest(layergroupConfig, method, callbackName, extraParams) {
    method = method || 'POST';

    var request = {
        url: '/database/windshaft_test/layergroup',
        headers: {
            'Content-Type': 'application/json'
        }
    };

    var urlParams = _.extend({}, extraParams);
    if (callbackName) {
        urlParams.callback = callbackName;
    }

    if (method.toUpperCase() === 'GET') {
        request.method = 'GET';
        urlParams.config = JSON.stringify(layergroupConfig);
    } else {
        request.method = 'POST';
        request.data = JSON.stringify(layergroupConfig);
    }

    if (Object.keys(urlParams).length) {
        request.url += '?' + querystring.stringify(urlParams);
    }

    return request;
}

function singleLayerMapConfig(sql, cartocss, cartocssVersion, interactivity) {
    return {
        version: '1.3.0',
        layers: [
            {
                type: 'mapnik',
                options: {
                    sql: sql,
                    cartocss: cartocss || DEFAULT_POINT_STYLE,
                    cartocss_version: cartocssVersion || '2.3.0',
                    interactivity: interactivity
                }
            }
        ]
    };
}

function defaultTableMapConfig(tableName, cartocss, cartocssVersion, interactivity) {
    return singleLayerMapConfig(defaultTableQuery(tableName), cartocss, cartocssVersion, interactivity);
}

function defaultTableQuery(tableName) {
    return _.template('SELECT * FROM <%= tableName %>', {tableName: tableName});
}

function getStaticBbox(layergroupConfig, west, south, east, north, width, height, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = pngContentType;
    }

    var url = [
        'static',
        'bbox',
        '<%= layergroupid %>',
        [west, south, east, north].join(','),
        width,
        height
    ].join('/') + '.png';
    return getGeneric(layergroupConfig, url, expectedResponse, callback);
}

function getStaticCenter(layergroupConfig, zoom, lat, lon, width, height, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = pngContentType;
    }

    var url = [
        'static',
        'center',
        '<%= layergroupid %>',
        zoom,
        lat,
        lon,
        width,
        height
    ].join('/') + '.png';
    return getGeneric(layergroupConfig, url, expectedResponse, callback);
}

function getGrid(layergroupConfig, layer, z, x, y, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = jsonContentType;
    }

    var options = {
        layer: layer,
        z: z,
        x: x,
        y: y,
        format: 'grid.json'
    };
    return getLayer(layergroupConfig, options, expectedResponse, callback);
}

function getGridJsonp(layergroupConfig, layer, z, x, y, jsonpCallbackName, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = jsonContentType;
    }

    var options = {
        layer: layer,
        z: z,
        x: x,
        y: y,
        format: 'grid.json',
        jsonpCallbackName: jsonpCallbackName
    };
    return getLayer(layergroupConfig, options, expectedResponse, callback);
}

function getTorque(layergroupConfig, layer, z, x, y, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = jsonContentType;
    }

    var options = {
        layer: layer,
        z: z,
        x: x,
        y: y,
        format: 'torque.json'
    };
    return getLayer(layergroupConfig, options, expectedResponse, callback);
}

function getTile(layergroupConfig, z, x, y, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = pngContentType;
    }

    var options = {
        z: z,
        x: x,
        y: y,
        format: 'png'
    };
    return getLayer(layergroupConfig, options, expectedResponse, callback);
}

function getTileLayer(layergroupConfig, options, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = pngContentType;
    }

    return getLayer(layergroupConfig, options, expectedResponse, callback);
}

function getLayer(layergroupConfig, options, expectedResponse, callback) {
    return getGeneric(layergroupConfig, tileUrlStrategy(options), expectedResponse, callback);
}

function tileUrlStrategy(options) {
    var urlLayerPattern = [
        '<%= layer %>',
        '<%= z %>',
        '<%= x %>',
        '<%= y %>'
    ].join('/') + '.<%= format %>';

    if (options.jsonpCallbackName) {
        urlLayerPattern += '?callback=<%= jsonpCallbackName %>';
    }

    var urlNoLayerPattern = [
        '<%= z %>',
        '<%= x %>',
        '<%= y %>'
    ].join('/') + '.<%= format %>';

    var urlTemplate = _.template((options.layer === undefined) ? urlNoLayerPattern : urlLayerPattern);

    options.format = options.format || 'png';

    return '<%= layergroupid %>/' + urlTemplate(_.defaults(options, { z: 0, x: 0, y: 0, layer: 0 }));
}

function getGeneric(layergroupConfig, url, expectedResponse, callback) {
    if (_.isString(expectedResponse)) {
        expectedResponse = {
            status: 200,
            headers: {
                'Content-Type': expectedResponse
            }
        };
    }
    var contentType = expectedResponse.headers['Content-Type'];

    var layergroupid = null;

    step(
        function requestLayergroup() {
            var next = this;
            var request = {
                url: '/database/windshaft_test/layergroup',
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify(layergroupConfig)
            };
            var expectedResponse = {
                status: 200,
                headers: {
                    'Content-Type': 'application/json; charset=utf-8'
                }
            };
            assert.response(server, request, expectedResponse, function (res, err) {
                next(err, res);
            });
        },
        function validateLayergroup(err, res) {
            assert.ok(!err, 'Failed to create layergroup');

            var parsedBody = JSON.parse(res.body);
            layergroupid = parsedBody.layergroupid;

            assert.ok(layergroupid);

            return res;
        },
        function requestTile(err, res) {
            assert.ok(!err, 'Invalid layergroup response: ' + res.body);

            var next = this;

            var finalUrl = '/database/windshaft_test/layergroup/' + _.template(url, {
                layergroupid: layergroupid
            });

            var request = {
                url: finalUrl,
                method: 'GET'
            };

            if (contentType === pngContentType) {
                request.encoding = 'binary';
            }

            assert.response(server, request, expectedResponse, function (res, err) {
                next(err, res);
            });
        },
        function validateTile(err, res) {
            assert.ok(!err, 'Failed to get tile');
            var redisKey = 'map_cfg|' + layergroupid;

            var img;
            if (contentType === pngContentType) {
                img = new mapnik.Image.fromBytesSync(new Buffer(res.body, 'binary'));
            }

            redisClient.del(redisKey, function (/*delErr*/) {
                return callback(err, res, img);
            });
        }
    );
}

function withLayergroup(layergroupConfig, validationLayergroupFn, callback) {
    if (!callback) {
        callback = validationLayergroupFn;
        validationLayergroupFn = function() {};
    }

    var layergroupExpectedResponse = {
        status: 200,
        headers: {
            'Content-Type': 'application/json; charset=utf-8'
        }
    };

    step(
        function requestLayergroup() {
            var next = this;
            var request = layergroupRequest(layergroupConfig, 'POST');
            assert.response(server, request, layergroupExpectedResponse, function (res, err) {
                next(err, res);
            });
        },
        function validateLayergroup(err, res) {
            assert.ok(!err, 'Failed to request layergroup');

            var parsedBody = JSON.parse(res.body);
            var layergroupid = parsedBody.layergroupid;

            assert.ok(layergroupid, 'No layergroup was created');

            validationLayergroupFn(res);

            function requestTile(layergroupUrl, options, callback) {
                if (!callback) {
                    callback = options;
                    options = {
                        statusCode: 200,
                        contentType: pngContentType
                    };
                }

                var baseUrlTpl = '/database/windshaft_test/layergroup/<%= layergroupid %>';
                var finalUrl = _.template(baseUrlTpl, { layergroupid: layergroupid }) + layergroupUrl;

                var request = {
                    url: finalUrl,
                    method: 'GET'
                };

                if (options.contentType === pngContentType) {
                    request.encoding = 'binary';
                }

                var tileExpectedResponse = {
                    status: options.statusCode || 200,
                    headers: {
                        'Content-Type': options.contentType || pngContentType
                    }
                };

                assert.response(server, request, tileExpectedResponse, function (res, err) {
                    callback(err, res);
                });
            }

            function finish(done) {
                var redisKey = 'map_cfg|' + layergroupid;
                redisClient.del(redisKey, function (delErr) {
                    return done(delErr);
                });
            }

            return callback(err, requestTile, finish);
        }
    );
}
<commit_msg>Allow to pass server options when using withLayergroup<commit_after>require('./test_helper');
var step = require('step');
var assert = require('./assert');
var redis = require('redis');
var _ = require('underscore');
var querystring = require('querystring');
var mapnik = require('mapnik');
var Windshaft = require('../../lib/windshaft');
var ServerOptions = require('./server_options');

var DEFAULT_POINT_STYLE = [
    '#layer {',
    '  marker-fill: #FF6600;',
    '  marker-opacity: 1;',
    '  marker-width: 16;',
    '  marker-line-color: white;',
    '  marker-line-width: 3;',
    '  marker-line-opacity: 0.9;',
    '  marker-placement: point;',
    '  marker-type: ellipse;',
    '  marker-allow-overlap: true;',
    '}'
].join('');

module.exports = {
    createLayergroup: createLayergroup,
    withLayergroup: withLayergroup,

    singleLayerMapConfig: singleLayerMapConfig,
    defaultTableMapConfig: defaultTableMapConfig,

    getStaticBbox: getStaticBbox,
    getStaticCenter: getStaticCenter,
    getGrid: getGrid,
    getGridJsonp: getGridJsonp,
    getTorque: getTorque,
    getTile: getTile,
    getTileLayer: getTileLayer
};

var server = new Windshaft.Server(ServerOptions);
server.setMaxListeners(0);
var redisClient = redis.createClient(global.environment.redis.port);

var jsonContentType = 'application/json; charset=utf-8';
var pngContentType = 'image/png';

function createLayergroup(layergroupConfig, options, callback) {
    if (!callback) {
        callback = options;
        options = {
            method: 'POST',
            statusCode: 200
        };
    }

    var expectedResponse = {
        status: options.statusCode || 200,
        headers: {
            'Content-Type': 'application/json; charset=utf-8'
        }
    };

    step(
        function requestLayergroup() {
            var next = this;
            var request = layergroupRequest(layergroupConfig, options.method, options.callbackName, options.params);
            assert.response(serverInstance(options), request, expectedResponse, function (res, err) {
                next(err, res);
            });
        },
        function validateLayergroup(err, res) {
            assert.ok(!err, 'Failed to request layergroup');

            var parsedBody = JSON.parse(res.body);
            var layergroupid = parsedBody.layergroupid;

            if (layergroupid) {
                var redisKey = 'map_cfg|' + layergroupid;
                redisClient.del(redisKey, function (/*delErr*/) {
                    return callback(err, res, parsedBody);
                });
            } else {
                return callback(err, res, parsedBody);
            }
        }
    );
}

function serverInstance(options) {
    if (options.server) {
        return options.server;
    }

    if (options.serverOptions) {
        var otherServer = new Windshaft.Server(options.serverOptions);
        otherServer.setMaxListeners(0);
        return otherServer;
    }

    return server;
}

function layergroupRequest(layergroupConfig, method, callbackName, extraParams) {
    method = method || 'POST';

    var request = {
        url: '/database/windshaft_test/layergroup',
        headers: {
            'Content-Type': 'application/json'
        }
    };

    var urlParams = _.extend({}, extraParams);
    if (callbackName) {
        urlParams.callback = callbackName;
    }

    if (method.toUpperCase() === 'GET') {
        request.method = 'GET';
        urlParams.config = JSON.stringify(layergroupConfig);
    } else {
        request.method = 'POST';
        request.data = JSON.stringify(layergroupConfig);
    }

    if (Object.keys(urlParams).length) {
        request.url += '?' + querystring.stringify(urlParams);
    }

    return request;
}

function singleLayerMapConfig(sql, cartocss, cartocssVersion, interactivity) {
    return {
        version: '1.3.0',
        layers: [
            {
                type: 'mapnik',
                options: {
                    sql: sql,
                    cartocss: cartocss || DEFAULT_POINT_STYLE,
                    cartocss_version: cartocssVersion || '2.3.0',
                    interactivity: interactivity
                }
            }
        ]
    };
}

function defaultTableMapConfig(tableName, cartocss, cartocssVersion, interactivity) {
    return singleLayerMapConfig(defaultTableQuery(tableName), cartocss, cartocssVersion, interactivity);
}

function defaultTableQuery(tableName) {
    return _.template('SELECT * FROM <%= tableName %>', {tableName: tableName});
}

function getStaticBbox(layergroupConfig, west, south, east, north, width, height, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = pngContentType;
    }

    var url = [
        'static',
        'bbox',
        '<%= layergroupid %>',
        [west, south, east, north].join(','),
        width,
        height
    ].join('/') + '.png';
    return getGeneric(layergroupConfig, url, expectedResponse, callback);
}

function getStaticCenter(layergroupConfig, zoom, lat, lon, width, height, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = pngContentType;
    }

    var url = [
        'static',
        'center',
        '<%= layergroupid %>',
        zoom,
        lat,
        lon,
        width,
        height
    ].join('/') + '.png';
    return getGeneric(layergroupConfig, url, expectedResponse, callback);
}

function getGrid(layergroupConfig, layer, z, x, y, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = jsonContentType;
    }

    var options = {
        layer: layer,
        z: z,
        x: x,
        y: y,
        format: 'grid.json'
    };
    return getLayer(layergroupConfig, options, expectedResponse, callback);
}

function getGridJsonp(layergroupConfig, layer, z, x, y, jsonpCallbackName, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = jsonContentType;
    }

    var options = {
        layer: layer,
        z: z,
        x: x,
        y: y,
        format: 'grid.json',
        jsonpCallbackName: jsonpCallbackName
    };
    return getLayer(layergroupConfig, options, expectedResponse, callback);
}

function getTorque(layergroupConfig, layer, z, x, y, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = jsonContentType;
    }

    var options = {
        layer: layer,
        z: z,
        x: x,
        y: y,
        format: 'torque.json'
    };
    return getLayer(layergroupConfig, options, expectedResponse, callback);
}

function getTile(layergroupConfig, z, x, y, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = pngContentType;
    }

    var options = {
        z: z,
        x: x,
        y: y,
        format: 'png'
    };
    return getLayer(layergroupConfig, options, expectedResponse, callback);
}

function getTileLayer(layergroupConfig, options, expectedResponse, callback) {
    if (!callback) {
        callback = expectedResponse;
        expectedResponse = pngContentType;
    }

    return getLayer(layergroupConfig, options, expectedResponse, callback);
}

function getLayer(layergroupConfig, options, expectedResponse, callback) {
    return getGeneric(layergroupConfig, tileUrlStrategy(options), expectedResponse, callback);
}

function tileUrlStrategy(options) {
    var urlLayerPattern = [
        '<%= layer %>',
        '<%= z %>',
        '<%= x %>',
        '<%= y %>'
    ].join('/') + '.<%= format %>';

    if (options.jsonpCallbackName) {
        urlLayerPattern += '?callback=<%= jsonpCallbackName %>';
    }

    var urlNoLayerPattern = [
        '<%= z %>',
        '<%= x %>',
        '<%= y %>'
    ].join('/') + '.<%= format %>';

    var urlTemplate = _.template((options.layer === undefined) ? urlNoLayerPattern : urlLayerPattern);

    options.format = options.format || 'png';

    return '<%= layergroupid %>/' + urlTemplate(_.defaults(options, { z: 0, x: 0, y: 0, layer: 0 }));
}

function getGeneric(layergroupConfig, url, expectedResponse, callback) {
    if (_.isString(expectedResponse)) {
        expectedResponse = {
            status: 200,
            headers: {
                'Content-Type': expectedResponse
            }
        };
    }
    var contentType = expectedResponse.headers['Content-Type'];

    var layergroupid = null;

    step(
        function requestLayergroup() {
            var next = this;
            var request = {
                url: '/database/windshaft_test/layergroup',
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify(layergroupConfig)
            };
            var expectedResponse = {
                status: 200,
                headers: {
                    'Content-Type': 'application/json; charset=utf-8'
                }
            };
            assert.response(server, request, expectedResponse, function (res, err) {
                next(err, res);
            });
        },
        function validateLayergroup(err, res) {
            assert.ok(!err, 'Failed to create layergroup');

            var parsedBody = JSON.parse(res.body);
            layergroupid = parsedBody.layergroupid;

            assert.ok(layergroupid);

            return res;
        },
        function requestTile(err, res) {
            assert.ok(!err, 'Invalid layergroup response: ' + res.body);

            var next = this;

            var finalUrl = '/database/windshaft_test/layergroup/' + _.template(url, {
                layergroupid: layergroupid
            });

            var request = {
                url: finalUrl,
                method: 'GET'
            };

            if (contentType === pngContentType) {
                request.encoding = 'binary';
            }

            assert.response(server, request, expectedResponse, function (res, err) {
                next(err, res);
            });
        },
        function validateTile(err, res) {
            assert.ok(!err, 'Failed to get tile');
            var redisKey = 'map_cfg|' + layergroupid;

            var img;
            if (contentType === pngContentType) {
                img = new mapnik.Image.fromBytesSync(new Buffer(res.body, 'binary'));
            }

            redisClient.del(redisKey, function (/*delErr*/) {
                return callback(err, res, img);
            });
        }
    );
}

function withLayergroup(layergroupConfig, options, callback) {
    var validationLayergroupFn = function() {};
    if (!callback) {
        callback = options;
        options = {};
    }

    if (_.isFunction(options)) {
        validationLayergroupFn = options;
        options = {};
    }

    var layergroupExpectedResponse = {
        status: 200,
        headers: {
            'Content-Type': 'application/json; charset=utf-8'
        }
    };

    step(
        function requestLayergroup() {
            var next = this;
            var request = layergroupRequest(layergroupConfig, 'POST');
            assert.response(serverInstance(options), request, layergroupExpectedResponse, function (res, err) {
                next(err, res);
            });
        },
        function validateLayergroup(err, res) {
            assert.ok(!err, 'Failed to request layergroup');

            var parsedBody = JSON.parse(res.body);
            var layergroupid = parsedBody.layergroupid;

            assert.ok(layergroupid, 'No layergroup was created');

            validationLayergroupFn(res);

            function requestTile(layergroupUrl, options, callback) {
                if (!callback) {
                    callback = options;
                    options = {
                        statusCode: 200,
                        contentType: pngContentType
                    };
                }

                var baseUrlTpl = '/database/windshaft_test/layergroup/<%= layergroupid %>';
                var finalUrl = _.template(baseUrlTpl, { layergroupid: layergroupid }) + layergroupUrl;

                var request = {
                    url: finalUrl,
                    method: 'GET'
                };

                if (options.contentType === pngContentType) {
                    request.encoding = 'binary';
                }

                var tileExpectedResponse = {
                    status: options.statusCode || 200,
                    headers: {
                        'Content-Type': options.contentType || pngContentType
                    }
                };

                assert.response(serverInstance(options), request, tileExpectedResponse, function (res, err) {
                    callback(err, res);
                });
            }

            function finish(done) {
                var redisKey = 'map_cfg|' + layergroupid;
                redisClient.del(redisKey, function (delErr) {
                    return done(delErr);
                });
            }

            return callback(err, requestTile, finish);
        }
    );
}
<|endoftext|>