<commit_before>'use strict'

class ElasticSlider {
    constructor(el, options) {

        // Public properties
        // ====================================================================
        this.NAMESPACE = 'ElasticSlider';
        this.options = this._filterOptions(options);
        this.CLASS_NAME_LIST = {
            'container': `${this.NAMESPACE}-container`,
            'item': `${this.NAMESPACE}-item`,
            'itemActive': `${this.NAMESPACE}-item--isActive`,
            'itemClone': `${this.NAMESPACE}-item--clone`,
        }
        this._properties = new Map();

        // Class requirement pass check
        // ====================================================================
        this._checkClassRequirements(el);

        // Private properties
        // ====================================================================
        this._animationFunctionHash = {};
        this._slideCount = null;
        this._initialAnimationDelay = 50; // ms in which animation begins
        this._startAnimationUserCallback = () => {};
        this._endAnimationUserCallback = () => {};

        // Create animations
        // ====================================================================
        this._createAnimationFunctions();

        // Elements
        // ====================================================================
        this.elementList = {};
        this.elementList.slider = el;
        this.elementList.containerEl = this.elementList.slider.querySelector(`.${this.CLASS_NAME_LIST.container}`);
        this.elementList.slideArr = this.elementList.containerEl.children;
        this.elementList.slideActiveEl = this.elementList.slideArr[this.options.activeSlide - 1];
        this.elementList.cloneEl = null;
        this._slideCount = this.elementList.slideArr.length;

        // Run methods
        // ====================================================================
        this.setProp('activeSlideIndex', this.options.activeSlide - 1);
        this.setProp('nextActiveSlideIndex', this.options.activeSlide);
        this.setProp('isAnimating', false);

        // DOM manipulation
        // ====================================================================
        this.elementList.slider.classList.add(this.NAMESPACE);

        for (let i = 0; i < this.elementList.slideArr.length; i++) {
            let itemEl = this.elementList.slideArr[i]

            itemEl.classList.add(this.CLASS_NAME_LIST.item);
        }

        this._setActiveSlide(this.getProp('activeSlideIndex'));

        this._addCustomAnimationFunctions();
    }

    // ========================================================================
    // Public methods
    // ========================================================================
    // May be useful in future. No events to remove.
    destroy() {
        for (let k in this.CLASS_NAME_LIST) {
            let element = document.querySelector(`.${this.CLASS_NAME_LIST[k]}`);

            if (element) {
                element.classList.remove(`${this.CLASS_NAME_LIST[k]}`);
            }
        }

        this.elementList.slider.classList.remove(this.NAMESPACE);
    }

    toSlide(params = {}) {
        let {
            index,
            animation,
            startAnimationCallback,
            endAnimationCallback
        } = params;

        // `animation` fallback
        if (!this._typeTest('string', animation)) {
            animation = this.options.animation;
        }

        // Don't animate to the same slide
        // Don't animate while animating
        if (
            index === this.getProp('activeSlideIndex')
            || this.getProp('isAnimating') === true
        ) {
            return;
        };

        // Handle invalid index values
        if (!this._typeTest('number', index) || index > this._slideCount - 1) {
            index = 0;
        } else if (index < 0) {
            index = this._slideCount - 1;
        };

        this._createTransitionEl(index);
        this.setProp('nextActiveSlideIndex', index);
        this.setProp('isAnimating', true);

        // Set callback animations
        if (this._typeTest('function', startAnimationCallback)) {
            this._startAnimationUserCallback = startAnimationCallback;
        }

        if (this._typeTest('function', endAnimationCallback)) {
            this._endAnimationUserCallback = endAnimationCallback;
        }

        if (this._typeTest('string', animation)) {
            this._startAnimationUserCallback();
            this._animationFunctionHash[animation](this, index + 1)
        } else {
            this._endSlide(index);
        }
    }

    animationInit(cb) {
        if (this._typeTest('function', cb)) {
            // Run the method in context of the class
            cb = cb.bind(this);
            cb();
        }
    }

    animationStart(cb) {
        let self = this;

        // Defer
        window.setTimeout(() => {
            if (this._typeTest('function', cb)) {
                // Run the method in context of the class
                cb = cb.bind(self);
                cb();
            }
        }, this._initialAnimationDelay);
    }

    animationEnd(duration = 100, cb) {
        let self = this;
        let totalDuration = (duration) + this._initialAnimationDelay;

        window.setTimeout(() => {
            if (this._typeTest('function', cb)) {
                // Run the method in context of the class
                cb = cb.bind(self);
                cb();
            }

            self._endSlide();
        }, totalDuration);
    }

    addAnimationFunction(name, func) {
        if (this._typeTest('string', name) && this._typeTest('function', func)) {
            this._animationFunctionHash[name] = func.bind(this);
        } else {
            console.error('ElasticSlider.addAnimationFunction: Parameters must be name<String>, func<Function>');
        }
    }

    getElement(elName) {
        return this.elementList[elName];
    }

    getProp(propName) {
        return this._properties.get(propName);
    }

    setProp(propName, val) {
        this._properties.set(propName, val);
    }

    removeProp(propName, val) {
        this._properties.delete(propName);
    }

    // ========================================================================
    // Private methods
    // ========================================================================
    _filterOptions(o = {}) {
        if (!this._typeTest('number', o.activeSlide)) o.activeSlide = 1;
        if (!this._typeTest('string', o.animation)) o.animation = 'slide';

        return o;
    }

    _checkClassRequirements(el) {
        let requiredElementsArr = [
            this.CLASS_NAME_LIST.container
        ];

        requiredElementsArr.forEach((item) => {
            let element = el.querySelector(`.${item}`);

            if (!element) {
                throw `Required element .${item} missing. Please use an ElasticSlider UI plugin or read the documentation.`;
            }
        });
    }

    _endSlide() {
        this._setActiveSlide();
        this.setProp('isAnimating', false);

        // Remove clone
        this.elementList.containerEl.removeChild(this.elementList.cloneEl);
        this.elementList.cloneEl = null;

        // `toSlide` callback
        this._endAnimationUserCallback();

        // Reset callbacks
        this._startAnimationUserCallback = () => {};
        this._endAnimationUserCallback = () => {};
    }

    _setActiveSlide(index = this.getProp('nextActiveSlideIndex')) {
        // Remove the active class name from all elements
        for (let i = 0; i < this.elementList.slideArr.length; i++) {
            let slide = this.elementList.slideArr[i];

            slide.classList.remove(this.CLASS_NAME_LIST.itemActive);
        }

        this.elementList.slideActiveEl = this.elementList.slideArr[index];
        this.elementList.slideActiveEl.classList.add(this.CLASS_NAME_LIST.itemActive);
        // Set active index and remove target item active index
        this.setProp('activeSlideIndex', index);
        this.setProp('nextActiveSlideIndex', null);
    }

    _createTransitionEl(index) {
        if (this.elementList.cloneEl) {
            this.elementList.containerEl.removeChild(this.elementList.cloneEl);
        }

        // Create el
        this.elementList.cloneEl = this.elementList.slideArr[index].cloneNode(true);
        this.elementList.cloneEl.classList.add(this.CLASS_NAME_LIST.itemClone)

        // Add to dom
        this.elementList.containerEl.appendChild(this.elementList.cloneEl);
    }

    // Animations. These are the default animations. Any extra animations
    // should be added via the external add method
    _createAnimationFunctions() {
        this.addAnimationFunction('fade', () =>  {
            this.animationInit(() => {
                this.elementList.cloneEl.classList.add(`${this.NAMESPACE}-item--animateFadeInit`);
            });

            this.animationStart(() => {
                this.elementList.cloneEl.classList.add(`${this.NAMESPACE}-item--animateFadeStart`);
            });

            this.animationEnd(600);
        });

        this.addAnimationFunction('slide', () => {
            let direction = null; // Determines which direction to slide
            let animationDirection = this.getProp('animationDirection');
            let nextActiveSlideIndex = this.getProp('nextActiveSlideIndex')
            let activeSlideIndex = this.getProp('activeSlideIndex')

            // If an explicit direction has been set
            if (animationDirection) {
                direction = animationDirection;
            }
            // Otherwise greater index means next
            else if (nextActiveSlideIndex > activeSlideIndex) {
                direction = 'Next';
            }
            else {
                direction = 'Prev';
            };

            this.animationInit(() => {
                this.elementList.cloneEl.classList.add(
                    `${this.NAMESPACE}-item--animate${direction}SlideInit`
                );
                this.elementList.slideActiveEl.classList.add(
                    `${this.NAMESPACE}-item--animateActive${direction}SlideInit`
                );
            });

            this.animationStart(() => {
                this.elementList.cloneEl.classList.add(
                    `${this.NAMESPACE}-item--animate${direction}SlideStart`
                );
                this.elementList.slideActiveEl.classList.add(
                    `${this.NAMESPACE}-item--animateActive${direction}SlideStart`
                );
            });

            this.animationEnd(1000, () => {
                this.elementList.slideActiveEl.classList.remove(
                    `${this.NAMESPACE}-item--animateActive${direction}SlideInit`
                );
                this.elementList.slideActiveEl.classList.remove(
                    `${this.NAMESPACE}-item--animateActive${direction}SlideStart`
                );

                // Don't allow this to be cached permanently
                this.removeProp('animationDirection');
            });
        });
    }

    // Allows for animations functions to be inserted from an external source
    _addCustomAnimationFunctions() {
        let customAnimationMap = window.elasticSliderAnimationMap;

        if (this._typeTest('object', customAnimationMap)) {
            for (let name in customAnimationMap) {
                this.addAnimationFunction(name, customAnimationMap[name]);
            }
        }
    }

    _typeTest(type, item) {
        let pass = false;

        switch (type) {
            case 'object':
                if (
                    typeof item === 'object' &&
                    item !== null &&
                    typeof item.length === 'undefined'
                ) {

                    pass = true;

                }
                break;

            case 'array':
                if (
                    typeof item === 'object' &&
                    item !== null &&
                    typeof item.length === 'number'
                ) {
                    pass = true;
                }
                break;

            case 'function':
                if (typeof item === 'function') {
                    pass = true;
                }

            case 'string':
                if (typeof item === 'string') {
                    pass = true;
                }
                break;

            case 'boolean':
                if (typeof item === 'boolean') {
                    pass = true;
                }
                break;

            case 'number':
                if (typeof item === 'number' && !isNaN(item)) {
                    pass = true;
                }
                break;
        }

        return pass;
    }
}
<commit_msg>Add a toSlide fallback<commit_after>'use strict'

class ElasticSlider {
    constructor(el, options) {

        // Public properties
        // ====================================================================
        this.NAMESPACE = 'ElasticSlider';
        this.options = this._filterOptions(options);
        this.CLASS_NAME_LIST = {
            'container': `${this.NAMESPACE}-container`,
            'item': `${this.NAMESPACE}-item`,
            'itemActive': `${this.NAMESPACE}-item--isActive`,
            'itemClone': `${this.NAMESPACE}-item--clone`,
        }
        this._properties = new Map();

        // Class requirement pass check
        // ====================================================================
        this._checkClassRequirements(el);

        // Private properties
        // ====================================================================
        this._animationFunctionHash = {};
        this._slideCount = null;
        this._initialAnimationDelay = 50; // ms in which animation begins
        this._startAnimationUserCallback = () => {};
        this._endAnimationUserCallback = () => {};

        // Create animations
        // ====================================================================
        this._createAnimationFunctions();

        // Elements
        // ====================================================================
        this.elementList = {};
        this.elementList.slider = el;
        this.elementList.containerEl = this.elementList.slider.querySelector(`.${this.CLASS_NAME_LIST.container}`);
        this.elementList.slideArr = this.elementList.containerEl.children;
        this.elementList.slideActiveEl = this.elementList.slideArr[this.options.activeSlide - 1];
        this.elementList.cloneEl = null;
        this._slideCount = this.elementList.slideArr.length;

        // Run methods
        // ====================================================================
        this.setProp('activeSlideIndex', this.options.activeSlide - 1);
        this.setProp('nextActiveSlideIndex', this.options.activeSlide);
        this.setProp('isAnimating', false);

        // DOM manipulation
        // ====================================================================
        this.elementList.slider.classList.add(this.NAMESPACE);

        for (let i = 0; i < this.elementList.slideArr.length; i++) {
            let itemEl = this.elementList.slideArr[i]

            itemEl.classList.add(this.CLASS_NAME_LIST.item);
        }

        this._setActiveSlide(this.getProp('activeSlideIndex'));

        this._addCustomAnimationFunctions();
    }

    // ========================================================================
    // Public methods
    // ========================================================================
    // May be useful in future. No events to remove.
    destroy() {
        for (let k in this.CLASS_NAME_LIST) {
            let element = document.querySelector(`.${this.CLASS_NAME_LIST[k]}`);

            if (element) {
                element.classList.remove(`${this.CLASS_NAME_LIST[k]}`);
            }
        }

        this.elementList.slider.classList.remove(this.NAMESPACE);
    }

    toSlide(params = {}) {
        let {
            index,
            animation,
            startAnimationCallback,
            endAnimationCallback
        } = params;

        // Fallback
        // Allow for `params` to be a number.
        if (this._typeTest('number', params)) {
            params = { index: params };
            index = params.index;
        }
        else if (!this._typeTest('object', params)) {
            console.error('ElasticSlider.toSlide: params is expected to be an object. Eg. {index: 5}');
        }

        // `animation` fallback
        if (!this._typeTest('string', animation)) {
            animation = this.options.animation;
        }

        // Don't animate to the same slide
        // Don't animate while animating
        if (
            index === this.getProp('activeSlideIndex')
            || this.getProp('isAnimating') === true
        ) {
            return;
        };

        // Handle invalid index values
        if (!this._typeTest('number', index) || index > this._slideCount - 1) {
            index = 0;
        } else if (index < 0) {
            index = this._slideCount - 1;
        };

        this._createTransitionEl(index);
        this.setProp('nextActiveSlideIndex', index);
        this.setProp('isAnimating', true);

        // Set callback animations
        if (this._typeTest('function', startAnimationCallback)) {
            this._startAnimationUserCallback = startAnimationCallback;
        }

        if (this._typeTest('function', endAnimationCallback)) {
            this._endAnimationUserCallback = endAnimationCallback;
        }

        if (this._typeTest('string', animation)) {
            this._startAnimationUserCallback();
            this._animationFunctionHash[animation](this, index + 1)
        } else {
            this._endSlide(index);
        }
    }

    animationInit(cb) {
        if (this._typeTest('function', cb)) {
            // Run the method in context of the class
            cb = cb.bind(this);
            cb();
        }
    }

    animationStart(cb) {
        let self = this;

        // Defer
        window.setTimeout(() => {
            if (this._typeTest('function', cb)) {
                // Run the method in context of the class
                cb = cb.bind(self);
                cb();
            }
        }, this._initialAnimationDelay);
    }

    animationEnd(duration = 100, cb) {
        let self = this;
        let totalDuration = (duration) + this._initialAnimationDelay;

        window.setTimeout(() => {
            if (this._typeTest('function', cb)) {
                // Run the method in context of the class
                cb = cb.bind(self);
                cb();
            }

            self._endSlide();
        }, totalDuration);
    }

    addAnimationFunction(name, func) {
        if (this._typeTest('string', name) && this._typeTest('function', func)) {
            this._animationFunctionHash[name] = func.bind(this);
        } else {
            console.error('ElasticSlider.addAnimationFunction: Parameters must be name<String>, func<Function>');
        }
    }

    getElement(elName) {
        return this.elementList[elName];
    }

    getProp(propName) {
        return this._properties.get(propName);
    }

    setProp(propName, val) {
        this._properties.set(propName, val);
    }

    removeProp(propName, val) {
        this._properties.delete(propName);
    }

    // ========================================================================
    // Private methods
    // ========================================================================
    _filterOptions(o = {}) {
        if (!this._typeTest('number', o.activeSlide)) o.activeSlide = 1;
        if (!this._typeTest('string', o.animation)) o.animation = 'slide';

        return o;
    }

    _checkClassRequirements(el) {
        let requiredElementsArr = [
            this.CLASS_NAME_LIST.container
        ];

        requiredElementsArr.forEach((item) => {
            let element = el.querySelector(`.${item}`);

            if (!element) {
                throw `Required element .${item} missing. Please use an ElasticSlider UI plugin or read the documentation.`;
            }
        });
    }

    _endSlide() {
        this._setActiveSlide();
        this.setProp('isAnimating', false);

        // Remove clone
        this.elementList.containerEl.removeChild(this.elementList.cloneEl);
        this.elementList.cloneEl = null;

        // `toSlide` callback
        this._endAnimationUserCallback();

        // Reset callbacks
        this._startAnimationUserCallback = () => {};
        this._endAnimationUserCallback = () => {};
    }

    _setActiveSlide(index = this.getProp('nextActiveSlideIndex')) {
        // Remove the active class name from all elements
        for (let i = 0; i < this.elementList.slideArr.length; i++) {
            let slide = this.elementList.slideArr[i];

            slide.classList.remove(this.CLASS_NAME_LIST.itemActive);
        }

        this.elementList.slideActiveEl = this.elementList.slideArr[index];
        this.elementList.slideActiveEl.classList.add(this.CLASS_NAME_LIST.itemActive);
        // Set active index and remove target item active index
        this.setProp('activeSlideIndex', index);
        this.setProp('nextActiveSlideIndex', null);
    }

    _createTransitionEl(index) {
        if (this.elementList.cloneEl) {
            this.elementList.containerEl.removeChild(this.elementList.cloneEl);
        }

        // Create el
        this.elementList.cloneEl = this.elementList.slideArr[index].cloneNode(true);
        this.elementList.cloneEl.classList.add(this.CLASS_NAME_LIST.itemClone)

        // Add to dom
        this.elementList.containerEl.appendChild(this.elementList.cloneEl);
    }

    // Animations. These are the default animations. Any extra animations
    // should be added via the external add method
    _createAnimationFunctions() {
        this.addAnimationFunction('fade', () =>  {
            this.animationInit(() => {
                this.elementList.cloneEl.classList.add(`${this.NAMESPACE}-item--animateFadeInit`);
            });

            this.animationStart(() => {
                this.elementList.cloneEl.classList.add(`${this.NAMESPACE}-item--animateFadeStart`);
            });

            this.animationEnd(600);
        });

        this.addAnimationFunction('slide', () => {
            let direction = null; // Determines which direction to slide
            let animationDirection = this.getProp('animationDirection');
            let nextActiveSlideIndex = this.getProp('nextActiveSlideIndex')
            let activeSlideIndex = this.getProp('activeSlideIndex')

            // If an explicit direction has been set
            if (animationDirection) {
                direction = animationDirection;
            }
            // Otherwise greater index means next
            else if (nextActiveSlideIndex > activeSlideIndex) {
                direction = 'Next';
            }
            else {
                direction = 'Prev';
            };

            this.animationInit(() => {
                this.elementList.cloneEl.classList.add(
                    `${this.NAMESPACE}-item--animate${direction}SlideInit`
                );
                this.elementList.slideActiveEl.classList.add(
                    `${this.NAMESPACE}-item--animateActive${direction}SlideInit`
                );
            });

            this.animationStart(() => {
                this.elementList.cloneEl.classList.add(
                    `${this.NAMESPACE}-item--animate${direction}SlideStart`
                );
                this.elementList.slideActiveEl.classList.add(
                    `${this.NAMESPACE}-item--animateActive${direction}SlideStart`
                );
            });

            this.animationEnd(1000, () => {
                this.elementList.slideActiveEl.classList.remove(
                    `${this.NAMESPACE}-item--animateActive${direction}SlideInit`
                );
                this.elementList.slideActiveEl.classList.remove(
                    `${this.NAMESPACE}-item--animateActive${direction}SlideStart`
                );

                // Don't allow this to be cached permanently
                this.removeProp('animationDirection');
            });
        });
    }

    // Allows for animations functions to be inserted from an external source
    _addCustomAnimationFunctions() {
        let customAnimationMap = window.elasticSliderAnimationMap;

        if (this._typeTest('object', customAnimationMap)) {
            for (let name in customAnimationMap) {
                this.addAnimationFunction(name, customAnimationMap[name]);
            }
        }
    }

    _typeTest(type, item) {
        let pass = false;

        switch (type) {
            case 'object':
                if (
                    typeof item === 'object' &&
                    item !== null &&
                    typeof item.length === 'undefined'
                ) {

                    pass = true;

                }
                break;

            case 'array':
                if (
                    typeof item === 'object' &&
                    item !== null &&
                    typeof item.length === 'number'
                ) {
                    pass = true;
                }
                break;

            case 'function':
                if (typeof item === 'function') {
                    pass = true;
                }

            case 'string':
                if (typeof item === 'string') {
                    pass = true;
                }
                break;

            case 'boolean':
                if (typeof item === 'boolean') {
                    pass = true;
                }
                break;

            case 'number':
                if (typeof item === 'number' && !isNaN(item)) {
                    pass = true;
                }
                break;
        }

        return pass;
    }
}
<|endoftext|>


<commit_before>'use strict'

Creep.getRole = function (roleName) {
  let Role = require('roles_' + roleName)
  return new Role()
}

Creep.getRoleFromName = function (creepname) {
  return Creep.getRole(creepname.split('_', 1)[0])
}

Creep.prototype.getRole = function () {
  // If the creep role isn't in memory grab it based off of the name
  let roleType = this.memory.role ? this.memory.role : this.name.split('_', 1)[0]
  return Creep.getRole(roleType)
}

Creep.getCost = function (parts) {
  if (typeof parts === 'string') {
    if (BODYPART_COST[parts]) {
      return BODYPART_COST[parts]
    }
  }
  let cost = 0
  _.forEach(parts, function (part) {
    cost += BODYPART_COST[part]
  })
  return cost
}

Creep.buildFromTemplate = function (template, energy) {
  let parts = []
  while (energy > 0 && parts.length < 50) {
    let next = template[parts.length % template.length]
    if (BODYPART_COST[next] > energy) {
      break
    }
    energy -= BODYPART_COST[next]
    parts.push(next)
  }
  return parts
}
<commit_msg>Creep.prototype.getCarryPercentage<commit_after>'use strict'

Creep.getRole = function (roleName) {
  let Role = require('roles_' + roleName)
  return new Role()
}

Creep.getRoleFromName = function (creepname) {
  return Creep.getRole(creepname.split('_', 1)[0])
}

Creep.prototype.getRole = function () {
  // If the creep role isn't in memory grab it based off of the name
  let roleType = this.memory.role ? this.memory.role : this.name.split('_', 1)[0]
  return Creep.getRole(roleType)
}

Creep.getCost = function (parts) {
  if (typeof parts === 'string') {
    if (BODYPART_COST[parts]) {
      return BODYPART_COST[parts]
    }
  }
  let cost = 0
  _.forEach(parts, function (part) {
    cost += BODYPART_COST[part]
  })
  return cost
}

Creep.buildFromTemplate = function (template, energy) {
  let parts = []
  while (energy > 0 && parts.length < 50) {
    let next = template[parts.length % template.length]
    if (BODYPART_COST[next] > energy) {
      break
    }
    energy -= BODYPART_COST[next]
    parts.push(next)
  }
  return parts
}

Creep.prototype.getCarryPercentage = function () {
  if (this.carryCapacity <= 0) {
    return 0
  }
  return _.sum(this.carry) / this.carryCapacity
}
<|endoftext|>


<commit_before>// TODO support more airlines - EasyJet
// TODO support request cache

const ryanairHandler = require("./ryanairHandler");
const wizzAirHandler = require("./wizzAirHandler");

module.exports = function findFlights (body) {
	const airportsFrom = body.airportsFrom
	const airportsTo = body.airportsTo
	const minDate = new Date(body.minDate)
	const maxDate = new Date(body.maxDate)
	const allowedDaysThere = body.allowedDaysThere
	const allowedDaysBack = body.allowedDaysBack
	const spanInDaysMin = body.spanInDaysMin
	const spanInDaysMax = body.spanInDaysMax

	const dates = getDatesBetween(minDate, maxDate);
	return fetchAllFlights(airportsFrom, airportsTo, dates)
		.then(function (flights) {
			const ryanairFlights = mergeFlights(flights[0]).map(ryanairHandler.parse);
			const wizzAirFlights = mergeFlights(flights[1]).map(wizzAirHandler.parse);

			var flightsThere = ryanairFlights[0].concat(wizzAirFlights[0])
			flightsThere = filterFlights(flightsThere, minDate, maxDate, allowedDaysThere);

			var flightsBack = ryanairFlights[1].concat(wizzAirFlights[1]);
			flightsBack = filterFlights(flightsBack, minDate, maxDate, allowedDaysBack);

			const flightsPairs = [];
			flightsThere.forEach(function (flightThere) {
				flightsBack.forEach(function (flightBack) {
					const spanInDays = getSpanInDays(flightThere, flightBack)
					if (spanInDaysMax >= spanInDays && spanInDays >= spanInDaysMin) {
						flightsPairs.push({
							price: (flightThere.price + flightBack.price).toFixed(2),
							spanInDays: spanInDays,
							there: flightThere,
							back: flightBack
						});
					}
				});
			});

			return {
				count: flightsPairs.length,
				flights: flightsPairs
					.sort(function (flights1, flights2) {
						return flights1.price - flights2.price;
					})
					.slice(0, 10)
			}
		})
}

function getDatesBetween (minDate, maxDate) {
	const dates = [];
	const date = new Date(minDate);
	date.setUTCDate(1);
	do {
		dates.push(new Date(date));
		date.setUTCMonth(date.getUTCMonth() + 1);
	} while (date <= maxDate)
	return dates;
}

function fetchAllFlights (airportsFrom, airportsTo, dates) {
	const ryanairFlightsFetches = [];
	const wizzAirFlightsFetches = [];
	airportsFrom.forEach(function (airportFrom) {
		airportsTo.forEach(function (airportTo) {
			dates.forEach(function (date) {
				ryanairFlightsFetches.push(ryanairHandler.fetch(airportFrom, airportTo, date));
				wizzAirFlightsFetches.push(wizzAirHandler.fetch(airportFrom, airportTo, date));
			})
		})
	})

	return Promise.all([
			Promise.all(ryanairFlightsFetches),
			Promise.all(wizzAirFlightsFetches)
		]);
}

function mergeFlights (flights) {
	var outboundFlights = flights.reduce(function (result, next) {
		return result.concat(next.outbound);
	}, []);
	var inboundFlights = flights.reduce(function (result, next) {
		return result.concat(next.inbound);
	}, []);
	return [outboundFlights, inboundFlights]
}

function filterFlights (flights, minDate, maxDate, allowedDays) {
	return flights
		.filter(function (flight) {
			return (maxDate >= flight.date) && (flight.date >= minDate) && (allowedDays.indexOf(flight.date.getUTCDay()) >= 0);
		});
}

function getSpanInDays (flight1, flight2) {
	return (flight2.date.getTime() - flight1.date.getTime()) / (24 * 60 * 60 * 1000);
}
<commit_msg>Use currencyRatio to calculate total price<commit_after>// TODO support more airlines - EasyJet
// TODO support request cache

const ryanairHandler = require("./ryanairHandler");
const wizzAirHandler = require("./wizzAirHandler");

module.exports = function findFlights (body) {
	const airportsFrom = body.airportsFrom
	const airportsTo = body.airportsTo
	const minDate = new Date(body.minDate)
	const maxDate = new Date(body.maxDate)
	const allowedDaysThere = body.allowedDaysThere
	const allowedDaysBack = body.allowedDaysBack
	const spanInDaysMin = body.spanInDaysMin
	const spanInDaysMax = body.spanInDaysMax
	const currencyRatio = 1

	const dates = getDatesBetween(minDate, maxDate);
	return fetchAllFlights(airportsFrom, airportsTo, dates)
		.then(function (flights) {
			const ryanairFlights = mergeFlights(flights[0]).map(ryanairHandler.parse);
			const wizzAirFlights = mergeFlights(flights[1]).map(wizzAirHandler.parse);

			var flightsThere = ryanairFlights[0].concat(wizzAirFlights[0])
			flightsThere = filterFlights(flightsThere, minDate, maxDate, allowedDaysThere);

			var flightsBack = ryanairFlights[1].concat(wizzAirFlights[1]);
			flightsBack = filterFlights(flightsBack, minDate, maxDate, allowedDaysBack);

			const flightsPairs = [];
			flightsThere.forEach(function (flightThere) {
				flightsBack.forEach(function (flightBack) {
					const spanInDays = getSpanInDays(flightThere, flightBack)
					if (spanInDaysMax >= spanInDays && spanInDays >= spanInDaysMin) {
						flightsPairs.push({
							price: +((flightThere.price + (flightBack.price * currencyRatio)).toFixed(2)),
							spanInDays: spanInDays,
							there: flightThere,
							back: flightBack
						});
					}
				});
			});

			return {
				count: flightsPairs.length,
				flights: flightsPairs
					.sort(function (flights1, flights2) {
						return flights1.price - flights2.price;
					})
					.slice(0, 10)
			}
		})
}

function getDatesBetween (minDate, maxDate) {
	const dates = [];
	const date = new Date(minDate);
	date.setUTCDate(1);
	do {
		dates.push(new Date(date));
		date.setUTCMonth(date.getUTCMonth() + 1);
	} while (date <= maxDate)
	return dates;
}

function fetchAllFlights (airportsFrom, airportsTo, dates) {
	const ryanairFlightsFetches = [];
	const wizzAirFlightsFetches = [];
	airportsFrom.forEach(function (airportFrom) {
		airportsTo.forEach(function (airportTo) {
			dates.forEach(function (date) {
				ryanairFlightsFetches.push(ryanairHandler.fetch(airportFrom, airportTo, date));
				wizzAirFlightsFetches.push(wizzAirHandler.fetch(airportFrom, airportTo, date));
			})
		})
	})

	return Promise.all([
			Promise.all(ryanairFlightsFetches),
			Promise.all(wizzAirFlightsFetches)
		]);
}

function mergeFlights (flights) {
	var outboundFlights = flights.reduce(function (result, next) {
		return result.concat(next.outbound);
	}, []);
	var inboundFlights = flights.reduce(function (result, next) {
		return result.concat(next.inbound);
	}, []);
	return [outboundFlights, inboundFlights]
}

function filterFlights (flights, minDate, maxDate, allowedDays) {
	return flights
		.filter(function (flight) {
			return (maxDate >= flight.date) && (flight.date >= minDate) && (allowedDays.indexOf(flight.date.getUTCDay()) >= 0);
		});
}

function getSpanInDays (flight1, flight2) {
	return (flight2.date.getTime() - flight1.date.getTime()) / (24 * 60 * 60 * 1000);
}
<|endoftext|>


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

  var uiSelectModule = angular.module('fng.uiSelect', ['ui.select']);

  uiSelectModule.factory('uiSelectHelper', ['$rootScope', '$q', 'SubmissionsService', function ($rootScope, $q, SubmissionsService) {
    var lastW, lastH;
    var localLookups = {};
    return {
      windowChanged: function (w, h) {
        var result = false;
        if (w !== lastW || h !== lastH) {
          lastW = w;
          lastH = h;
          result = true;
        }
        return result;
      },
      addClientLookup: function (lkpName, lkpData) {
        localLookups[lkpName] = lkpData;
      },
      lookupFunc: function (value, formSchema, cb) {
        if (formSchema.array) {
          // TODO extend back end to do multiple lookups in one hit

          var promises = [];
          var results = [];
          angular.forEach(value, function (obj) {
            promises.push(SubmissionsService.getListAttributes(formSchema.ref, obj.x));
          });
          $q.all(promises).then(function (responses) {
            angular.forEach(responses, function (response) {
              results.push({x: {id: value.shift().x, text: response.data.list}});
            });
            cb(formSchema, results);
            setTimeout(function () {
              $rootScope.$digest();
            });
          });
        } else if (formSchema.fngUiSelect.deriveOptions) {
          var retVal;
          if (typeof value === 'string') {
            var obj = localLookups[formSchema.fngUiSelect.deriveOptions].find(function (test) {
              return test.id == value
            });
            retVal = {id: value, text: obj ? obj.text : ''}
          } else {
            retVal = value;
          }
          cb(formSchema, retVal);
        } else {
          SubmissionsService.getListAttributes(formSchema.ref, value).then(function (response) {
            cb(formSchema, {id: value, text: response.data.list});
          });
        }
      }
    };
  }]);

  uiSelectModule.controller('FngUISelectCtrl', ['$scope', '$window', '$timeout', '$http', 'uiSelectHelper', function ($scope, $window, $timeout, $http, uiSelectHelper) {

    $scope.uiSelect = $scope.uiSelect || [];

    $scope.windowResizeUiSelect = function (checkResize) {
      // This is to stop the resize firing too often.  Really horrible way of doing it caused by some very regrettable design choices which will be factored out later
      // Actually not choices - just not knowing any better
      if (!checkResize || uiSelectHelper.windowChanged($window.innerWidth, $window.innerHeight)) {
        for (var i = 0; i < $scope.uiSelect.length; i++) {
          var selectId = $scope.uiSelect[i].selectId;
          var select = document.getElementById(selectId);
          if (select && select.offsetParent) {
            var helper = document.getElementById(selectId + '_width-helper');
            if (helper) {
              var w = helper.offsetWidth;
              if (select.offsetLeft + w > $window.innerWidth) {
                w = $window.innerWidth - select.offsetLeft - 20;
              }
              angular.element(document.getElementById(selectId)).css('width', w);
            } else {
              console.log('No helper element found for ' + selectId);
            }
          }
        }
      }
    };

    $scope.refreshOptions = function (searchString, id) {
      var elem = _.find($scope.uiSelect, function (elem) {
        return elem.selectId === id
      });
      if (elem) {
        if (searchString.length === 0) {
          $scope[id + '_options'] = [];
        } else {
          var promise;
          if (elem.filter && elem.filter[0] === '/') {
            // HACK We want to pass the existing record in e, but we can fall foul of 431 errors if it is large, so we test for that
            // using an arbitrary number and if so just send the _id (on the assumption that large records won't be new so will have an _id).
            let record = JSON.stringify($scope.record).length > 20000 ? {_id: $scope.record._id} : $scope.record;
            // Custom URL
            promise = $http({
              method: 'GET',
              url: elem.filter,
              params: {
                q: searchString,
                e: record,
                i: $scope.$index
              },
              cache: false
            });
          } else {
            promise = $http.get('/api/search/' + elem.ref + '?q=' + searchString + (elem.filter ? ('&f=' + elem.filter) : ''));
          }
          promise.then(function (response) {
            if (response.status >= 400) {
              throw new Error(response.statusMessage)
            } else {
              if (elem.additional) {
                $scope[id + '_options'] = response.data.results.map(function (result) {
                  result.text = result.text + (result.additional ? (', ' + result.additional) : '');
                  return result;
                });
              } else {
                $scope[id + '_options'] = response.data.results;
              }
            }
          })
          .catch(err => {
            $scope.showError(`Error ${err.status}: ${err.statusText} - ${err.data}`);
          });
        }
      } else {
        throw new Error('Could not find uiSelect element for ' + id);
      }
    };

    angular.element($window).bind('resize', function () {
      $scope.windowResizeUiSelect(true);
      return $scope.$apply();
    });

    $timeout($scope.windowResizeUiSelect);
  }]);

  uiSelectModule.directive('fngUiSelect', ['$compile', '$window', 'pluginHelper', 'cssFrameworkService', 'formMarkupHelper', 'uiSelectHelper',
    function ($compile, $window, pluginHelper, cssFrameworkService, formMarkupHelper, uiSelectHelper) {
      return {
        restrict: 'E',
        controller: 'FngUISelectCtrl',
        link: function (scope, element, attr) {

          function addToConversions(path, options) {
            if (Object.keys(options).length > 0) {
              var keys = path.split('.');
              if (!scope.conversions) {
                  scope.conversions = {};
              }
              var target = scope.conversions;
              for (var i = 0; i < keys.length; i++) {
                var thisKey = keys[i];
                target[thisKey] = target[thisKey] || {};
                target = target[thisKey];
              }
              angular.extend(target, options);
            }
          }

          var processedAttr = pluginHelper.extractFromAttr(attr, 'fngUiSelect');
          var elemScope = angular.extend({selectId: processedAttr.info.id}, processedAttr.directiveOptions);
          var multi = processedAttr.info.array;
          var elementHtml;
          var input = '';

          scope.uiSelect.push(elemScope);
          addToConversions(processedAttr.info.name, processedAttr.directiveOptions);

          // Sort out the theme, defaulting to select2 (so old users won't see the change).  Bootstrap theme only works with Bootstrap 3
          // and not with animate (see https://github.com/angular-ui/ui-select/issues/1731 and others)
          var theme = processedAttr.directiveOptions.theme || 'select2';
          if (theme === 'bootstrap') {
            if (cssFrameworkService.framework() !== 'bs3') {
              theme = 'select2';
            } else {
              try {
                if (angular.module('ngAnimate')) {
                  theme = 'select2';
                }
              } catch(e) {
                ;    // module not present, so bootstrap theme will work
              }
            }
          }

          var multiControl = false;
          var multiStr = '';
          if (multi && (processedAttr.directiveOptions.fngajax || processedAttr.directiveOptions.forcemultiple)) {
            // We need the array to be an array of objects with a x property.  This tells forms-angular to convert it by
            // adding an attribute to the schema.
            pluginHelper.findIdInSchemaAndFlagNeedX(scope.baseSchema(), processedAttr.info.id);
            multiControl = true;
          } else {
            multiStr = multi ? 'multiple close-on-select reset-search-input ' : '';
          }

          var requiredStr = '';
          var allowClearStr = '';
          if (processedAttr.info.required) {
            requiredStr = ' ng-required="true"';
          } else {
            allowClearStr = ' allow-clear';
          }

          // First of all add a hidden input field which we will use to set the width of the select
          if (!angular.element('#' + processedAttr.info.id + '_width-helper').length > 0) {
            var hiddenInputInfo = {
              id: processedAttr.info.id + '_width-helper',
              name: processedAttr.info.name + '_width-helper',
              label: ''
            };
            if (processedAttr.info.size) {
              hiddenInputInfo.size = processedAttr.info.size;
            }
            input = pluginHelper.buildInputMarkup(scope, attr.model, hiddenInputInfo, processedAttr.options, false, multiControl, function (buildingBlocks) {
              return '<input id="' + hiddenInputInfo.id + '" type="text" class="form-control" disabled="" style="position: absolute; left: -4200px;">';
            });
            input = input.replace('class="row', 'class="hidden-row row')
          }

          function optionsFromArray(multiControl, multi, array, arrayGetter) {
            var isObjects = scope[array] && (scope[array].isObjects || typeof scope[array][0] === "object");
            if (isObjects) {
              addToConversions(processedAttr.info.name, {fngajax: uiSelectHelper.lookupFunc});
              uiSelectHelper.addClientLookup(arrayGetter, scope[array]);
            }
            var select = '';
            if (multiControl) {
              select += '{{$select.selected' + (isObjects ? '.text' : '') + '}}';
            } else {
              select += multi ? '{{$item}}' : ('{{$select.selected' + (isObjects ? '.text' : '') + '}}');
            }
            select += '</ui-select-match>';
            select += '<ui-select-choices repeat="option in ' + array + ' | filter:$select.search">';
            select += '

';
            return select;
          }

          elementHtml = pluginHelper.buildInputMarkup(scope, attr.model, processedAttr.info, processedAttr.options, multiControl, multiControl, function (buildingBlocks) {
            var defaultPlaceholder = 'Select an option...';
            if (processedAttr.directiveOptions.fngajax) {
              defaultPlaceholder = 'Start typing...'
            }
            // set up the ui-select directives
            var select = '<ui-select ' + multiStr + buildingBlocks.common + requiredStr + ' theme="' + theme + '" ng-disabled="disabled" style="width:300px;">'
            select += '<ui-select-match' + allowClearStr + ' placeholder="' + (processedAttr.info.placeholder || defaultPlaceholder) + '">';

            if (processedAttr.directiveOptions.fngajax) {
              // Stash any filters
              if (processedAttr.directiveOptions.fngajax !== true) {
                elemScope.filter = processedAttr.directiveOptions.fngajax;
              }
              // Set up lookup function
              addToConversions(processedAttr.info.name, {fngajax: uiSelectHelper.lookupFunc});
              // Use the forms-angular API to query the referenced collection
              elemScope.ref = processedAttr.info.ref;
              scope[processedAttr.info.id + '_options'] = [];
              if (multiControl) {
                select += '{{$select.selected.text}}';
              } else if (processedAttr.options.subschema) {
                select += '{{' + attr.model + '.' + processedAttr.info.name.replace(processedAttr.options.subschemaroot, processedAttr.options.subschemaroot + '[$index]') + '.text}}';
              } else {
                select += '{{' + buildingBlocks.modelString + '.text}}';
              }
              select += '</ui-select-match>';
              select += '<ui-select-choices repeat="option in (' + processedAttr.info.id + '_options) track by $index" ';
              select += 'refresh="refreshOptions($select.search, \'' + processedAttr.info.id + '\')" ';
              select += 'refresh-delay="100"> ';
              select += '

';
            } else if (processedAttr.directiveOptions.deriveoptions) {
              if (typeof scope[processedAttr.directiveOptions.deriveoptions] === "function") {
                select += optionsFromArray(multiControl, multi, scope[processedAttr.directiveOptions.deriveoptions](), processedAttr.directiveOptions.deriveoptions);
              } else {
                throw new Error("In fng-ui-select " + processedAttr.directiveOptions.deriveoptions + " is not a function on the scope");
              }
            } else if (processedAttr.info.options) {
              // Simple case - enumerated options on the form scope
              select += optionsFromArray(multiControl, multi, processedAttr.info.options);
            } else {
              throw new Error('fng-ui-select has no means of populating select');
            }
            select += '</ui-select-choices>';
            select += '</ui-select>';
            return select;
          });
          element.append($compile(input + elementHtml)(scope));
        }
      }
    }]);
})();
<commit_msg>Base the limit of record size sent back on some(one else's) actual knowledge<commit_after>(function () {
  'use strict';

  var uiSelectModule = angular.module('fng.uiSelect', ['ui.select']);

  uiSelectModule.factory('uiSelectHelper', ['$rootScope', '$q', 'SubmissionsService', function ($rootScope, $q, SubmissionsService) {
    var lastW, lastH;
    var localLookups = {};
    return {
      windowChanged: function (w, h) {
        var result = false;
        if (w !== lastW || h !== lastH) {
          lastW = w;
          lastH = h;
          result = true;
        }
        return result;
      },
      addClientLookup: function (lkpName, lkpData) {
        localLookups[lkpName] = lkpData;
      },
      lookupFunc: function (value, formSchema, cb) {
        if (formSchema.array) {
          // TODO extend back end to do multiple lookups in one hit

          var promises = [];
          var results = [];
          angular.forEach(value, function (obj) {
            promises.push(SubmissionsService.getListAttributes(formSchema.ref, obj.x));
          });
          $q.all(promises).then(function (responses) {
            angular.forEach(responses, function (response) {
              results.push({x: {id: value.shift().x, text: response.data.list}});
            });
            cb(formSchema, results);
            setTimeout(function () {
              $rootScope.$digest();
            });
          });
        } else if (formSchema.fngUiSelect.deriveOptions) {
          var retVal;
          if (typeof value === 'string') {
            var obj = localLookups[formSchema.fngUiSelect.deriveOptions].find(function (test) {
              return test.id == value
            });
            retVal = {id: value, text: obj ? obj.text : ''}
          } else {
            retVal = value;
          }
          cb(formSchema, retVal);
        } else {
          SubmissionsService.getListAttributes(formSchema.ref, value).then(function (response) {
            cb(formSchema, {id: value, text: response.data.list});
          });
        }
      }
    };
  }]);

  uiSelectModule.controller('FngUISelectCtrl', ['$scope', '$window', '$timeout', '$http', 'uiSelectHelper', function ($scope, $window, $timeout, $http, uiSelectHelper) {

    $scope.uiSelect = $scope.uiSelect || [];

    $scope.windowResizeUiSelect = function (checkResize) {
      // This is to stop the resize firing too often.  Really horrible way of doing it caused by some very regrettable design choices which will be factored out later
      // Actually not choices - just not knowing any better
      if (!checkResize || uiSelectHelper.windowChanged($window.innerWidth, $window.innerHeight)) {
        for (var i = 0; i < $scope.uiSelect.length; i++) {
          var selectId = $scope.uiSelect[i].selectId;
          var select = document.getElementById(selectId);
          if (select && select.offsetParent) {
            var helper = document.getElementById(selectId + '_width-helper');
            if (helper) {
              var w = helper.offsetWidth;
              if (select.offsetLeft + w > $window.innerWidth) {
                w = $window.innerWidth - select.offsetLeft - 20;
              }
              angular.element(document.getElementById(selectId)).css('width', w);
            } else {
              console.log('No helper element found for ' + selectId);
            }
          }
        }
      }
    };

    $scope.refreshOptions = function (searchString, id) {
      var elem = _.find($scope.uiSelect, function (elem) {
        return elem.selectId === id
      });
      if (elem) {
        if (searchString.length === 0) {
          $scope[id + '_options'] = [];
        } else {
          var promise;
          if (elem.filter && elem.filter[0] === '/') {
            // HACK We want to pass the existing record in e, but we can fall foul of 431 errors if it is large, so we test for that
            // using an arbitrary number and if so just send the _id (on the assumption that large records won't be new so will have an _id).
            // According to https://stackoverflow.com/questions/57431355/how-to-fix-431-request-header-fields-too-large-in-react-redux-app the limit is 8KB
            // And we are grabbing a few bytes for the other stuff
            let record = JSON.stringify($scope.record).length >= (8 * 1024 - 200) ? {_id: $scope.record._id} : $scope.record;
            // Custom URL
            promise = $http({
              method: 'GET',
              url: elem.filter,
              params: {
                q: searchString,
                e: record,
                i: $scope.$index
              },
              cache: false
            });
          } else {
            promise = $http.get('/api/search/' + elem.ref + '?q=' + searchString + (elem.filter ? ('&f=' + elem.filter) : ''));
          }
          promise.then(function (response) {
            if (response.status >= 400) {
              throw new Error(response.statusMessage)
            } else {
              if (elem.additional) {
                $scope[id + '_options'] = response.data.results.map(function (result) {
                  result.text = result.text + (result.additional ? (', ' + result.additional) : '');
                  return result;
                });
              } else {
                $scope[id + '_options'] = response.data.results;
              }
            }
          })
          .catch(err => {
            $scope.showError(`Error ${err.status}: ${err.statusText} - ${err.data}`);
          });
        }
      } else {
        throw new Error('Could not find uiSelect element for ' + id);
      }
    };

    angular.element($window).bind('resize', function () {
      $scope.windowResizeUiSelect(true);
      return $scope.$apply();
    });

    $timeout($scope.windowResizeUiSelect);
  }]);

  uiSelectModule.directive('fngUiSelect', ['$compile', '$window', 'pluginHelper', 'cssFrameworkService', 'formMarkupHelper', 'uiSelectHelper',
    function ($compile, $window, pluginHelper, cssFrameworkService, formMarkupHelper, uiSelectHelper) {
      return {
        restrict: 'E',
        controller: 'FngUISelectCtrl',
        link: function (scope, element, attr) {

          function addToConversions(path, options) {
            if (Object.keys(options).length > 0) {
              var keys = path.split('.');
              if (!scope.conversions) {
                  scope.conversions = {};
              }
              var target = scope.conversions;
              for (var i = 0; i < keys.length; i++) {
                var thisKey = keys[i];
                target[thisKey] = target[thisKey] || {};
                target = target[thisKey];
              }
              angular.extend(target, options);
            }
          }

          var processedAttr = pluginHelper.extractFromAttr(attr, 'fngUiSelect');
          var elemScope = angular.extend({selectId: processedAttr.info.id}, processedAttr.directiveOptions);
          var multi = processedAttr.info.array;
          var elementHtml;
          var input = '';

          scope.uiSelect.push(elemScope);
          addToConversions(processedAttr.info.name, processedAttr.directiveOptions);

          // Sort out the theme, defaulting to select2 (so old users won't see the change).  Bootstrap theme only works with Bootstrap 3
          // and not with animate (see https://github.com/angular-ui/ui-select/issues/1731 and others)
          var theme = processedAttr.directiveOptions.theme || 'select2';
          if (theme === 'bootstrap') {
            if (cssFrameworkService.framework() !== 'bs3') {
              theme = 'select2';
            } else {
              try {
                if (angular.module('ngAnimate')) {
                  theme = 'select2';
                }
              } catch(e) {
                ;    // module not present, so bootstrap theme will work
              }
            }
          }

          var multiControl = false;
          var multiStr = '';
          if (multi && (processedAttr.directiveOptions.fngajax || processedAttr.directiveOptions.forcemultiple)) {
            // We need the array to be an array of objects with a x property.  This tells forms-angular to convert it by
            // adding an attribute to the schema.
            pluginHelper.findIdInSchemaAndFlagNeedX(scope.baseSchema(), processedAttr.info.id);
            multiControl = true;
          } else {
            multiStr = multi ? 'multiple close-on-select reset-search-input ' : '';
          }

          var requiredStr = '';
          var allowClearStr = '';
          if (processedAttr.info.required) {
            requiredStr = ' ng-required="true"';
          } else {
            allowClearStr = ' allow-clear';
          }

          // First of all add a hidden input field which we will use to set the width of the select
          if (!angular.element('#' + processedAttr.info.id + '_width-helper').length > 0) {
            var hiddenInputInfo = {
              id: processedAttr.info.id + '_width-helper',
              name: processedAttr.info.name + '_width-helper',
              label: ''
            };
            if (processedAttr.info.size) {
              hiddenInputInfo.size = processedAttr.info.size;
            }
            input = pluginHelper.buildInputMarkup(scope, attr.model, hiddenInputInfo, processedAttr.options, false, multiControl, function (buildingBlocks) {
              return '<input id="' + hiddenInputInfo.id + '" type="text" class="form-control" disabled="" style="position: absolute; left: -4200px;">';
            });
            input = input.replace('class="row', 'class="hidden-row row')
          }

          function optionsFromArray(multiControl, multi, array, arrayGetter) {
            var isObjects = scope[array] && (scope[array].isObjects || typeof scope[array][0] === "object");
            if (isObjects) {
              addToConversions(processedAttr.info.name, {fngajax: uiSelectHelper.lookupFunc});
              uiSelectHelper.addClientLookup(arrayGetter, scope[array]);
            }
            var select = '';
            if (multiControl) {
              select += '{{$select.selected' + (isObjects ? '.text' : '') + '}}';
            } else {
              select += multi ? '{{$item}}' : ('{{$select.selected' + (isObjects ? '.text' : '') + '}}');
            }
            select += '</ui-select-match>';
            select += '<ui-select-choices repeat="option in ' + array + ' | filter:$select.search">';
            select += '

';
            return select;
          }

          elementHtml = pluginHelper.buildInputMarkup(scope, attr.model, processedAttr.info, processedAttr.options, multiControl, multiControl, function (buildingBlocks) {
            var defaultPlaceholder = 'Select an option...';
            if (processedAttr.directiveOptions.fngajax) {
              defaultPlaceholder = 'Start typing...'
            }
            // set up the ui-select directives
            var select = '<ui-select ' + multiStr + buildingBlocks.common + requiredStr + ' theme="' + theme + '" ng-disabled="disabled" style="width:300px;">'
            select += '<ui-select-match' + allowClearStr + ' placeholder="' + (processedAttr.info.placeholder || defaultPlaceholder) + '">';

            if (processedAttr.directiveOptions.fngajax) {
              // Stash any filters
              if (processedAttr.directiveOptions.fngajax !== true) {
                elemScope.filter = processedAttr.directiveOptions.fngajax;
              }
              // Set up lookup function
              addToConversions(processedAttr.info.name, {fngajax: uiSelectHelper.lookupFunc});
              // Use the forms-angular API to query the referenced collection
              elemScope.ref = processedAttr.info.ref;
              scope[processedAttr.info.id + '_options'] = [];
              if (multiControl) {
                select += '{{$select.selected.text}}';
              } else if (processedAttr.options.subschema) {
                select += '{{' + attr.model + '.' + processedAttr.info.name.replace(processedAttr.options.subschemaroot, processedAttr.options.subschemaroot + '[$index]') + '.text}}';
              } else {
                select += '{{' + buildingBlocks.modelString + '.text}}';
              }
              select += '</ui-select-match>';
              select += '<ui-select-choices repeat="option in (' + processedAttr.info.id + '_options) track by $index" ';
              select += 'refresh="refreshOptions($select.search, \'' + processedAttr.info.id + '\')" ';
              select += 'refresh-delay="100"> ';
              select += '

';
            } else if (processedAttr.directiveOptions.deriveoptions) {
              if (typeof scope[processedAttr.directiveOptions.deriveoptions] === "function") {
                select += optionsFromArray(multiControl, multi, scope[processedAttr.directiveOptions.deriveoptions](), processedAttr.directiveOptions.deriveoptions);
              } else {
                throw new Error("In fng-ui-select " + processedAttr.directiveOptions.deriveoptions + " is not a function on the scope");
              }
            } else if (processedAttr.info.options) {
              // Simple case - enumerated options on the form scope
              select += optionsFromArray(multiControl, multi, processedAttr.info.options);
            } else {
              throw new Error('fng-ui-select has no means of populating select');
            }
            select += '</ui-select-choices>';
            select += '</ui-select>';
            return select;
          });
          element.append($compile(input + elementHtml)(scope));
        }
      }
    }]);
})();
<|endoftext|>


<commit_before>cdb.geo.ui.LegendItemModel = Backbone.Model.extend({ });

cdb.geo.ui.LegendItems = Backbone.Collection.extend({
  model: cdb.geo.ui.LegendItemModel
});

cdb.geo.ui.LegendItem = cdb.core.View.extend({

  tagName: "li",

  initialize: function() {

    _.bindAll(this, "render");
    this.template = cdb.templates.getTemplate('templates/map/legend/item');

  },

  render: function() {

    this.$el.html(this.template(this.model.toJSON()));
    return this.$el;

  }

});

cdb.geo.ui.Legend = cdb.core.View.extend({

  id: "legend",

  default_options: {

  },

  initialize: function() {

    this.map = this.model;

    _.bindAll(this, "render", "show", "hide");

    _.defaults(this.options, this.default_options);

    if (this.collection) {
      this.model.collection = this.collection;
    }

    this.template = this.options.template ? this.options.template : cdb.templates.getTemplate('geo/legend');
  },

  show: function() {
    this.$el.fadeIn(250);
  },

  hide: function() {
    this.$el.fadeOut(250);
  },

  render: function() {
    var self = this;

    if (this.model != undefined) {
      this.$el.html(this.template(this.model.toJSON()));
    }

    if (this.collection) {

      this.collection.each(function(item) {

        var view = new cdb.geo.ui.LegendItem({ className: item.get("className"), model: item });
        self.$el.find("ul").append(view.render());

      });
    }

    return this;
  }

});
<commit_msg>Binds the legend correctly<commit_after>cdb.geo.ui.LegendItemModel = Backbone.Model.extend({ });

cdb.geo.ui.LegendItems = Backbone.Collection.extend({
  model: cdb.geo.ui.LegendItemModel
});

cdb.geo.ui.LegendItem = cdb.core.View.extend({

  tagName: "li",

  initialize: function() {

    _.bindAll(this, "render");
    this.template = cdb.templates.getTemplate('templates/map/legend/item');

  },

  render: function() {

    this.$el.html(this.template(this.model.toJSON()));
    return this.$el;

  }

});

cdb.geo.ui.Legend = cdb.core.View.extend({

  id: "legend",

  default_options: {

  },

  initialize: function() {

    this.map = this.model;

    this.add_related_model(this.model);

    _.bindAll(this, "render", "show", "hide");

    _.defaults(this.options, this.default_options);

    if (this.collection) {
      this.model.collection = this.collection;
    }

    this.template = this.options.template ? this.options.template : cdb.templates.getTemplate('geo/legend');
  },

  show: function() {
    this.$el.fadeIn(250);
  },

  hide: function() {
    this.$el.fadeOut(250);
  },

  render: function() {
    var self = this;

    if (this.model != undefined) {
      this.$el.html(this.template(this.model.toJSON()));
    }

    if (this.collection) {

      this.collection.each(function(item) {

        var view = new cdb.geo.ui.LegendItem({ className: item.get("className"), model: item });
        self.$el.find("ul").append(view.render());

      });
    }

    return this;
  }

});
<|endoftext|>


<commit_before>/*
Handles the requests by executing stuff and replying to the client. Uses promises to get stuff done.
*/

'use strict';

const boom = require('boom'), //Boom gives us some predefined http codes and proper responses
  notificationsDB = require('../database/notificationsDatabase'), //Database functions specific for notifications
  co = require('../common');

const Microservices = require('../configs/microservices');
let http = require('http');
let rp = require('request-promise-native');
module.exports = {
  //Get notification from database or return NOT FOUND
  getNotification: function(request, reply) {
    return notificationsDB.get(encodeURIComponent(request.params.id)).then((notification) => {
      if (co.isEmpty(notification))
        reply(boom.notFound());
      else {
        return insertAuthor(notification).then((notification) => {
          reply(co.rewriteID(notification));
        }).catch((error) => {
          tryRequestLog(request, 'error', error);
          reply(boom.badImplementation());
        });
      }
    }).catch((error) => {

      tryRequestLog(request, 'error', error);
      reply(boom.badImplementation());
    });
  },

  //Create notification with new id and payload or return INTERNAL_SERVER_ERROR
  newNotification: function(request, reply) {
    return notificationsDB.insert(request.payload).then((inserted) => {
      //console.log('inserted: ', inserted);
      if (co.isEmpty(inserted.ops) || co.isEmpty(inserted.ops[0]))
        throw inserted;
      else {
        return insertAuthor(inserted.ops[0]).then((notification) => {
          reply(co.rewriteID(notification));
        }).catch((error) => {
          tryRequestLog(request, 'error', error);
          reply(boom.badImplementation());
        });
      }
    }).catch((error) => {
      tryRequestLog(request, 'error', error);
      reply(boom.badImplementation());
    });
  },

  //Update notification with id id and payload or return INTERNAL_SERVER_ERROR
  updateNotification: function(request, reply) {
    return notificationsDB.replace(encodeURIComponent(request.params.id), request.payload).then((replaced) => {
      //console.log('updated: ', replaced);
      if (co.isEmpty(replaced.value))
        throw replaced;
      else
        reply(replaced.value);
    }).catch((error) => {
      tryRequestLog(request, 'error', error);
      reply(boom.badImplementation());
    });
  },

  //Delete notification with id id
  deleteNotification: function(request, reply) {
    return notificationsDB.delete(encodeURIComponent(request.payload.id)).then(() =>
      reply({'msg': 'notification is successfully deleted...'})
    ).catch((error) => {
      tryRequestLog(request, 'error', error);
      reply(boom.badImplementation());
    });
  },

  //Delete notifications with subscribed_user_id
  deleteNotifications: function(request, reply) {
    return notificationsDB.deleteAllWithSubscribedUserID(encodeURIComponent(request.payload.subscribed_user_id)).then(() =>
      reply({'msg': 'notifications were successfully deleted...'})
    ).catch((error) => {
      tryRequestLog(request, 'error', error);
      reply(boom.badImplementation());
    });
  },

  //Get All notifications from database for the id in the request
  getNotifications: function(request, reply) {
    return notificationsDB.getAllWithSubscribedUserID(encodeURIComponent(request.params.id))
      .then((notifications) => {
        let arrayOfAuthorPromisses = [];
        notifications.forEach((notification) => {
          co.rewriteID(notification);
          let promise = insertAuthor(notification);
          arrayOfAuthorPromisses.push(promise);
        });
        Promise.all(arrayOfAuthorPromisses).then(() => {
          let jsonReply = JSON.stringify(notifications);
          reply(jsonReply);

        }).catch((error) => {
          tryRequestLog(request, 'error', error);
          reply(boom.badImplementation());
        });
      });
  },

  //Get All notifications from database
  getAllNotifications: function(request, reply) {
    return notificationsDB.getAllFromCollection()
      .then((notifications) => {
        let arrayOfAuthorPromisses = [];
        notifications.forEach((notification) => {
          co.rewriteID(notification);
          let promise = insertAuthor(notification);
          arrayOfAuthorPromisses.push(promise);
        });

        Promise.all(arrayOfAuthorPromisses).then(() => {
          let jsonReply = JSON.stringify(notifications);
          reply(jsonReply);

        }).catch((error) => {
          tryRequestLog(request, 'error', error);
          reply(boom.badImplementation());
        });

      }).catch((error) => {
        tryRequestLog(request, 'error', error);
        reply(boom.badImplementation());
      });
  }
};

//insert author data using user microservice
function insertAuthor(notification) {

// rp.get({uri: Microservices.user.uri + '/user/' + notification.user_id}).then((res) => {
  // let username = 'unknown';
  // let avatar = '';
  // if (res.statusCode === 200) {//user is found
  //   let parsed = JSON.parse(body);
  //   username = parsed.username;
  //   avatar = parsed.picture;
  // }
  //
  // notification.author = {
  //   id: notification.user_id,
  //   username: username,
  //   avatar: avatar
  // };
  // resolve(notification);

//     callback(null, {activities: activities, selector: selector, hasMore: (activities.length === 30)});
// }).catch((err) => {
//     console.log(err);
//     callback(null, {activities: [], selector: selector, hasMore: false});
// });

  let myPromise = new Promise((resolve, reject) => {

    let options = {
      host: Microservices.user.uri,
      port: 80,
      path: '/user/' + notification.user_id
    };

    let req = http.get(options, (res) => {
      // console.log('HEADERS: ' + JSON.stringify(res.headers));
      res.setEncoding('utf8');
      let body = '';
      res.on('data', (chunk) => {
        // console.log('Response: ', chunk);
        body += chunk;
      });
      res.on('end', () => {
        let username = 'unknown';
        let avatar = '';
        if (res.statusCode === 200) {//user is found
          let parsed = JSON.parse(body);
          username = parsed.username;
          avatar = parsed.picture;
        }

        notification.author = {
          id: notification.user_id,
          username: username,
          avatar: avatar
        };
        resolve(notification);
      });
    });
    req.on('error', (e) => {
      console.log('problem with request: ' + e.message);
      reject(e);
    });
  });

  return myPromise;
}

//This function tries to use request log and uses console.log if this doesnt work - this is the case in unit tests
function tryRequestLog(request, message, _object) {
  try {
    request.log(message, _object);
  } catch (e) {
    console.log(message, _object);
  }
}
<commit_msg>SWIK-996 Using request promise<commit_after>/*
Handles the requests by executing stuff and replying to the client. Uses promises to get stuff done.
*/

'use strict';

const boom = require('boom'), //Boom gives us some predefined http codes and proper responses
  notificationsDB = require('../database/notificationsDatabase'), //Database functions specific for notifications
  co = require('../common');

const Microservices = require('../configs/microservices');
let http = require('http');
let rp = require('request-promise-native');
module.exports = {
  //Get notification from database or return NOT FOUND
  getNotification: function(request, reply) {
    return notificationsDB.get(encodeURIComponent(request.params.id)).then((notification) => {
      if (co.isEmpty(notification))
        reply(boom.notFound());
      else {
        return insertAuthor(notification).then((notification) => {
          reply(co.rewriteID(notification));
        }).catch((error) => {
          tryRequestLog(request, 'error', error);
          reply(boom.badImplementation());
        });
      }
    }).catch((error) => {

      tryRequestLog(request, 'error', error);
      reply(boom.badImplementation());
    });
  },

  //Create notification with new id and payload or return INTERNAL_SERVER_ERROR
  newNotification: function(request, reply) {
    return notificationsDB.insert(request.payload).then((inserted) => {
      //console.log('inserted: ', inserted);
      if (co.isEmpty(inserted.ops) || co.isEmpty(inserted.ops[0]))
        throw inserted;
      else {
        return insertAuthor(inserted.ops[0]).then((notification) => {
          reply(co.rewriteID(notification));
        }).catch((error) => {
          tryRequestLog(request, 'error', error);
          reply(boom.badImplementation());
        });
      }
    }).catch((error) => {
      tryRequestLog(request, 'error', error);
      reply(boom.badImplementation());
    });
  },

  //Update notification with id id and payload or return INTERNAL_SERVER_ERROR
  updateNotification: function(request, reply) {
    return notificationsDB.replace(encodeURIComponent(request.params.id), request.payload).then((replaced) => {
      //console.log('updated: ', replaced);
      if (co.isEmpty(replaced.value))
        throw replaced;
      else
        reply(replaced.value);
    }).catch((error) => {
      tryRequestLog(request, 'error', error);
      reply(boom.badImplementation());
    });
  },

  //Delete notification with id id
  deleteNotification: function(request, reply) {
    return notificationsDB.delete(encodeURIComponent(request.payload.id)).then(() =>
      reply({'msg': 'notification is successfully deleted...'})
    ).catch((error) => {
      tryRequestLog(request, 'error', error);
      reply(boom.badImplementation());
    });
  },

  //Delete notifications with subscribed_user_id
  deleteNotifications: function(request, reply) {
    return notificationsDB.deleteAllWithSubscribedUserID(encodeURIComponent(request.payload.subscribed_user_id)).then(() =>
      reply({'msg': 'notifications were successfully deleted...'})
    ).catch((error) => {
      tryRequestLog(request, 'error', error);
      reply(boom.badImplementation());
    });
  },

  //Get All notifications from database for the id in the request
  getNotifications: function(request, reply) {
    return notificationsDB.getAllWithSubscribedUserID(encodeURIComponent(request.params.id))
      .then((notifications) => {
        let arrayOfAuthorPromisses = [];
        notifications.forEach((notification) => {
          co.rewriteID(notification);
          let promise = insertAuthor(notification);
          arrayOfAuthorPromisses.push(promise);
        });
        Promise.all(arrayOfAuthorPromisses).then(() => {
          let jsonReply = JSON.stringify(notifications);
          reply(jsonReply);

        }).catch((error) => {
          tryRequestLog(request, 'error', error);
          reply(boom.badImplementation());
        });
      });
  },

  //Get All notifications from database
  getAllNotifications: function(request, reply) {
    return notificationsDB.getAllFromCollection()
      .then((notifications) => {
        let arrayOfAuthorPromisses = [];
        notifications.forEach((notification) => {
          co.rewriteID(notification);
          let promise = insertAuthor(notification);
          arrayOfAuthorPromisses.push(promise);
        });

        Promise.all(arrayOfAuthorPromisses).then(() => {
          let jsonReply = JSON.stringify(notifications);
          reply(jsonReply);

        }).catch((error) => {
          tryRequestLog(request, 'error', error);
          reply(boom.badImplementation());
        });

      }).catch((error) => {
        tryRequestLog(request, 'error', error);
        reply(boom.badImplementation());
      });
  }
};

//insert author data using user microservice
function insertAuthor(notification) {

let myPromise = new Promise((resolve, reject) => {

  let username = 'unknown';
  let avatar = '';
  rp.get({uri: Microservices.user.uri + '/user/' + notification.user_id}).then((res) => {
    console.log('Res', res);

    try {
      let parsed = JSON.parse(res);
      username = parsed.username;
      avatar = parsed.picture;
    } catch(e) {
        console.log(e); // error in the above string (in this case, yes)!
    }
    // if (res.statusCode === 200) {//user is found
    //   let parsed = JSON.parse(res);
    //   username = parsed.username;
    //   avatar = parsed.picture;
    // }

    notification.author = {
      id: notification.user_id,
      username: username,
      avatar: avatar
    };
    resolve(notification);

      // callback(null, {activities: activities, selector: selector, hasMore: (activities.length === 30)});
  }).catch((err) => {
      console.log('Error', err);
      notification.author = {
        id: notification.user_id,
        username: username,
        avatar: avatar
      };
      resolve(notification);
      // callback(null, {activities: [], selector: selector, hasMore: false});
  });
});

  // let myPromise = new Promise((resolve, reject) => {
  //
  //   let options = {
  //     host: Microservices.user.uri,
  //     port: 80,
  //     path: '/user/' + notification.user_id
  //   };
  //
  //   let req = http.get(options, (res) => {
  //     // console.log('HEADERS: ' + JSON.stringify(res.headers));
  //     res.setEncoding('utf8');
  //     let body = '';
  //     res.on('data', (chunk) => {
  //       // console.log('Response: ', chunk);
  //       body += chunk;
  //     });
  //     res.on('end', () => {
  //       console.log('res', res);
  //         console.log('body', body);
  //       let username = 'unknown';
  //       let avatar = '';
  //       if (res.statusCode === 200) {//user is found
  //         let parsed = JSON.parse(body);
  //         username = parsed.username;
  //         avatar = parsed.picture;
  //       }
  //
  //       notification.author = {
  //         id: notification.user_id,
  //         username: username,
  //         avatar: avatar
  //       };
  //       resolve(notification);
  //     });
  //   });
  //   req.on('error', (e) => {
  //     console.log('problem with request: ' + e.message);
  //     reject(e);
  //   });
  // });
  //
  return myPromise;
}

//This function tries to use request log and uses console.log if this doesnt work - this is the case in unit tests
function tryRequestLog(request, message, _object) {
  try {
    request.log(message, _object);
  } catch (e) {
    console.log(message, _object);
  }
}
<|endoftext|>


<commit_before>/* note: when you run karma, you'll get a failure for every <script> and <link> in the html, because
those start with "/bloom". While the Bloom server strips those off, karma doesn't. Seems like
Karma's "proxies" argument should be able to do that, but I haven't been successful.
*/

//note: if you change this, change it in gulpfile.js & webpack.config.js
var outputDir = "../../output/browser";

module.exports = function(config) {
    config.set({
        // base path, that will be used to resolve files and exclude
        basePath: outputDir,
        frameworks: ["jasmine"],

        // list of files / patterns to load in the browser
        files: [
            "bookEdit/test/GetSettingsMock.js",
            "commonBundle.js",
            "testBundle.js", // If you want a test run, make sure it is in this bundle that webpack produces
            // If you want a jasmine fixture to be served, make sure the name ends in Fixture.html
            { pattern: "**/*Fixture.html", included: false, served: true },
            { pattern: "**/*.js.map", included: false }
        ],

        preprocessors: {
            "**/*.js": ["sourcemap"], // this doesn't actually work, in the error message it generates, you can see the name of the original file
            "**/*.html": [] //prevent  karma from preprocessing html (blocks jasmine fixture feature) https://github.com/karma-runner/karma/issues/788
        },

        // test results reporter to use
        // possible values: 'spec' (nice list with checkboxes), 'dots', 'progress', 'junit', 'growl', 'coverage', 'teamcity'
        reporters: ["spec"],
        port: 9876,
        colors: true,

        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,
        autoWatch: true,

        // Start these browsers, currently available:
        // - Chrome IF YOU USE CHROME, NOTE THAT IF YOU MINIMIZE CHROME, IT WILL RUN TESTS SUPER SLOWLY
        // - Firefox
        // - PhantomJS
        browsers: ["Firefox", "Chrome_allow_autoplay"],

        customLaunchers: {
            Chrome_allow_autoplay: {
                base: "Chrome",
                flags: ["--autoplay-policy=no-user-gesture-required"]
            }
        },

        // If browser does not capture in given timeout [ms], kill it
        captureTimeout: 6000,

        // Continuous Integration mode
        // if true, it capture browsers, run tests and exit
        singleRun: false,

        specReporter: {
            suppressSkipped: true // do not print information about skipped tests
        },

        client: { jasmine: { timeoutInterval: 15000 } }
    });
};
<commit_msg>Attempt to allow javascript tests to pass<commit_after>/* note: when you run karma, you'll get a failure for every <script> and <link> in the html, because
those start with "/bloom". While the Bloom server strips those off, karma doesn't. Seems like
Karma's "proxies" argument should be able to do that, but I haven't been successful.
*/

//note: if you change this, change it in gulpfile.js & webpack.config.js
var outputDir = "../../output/browser";

module.exports = function(config) {
    config.set({
        // base path, that will be used to resolve files and exclude
        basePath: outputDir,
        frameworks: ["jasmine"],

        // list of files / patterns to load in the browser
        files: [
            "bookEdit/test/GetSettingsMock.js",
            "commonBundle.js",
            "testBundle.js", // If you want a test run, make sure it is in this bundle that webpack produces
            // If you want a jasmine fixture to be served, make sure the name ends in Fixture.html
            { pattern: "**/*Fixture.html", included: false, served: true },
            { pattern: "**/*.js.map", included: false }
        ],

        preprocessors: {
            "**/*.js": ["sourcemap"], // this doesn't actually work, in the error message it generates, you can see the name of the original file
            "**/*.html": [] //prevent  karma from preprocessing html (blocks jasmine fixture feature) https://github.com/karma-runner/karma/issues/788
        },

        // test results reporter to use
        // possible values: 'spec' (nice list with checkboxes), 'dots', 'progress', 'junit', 'growl', 'coverage', 'teamcity'
        reporters: ["spec"],
        port: 9876,
        colors: true,

        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,
        autoWatch: true,

        // Start these browsers, currently available:
        // - Chrome IF YOU USE CHROME, NOTE THAT IF YOU MINIMIZE CHROME, IT WILL RUN TESTS SUPER SLOWLY
        // - Firefox
        // - PhantomJS
        browsers: ["Firefox", "Chrome_allow_autoplay"],

        customLaunchers: {
            Chrome_allow_autoplay: {
                base: "Chrome",
                flags: ["--autoplay-policy=no-user-gesture-required"]
            }
        },

        // If browser does not capture in given timeout [ms], kill it
        captureTimeout: 6000,

        // Continuous Integration mode
        // if true, it capture browsers, run tests and exit
        singleRun: false,

        specReporter: {
            suppressSkipped: true // do not print information about skipped tests
        },

        client: { jasmine: { timeoutInterval: 15000 } },

        // These lines are an attempt to fix test run failures on TeamCity.
        // Sometimes all tests run but the overall test run command fails.
        // Sometimes not all the tests run.
        // What seems to be common when it doesn't work is that we get
        // WARN [Chrome 106.0.0 (Windows 10.0.0)]: Disconnected (0 times), because no message in 30000 ms.
        // (30000 was the default value for browserNoActivityTimeout.)
        concurrency: 1,
        browserNoActivityTimeout: 120000
    });
};
<|endoftext|>


<commit_before>import { Command } from 'grind-cli'

export class QueueWorkCommand extends Command {
	name = 'queue:work'
	description = 'Process jobs in the queue'
	arguments = [ ]
	options = { }

	ready() {
		const shutdown = () => {
			this.app.queue.kue.shutdown(5000, err => {
				if(!err.isNil) {
					Log.error('Error shutting down', err)
				}

				process.exit(0)
			})
		}

		process.once('SIGTERM', shutdown)
		process.once('SIGINT', shutdown)

		return super.ready()
	}

	run() {
		this.app.queue.kue.watchStuckJobs()
		return new Promise(() => this.app.queue.process())
	}

}
<commit_msg>Added error logging<commit_after>import { Command } from 'grind-cli'

export class QueueWorkCommand extends Command {
	name = 'queue:work'
	description = 'Process jobs in the queue'
	arguments = [ ]
	options = { }

	ready() {
		const shutdown = () => {
			this.app.queue.kue.shutdown(5000, err => {
				if(!err.isNil) {
					Log.error('Error shutting down', err)
				}

				process.exit(0)
			})
		}

		process.once('SIGTERM', shutdown)
		process.once('SIGINT', shutdown)

		return super.ready()
	}

	run() {
		this.app.queue.kue.watchStuckJobs()
		this.app.queue.kue.on('error', err => {
			Log.error('Error', err)
		})

		return new Promise(() => this.app.queue.process())
	}

}
<|endoftext|>


<commit_before>var Page = Backbone.Model.extend({
  namespace: '/v1',
  icon: 'fa-file-text-o',
  type: "page",

  defaults: {
    text: "",
    markdown: "",
    title: "",
  },

  initialize: function () {
    this.on("change", this.updateMarkdown, this);
  },

  name: function () {
    return this.id.slice(this.id.lastIndexOf('/') + 1);
  },

  sync: function (method, collection, opts) {
    var _this = this;
    return $.ajax(this.namespace + this.id)
      .done(function (data) {
        opts.success({text: data});
      })
      .fail(opts.fail);
  },

  updateMarkdown: function () {
    // Take markdown title if matching
    var m = /^#(.*)\n([^]*)$/.exec(this.attributes.text);
    var markdownRaw = "";
    if (m) {
      this.attributes.title = m[1].trim();
      markdownRaw = m[2];
    } else {
      this.attributes.title = this.name();
      markdownRaw = this.attributes.text;
    }

    // Escape \[ and \] to \\[ and \\]
    markdownRaw = markdownRaw.replace(/\\\[/g, '\\\\[');
    markdownRaw = markdownRaw.replace(/\\\]/g, '\\\\]');

    // Replace [[links]]

    // Images, e.g. [[foo.jpg]]
    markdownRaw = markdownRaw.replace(/\[\[([^|\]]+\.(?:jpg|png))\]\]/g, "![$1]($1)");
    // [[foo]]
    markdownRaw = markdownRaw.replace(/\[\[([^|\]]+)\]\]/g, "[$1]($1)");
    // [[foo|bar]]
    markdownRaw = markdownRaw.replace(/\[\[([^|\]]+)\|([^\]]+)\]\]/g, "[$1]($2)");

    // Render markdown

    var renderer = new marked.Renderer();
    var _this = this;
    renderer.image = function (href, title, text) {
      if (href[0] == '/') {
        href = '/v1' + href;
      } else {
        href = '/v1' + _this.id.slice(0, _this.id.lastIndexOf('/')+1) + href;
      }
      return '

';
    };
    this.attributes.markdown = marked(markdownRaw, {renderer: renderer});
  },
});

export default Page;
<commit_msg>if links have no extension, make them .md links. fixes #12<commit_after>var Page = Backbone.Model.extend({
  namespace: '/v1',
  icon: 'fa-file-text-o',
  type: "page",

  defaults: {
    text: "",
    markdown: "",
    title: "",
  },

  initialize: function () {
    this.on("change", this.updateMarkdown, this);
  },

  name: function () {
    return this.id.slice(this.id.lastIndexOf('/') + 1);
  },

  sync: function (method, collection, opts) {
    var _this = this;
    return $.ajax(this.namespace + this.id)
      .done(function (data) {
        opts.success({text: data});
      })
      .fail(opts.fail);
  },

  updateMarkdown: function () {
    // Take markdown title if matching
    var m = /^#(.*)\n([^]*)$/.exec(this.attributes.text);
    var markdownRaw = "";
    if (m) {
      this.attributes.title = m[1].trim();
      markdownRaw = m[2];
    } else {
      this.attributes.title = this.name();
      markdownRaw = this.attributes.text;
    }

    // Escape \[ and \] to \\[ and \\]
    markdownRaw = markdownRaw.replace(/\\\[/g, '\\\\[');
    markdownRaw = markdownRaw.replace(/\\\]/g, '\\\\]');

    // Replace [[links]]

    // Images, e.g. [[foo.jpg]]
    markdownRaw = markdownRaw.replace(/\[\[([^|\]]+\.(?:jpg|png))\]\]/g, "![$1]($1)");
    // [[foo.extension]]
    markdownRaw = markdownRaw.replace(/\[\[([^|\]]+)\.([^|\]\.]+)\]\]/g, "[$1]($1.$2)");
    // [[foo]]
    markdownRaw = markdownRaw.replace(/\[\[([^|\]]+)\]\]/g, "[$1]($1.md)");
    // [[foo|bar.extension]]
    markdownRaw = markdownRaw.replace(/\[\[([^|\]]+)\|([^\]]+)\.([^\]\.]+)\]\]/g, "[$1]($2.$3)");
    markdownRaw = markdownRaw.replace(/\[\[([^|\]]+)\|([^\]]+)\]\]/g, "[$1]($2.md)");

    // Render markdown

    var renderer = new marked.Renderer();
    var _this = this;
    renderer.image = function (href, title, text) {
      if (href[0] == '/') {
        href = '/v1' + href;
      } else {
        href = '/v1' + _this.id.slice(0, _this.id.lastIndexOf('/')+1) + href;
      }
      return '

';
    };
    this.attributes.markdown = marked(markdownRaw, {renderer: renderer});
  },
});

export default Page;
<|endoftext|>


<commit_before>exports.productionVersionId = "a06370e58768";<commit_msg>Incrementing Production Version.<commit_after>exports.productionVersionId = "2ca4f42c95a6";<|endoftext|>


<commit_before>import React from 'react'
import App, {Container} from 'next/app'
import {MDXProvider} from '@mdx-js/tag'
import Head from 'next/head'
import Octicon, {Pencil} from '@githubprimer/octicons-react'
import {BaseStyles, Link, Text, BorderBox, Box, Flex, theme} from '@primer/components'
import {PackageHeader} from '../docs/components'
import {Header, MarkdownHeading, JumpNav, SideNav} from '@primer/blueprints'
import {NavList} from '@primer/blueprints/next-components'
import getComponents from '../docs/markdown'
import documents from '../searchIndex'
import {config, requirePage, rootPage} from '../docs/utils'
import {CONTENT_MAX_WIDTH} from '../docs/constants'
import {repository} from '../package.json'

import '../src/index.scss'

const DocLink = props => <Link nounderline {...props} />

export default class MyApp extends App {
  static async getInitialProps({Component, ctx}) {
    let page = {}

    if (Component.getInitialProps) {
      page = await Component.getInitialProps(ctx)
    }

    return {page}
  }

  render() {
    // strip the trailing slash
    const pathname = this.props.router.pathname.replace(/\/$/, '')
    const {Component, page} = this.props
    const node = rootPage.first(node => node.path === pathname) || {}
    const {file, meta = {}} = node || {}
    const isIndex = file.includes('index')
    const editLinkBase = `${repository}/edit/master/pages`
    const components = getComponents(node)

    const Hero = file ? requirePage(file).Hero : null

    return (
      <BaseStyles fontSize={2} style={{fontFamily: theme.fonts.normal}}>
        <Container>

            <title>Primer CSS{meta.title ? ` / ${meta.title}` : null}</title>

            <JumpNav />

          <Flex
            flexDirection={['column', 'column', 'column', 'row-reverse']}
            alignContent="stretch"
            justifyContent="space-between"
          >
            <Box width={['auto', 'auto', '100%']}>
              {Hero ? <Hero /> : null}
              <Box color="gray.9" maxWidth={['auto', 'auto', 'auto', CONTENT_MAX_WIDTH]} px={6} mx="auto" my={6}>

                  {!meta.hero && meta.title ? <MarkdownHeading>{meta.title}</MarkdownHeading> : null}
                  <PackageHeader {...meta} />
                  <MDXProvider components={components}>
                    <Component {...page} />
                  </MDXProvider>
                  {config.production ? null : (

                      Metadata

{JSON.stringify(meta, null, 2)}

                  )}
                  {pathname && (
                    <Box color="gray.5" borderColor="gray.2" borderTop={1} my={6} pt={2}>
                      <Text mr={2}>
                        <Octicon icon={Pencil} />
                      </Text>
                      <DocLink muted href={`${editLinkBase}${pathname}${isIndex ? '/index' : ''}.md`}>
                        Edit this page
                      </DocLink>{' '}
                      on GitHub
                    </Box>
                  )}

              </Box>
            </Box>
            <BorderBox
              width={['100%', '100%', 256]}
              minWidth={256}
              bg="gray.0"
              borderColor="gray.2"
              borderRadius={0}
              border={0}
              borderRight={1}
              borderTop={[1, 1, 0, 0]}
            >
              <SideNav>
                <NavList currentPath={pathname} path="/css/getting-started" />
                <NavList currentPath={pathname} path="/css/tools" />
                <NavList currentPath={pathname} path="/css/principles" />
                <NavList currentPath={pathname} path="/css/support" />
                <NavList currentPath={pathname} path="/css/utilities" />
                <NavList currentPath={pathname} path="/css/objects" />
                <NavList currentPath={pathname} path="/css/components" />
              </SideNav>
            </BorderBox>
          </Flex>
        </Container>
      </BaseStyles>
    )
  }
}
<commit_msg>move editLinkBase out of render function<commit_after>import React from 'react'
import App, {Container} from 'next/app'
import {MDXProvider} from '@mdx-js/tag'
import Head from 'next/head'
import Octicon, {Pencil} from '@githubprimer/octicons-react'
import {BaseStyles, Link, Text, BorderBox, Box, Flex, theme} from '@primer/components'
import {PackageHeader} from '../docs/components'
import {Header, MarkdownHeading, JumpNav, SideNav} from '@primer/blueprints'
import {NavList} from '@primer/blueprints/next-components'
import getComponents from '../docs/markdown'
import documents from '../searchIndex'
import {config, requirePage, rootPage} from '../docs/utils'
import {CONTENT_MAX_WIDTH} from '../docs/constants'
import {repository} from '../package.json'

import '../src/index.scss'

const DocLink = props => <Link nounderline {...props} />
const editLinkBase = `${repository}/edit/master/pages`

export default class MyApp extends App {
  static async getInitialProps({Component, ctx}) {
    let page = {}

    if (Component.getInitialProps) {
      page = await Component.getInitialProps(ctx)
    }

    return {page}
  }

  render() {
    // strip the trailing slash
    const pathname = this.props.router.pathname.replace(/\/$/, '')
    const {Component, page} = this.props
    const node = rootPage.first(node => node.path === pathname) || {}
    const {file, meta = {}} = node || {}
    const isIndex = file.includes('index')
    const components = getComponents(node)

    const Hero = file ? requirePage(file).Hero : null

    return (
      <BaseStyles fontSize={2} style={{fontFamily: theme.fonts.normal}}>
        <Container>

            <title>Primer CSS{meta.title ? ` / ${meta.title}` : null}</title>

            <JumpNav />

          <Flex
            flexDirection={['column', 'column', 'column', 'row-reverse']}
            alignContent="stretch"
            justifyContent="space-between"
          >
            <Box width={['auto', 'auto', '100%']}>
              {Hero ? <Hero /> : null}
              <Box color="gray.9" maxWidth={['auto', 'auto', 'auto', CONTENT_MAX_WIDTH]} px={6} mx="auto" my={6}>

                  {!meta.hero && meta.title ? <MarkdownHeading>{meta.title}</MarkdownHeading> : null}
                  <PackageHeader {...meta} />
                  <MDXProvider components={components}>
                    <Component {...page} />
                  </MDXProvider>
                  {config.production ? null : (

                      Metadata

{JSON.stringify(meta, null, 2)}

                  )}
                  {pathname && (
                    <Box color="gray.5" borderColor="gray.2" borderTop={1} my={6} pt={2}>
                      <Text mr={2}>
                        <Octicon icon={Pencil} />
                      </Text>
                      <DocLink muted href={`${editLinkBase}${pathname}${isIndex ? '/index' : ''}.md`}>
                        Edit this page
                      </DocLink>{' '}
                      on GitHub
                    </Box>
                  )}

              </Box>
            </Box>
            <BorderBox
              width={['100%', '100%', 256]}
              minWidth={256}
              bg="gray.0"
              borderColor="gray.2"
              borderRadius={0}
              border={0}
              borderRight={1}
              borderTop={[1, 1, 0, 0]}
            >
              <SideNav>
                <NavList currentPath={pathname} path="/css/getting-started" />
                <NavList currentPath={pathname} path="/css/tools" />
                <NavList currentPath={pathname} path="/css/principles" />
                <NavList currentPath={pathname} path="/css/support" />
                <NavList currentPath={pathname} path="/css/utilities" />
                <NavList currentPath={pathname} path="/css/objects" />
                <NavList currentPath={pathname} path="/css/components" />
              </SideNav>
            </BorderBox>
          </Flex>
        </Container>
      </BaseStyles>
    )
  }
}
<|endoftext|>