<commit_before>/**
enyo.Scroller is scroller suitable for use in both desktop and mobile applications.

In some mobile environments, a default scrolling solution is not implemented for dom elements. In these cases, enyo.Scroller implements
a touch based scrolling solution. This can be opted into either globally by setting the flag enyo.Scroller.touchScrolling = true;
or on a per instance basis by specifying a strategyKind of "TouchScrollStrategy."
*/
enyo.kind({
	name: "enyo.Scroller",
	published: {
		/**
			Specifies how to horizontally scroll. Acceptable values are "scroll", "auto," "hidden," and "default" The precise
			effect of the setting is determined by the scroll strategy.
		*/
		horizontal: "default",
		/**
			Specifies how to vertically scroll. Acceptable values are "scroll", "auto," "hidden," and "default" The precise
			effect of the setting is determined by the scroll strategy.
		*/
		vertical: "default",
		/**
			Sets the vertical scroll position.
		*/
		scrollTop: 0,
		/**
			Sets the horizontal scroll position.
		*/
		scrollLeft: 0,
		/**
			Sets the maximum height of the scroll content.
		*/
		maxHeight: null,
		/**
			Set to true to make this scroller select a platform appropriate touch based scrolling strategy.
			Please note that specifycing a scrollStrategy will take precedence over this setting.
		*/
		touch: false,
		/**
			Specify a type of scrolling. The enyo Scroller will attempt to automatically select
			a strategy compatbile with the runtime environment. A specific strategy can also be chosen:

			* ScrollStrategy is the default and implements no scrolling, relying instead on the environment to scroll properly.
			* TouchScrollStrategy implements a touch scrolling mechanism.
			* TranslateScrollStrategy implements a touch scrolling mechanism using translations, recommended only for Android 3 and 4 currently.
		*/
		strategyKind: "ScrollStrategy",
		//* set to true to display a scroll thumb in Touch scrollers.
		thumb: true
	},
	events: {
		onScrollStart: "",
		onScroll: "",
		onScrollStop: ""
	},
	handlers: {
		onscroll: "domScroll",
		onScrollStart: "scrollStart",
		onScroll: "scroll",
		onScrollStop: "scrollStop"
	},
	classes: "enyo-scroller",
	/**
		If true, the scroller will not propagate dragstart events that cause it to start scrolling (defaults to true)
	*/
	preventDragPropagation: true,
	/**
		If true, the scroller will not propagate scroll events.
	*/
	preventScrollPropagation: true,
	//* @protected
	statics: {
		osInfo: [
			{os: "android", version: 3},
			{os: "ios", version: 5},
			{os: "webos", version: 1e9}
		],
		//* returns true if platform should have touch events
		hasTouchScrolling: function() {
			for (var i=0, t, m; t=this.osInfo[i]; i++) {
				if (enyo.platform[t.os]) {
					return true;
				}
			}
		},
		//* returns true if the platform has native div scrollers, desktop browsers always have them
		hasNativeScrolling: function() {
			for (var i=0, t, m; t=this.osInfo[i]; i++) {
				if (enyo.platform[t.os] < t.version) {
					return false;
				}
			}
			return true;
		},
		getTouchStrategy: function() {
			return enyo.platform.android >= 3 ? "TranslateScrollStrategy" : "TouchScrollStrategy";
		}
	},
	//* @protected
	controlParentName: "strategy",
	create: function() {
		this.inherited(arguments);
		this.horizontalChanged();
		this.verticalChanged();
	},
	importProps: function(inProps) {
		this.inherited(arguments);
		// allow global overriding of strategy kind
		if (inProps && inProps.strategyKind === undefined && (enyo.Scroller.touchScrolling || this.touch)) {
			this.strategyKind = enyo.Scroller.getTouchStrategy();
		}
	},
	initComponents: function() {
		this.strategyKindChanged();
		this.inherited(arguments);
	},
	teardownChildren: function() {
		this.cacheScrollPosition();
		this.inherited(arguments);
	},
	rendered: function() {
		this.inherited(arguments);
		this.restoreScrollPosition();
	},
	strategyKindChanged: function() {
		if (this.$.strategy) {
			this.$.strategy.destroy();
			this.controlParent = null;
		}
		// note: createComponents automatically updates controlParent.
		this.createStrategy();
		if (this.hasNode()) {
			this.render();
		}
	},
	createStrategy: function() {
		this.createComponents([{name: "strategy", maxHeight: this.maxHeight, kind: this.strategyKind, thumb: this.thumb, preventDragPropagation: this.preventDragPropagation, isChrome: true}]);
	},
	getStrategy: function() {
		return this.$.strategy;
	},
	maxHeightChanged: function() {
		this.$.strategy.setMaxHeight(this.maxHeight);
	},
	showingChanged: function() {
		if (!this.showing) {
			this.cacheScrollPosition();
			this.setScrollLeft(0);
			this.setScrollTop(0);
		}
		this.inherited(arguments);
		if (this.showing) {
			this.restoreScrollPosition();
		}
	},
	thumbChanged: function() {
		this.$.strategy.setThumb(this.thumb);
	},
	cacheScrollPosition: function() {
		this.cachedPosition = {left: this.getScrollLeft(), top: this.getScrollTop()};
	},
	restoreScrollPosition: function() {
		if (this.cachedPosition) {
			this.setScrollLeft(this.cachedPosition.left);
			this.setScrollTop(this.cachedPosition.top);
			this.cachedPosition = null;
		}
	},
	horizontalChanged: function() {
		this.$.strategy.setHorizontal(this.horizontal);
	},
	verticalChanged: function() {
		this.$.strategy.setVertical(this.vertical);
	},
	// FIXME: these properties are virtual; property changed methods are fired only if
	// property value changes, not if getter changes.
	setScrollLeft: function(inLeft) {
		this.scrollLeft = inLeft;
		this.$.strategy.setScrollLeft(this.scrollLeft);
	},
	setScrollTop: function(inTop) {
		this.scrollTop = inTop;
		this.$.strategy.setScrollTop(inTop);
	},
	getScrollLeft: function() {
		return this.$.strategy.getScrollLeft();
	},
	getScrollTop: function() {
		return this.$.strategy.getScrollTop();
	},
	//* @public
	//* returns an object describing the scroll boundaries with height and width properties.
	getScrollBounds: function() {
		return this.$.strategy.getScrollBounds();
	},
	//* scrolls the given control (inControl) into view. If inAlignWithTop is true, inControl is aligned with the top of the scroller.
	scrollIntoView: function(inControl, inAlignWithTop) {
		this.$.strategy.scrollIntoView(inControl, inAlignWithTop);
	},
	//* Scroll to the position given by inX and inY in pixel units.
	scrollTo: function(inX, inY) {
		this.$.strategy.scrollTo(inX, inY);
	},
	//* ensure that the given control is visible in the scroller's viewport.  Unlike scrollIntoView which uses DOM's scrollIntoView, this only affects the current scroller.
	scrollToControl: function(inControl, inAlignWithTop) {
		this.scrollToNode(inControl.hasNode(), inAlignWithTop);
	},
	// ensure that the given node is visible in the scroller's viewport.
	scrollToNode: function(inNode, inAlignWithTop) {
		this.$.strategy.scrollToNode(inNode, inAlignWithTop);
	},
	// normalize scroll event to onScroll.
	domScroll: function(inSender, e) {
		// if a scroll event originated here, pass it to our strategy to handle
		if (this.$.strategy.domScroll && e.originator == this) {
			this.$.strategy.scroll(inSender, e);
		}
		this.doScroll(e);
		return true;
	},
	shouldStopScrollEvent: function(inEvent) {
		return (this.preventScrollPropagation && inEvent.originator.owner != this.$.strategy);
	},
	scrollStart: function(inSender, inEvent) {
		return this.shouldStopScrollEvent(inEvent);
	},
	scroll: function(inSender, inEvent) {
		// note: scroll event can be native dom or generated.
		if (inEvent.dispatchTarget) {
			// allow a dom event if it orignated with this scroller or its strategy
			return this.preventScrollPropagation && !(inEvent.originator == this || inEvent.originator.owner == this.$.strategy);
		} else {
			return this.shouldStopScrollEvent(inEvent);
		}
	},
	scrollStop: function(inSender, inEvent) {
		return this.shouldStopScrollEvent(inEvent);
	},
	scrollToBottom: function() {
		this.setScrollTop(this.getScrollBounds().maxTop);
	},
	scrollToRight: function() {
		this.setScrollTop(this.getScrollBounds().maxLeft);
	}
});

// provide a touch scrolling solution by default when the environment is mobile
if (enyo.Scroller.hasTouchScrolling()) {
	enyo.Scroller.prototype.strategyKind = enyo.Scroller.getTouchStrategy();
}
<commit_msg>* Scroller: add scrollTop/Left methods.<commit_after>/**
enyo.Scroller is scroller suitable for use in both desktop and mobile applications.

In some mobile environments, a default scrolling solution is not implemented for dom elements. In these cases, enyo.Scroller implements
a touch based scrolling solution. This can be opted into either globally by setting the flag enyo.Scroller.touchScrolling = true;
or on a per instance basis by specifying a strategyKind of "TouchScrollStrategy."
*/
enyo.kind({
	name: "enyo.Scroller",
	published: {
		/**
			Specifies how to horizontally scroll. Acceptable values are "scroll", "auto," "hidden," and "default" The precise
			effect of the setting is determined by the scroll strategy.
		*/
		horizontal: "default",
		/**
			Specifies how to vertically scroll. Acceptable values are "scroll", "auto," "hidden," and "default" The precise
			effect of the setting is determined by the scroll strategy.
		*/
		vertical: "default",
		/**
			Sets the vertical scroll position.
		*/
		scrollTop: 0,
		/**
			Sets the horizontal scroll position.
		*/
		scrollLeft: 0,
		/**
			Sets the maximum height of the scroll content.
		*/
		maxHeight: null,
		/**
			Set to true to make this scroller select a platform appropriate touch based scrolling strategy.
			Please note that specifycing a scrollStrategy will take precedence over this setting.
		*/
		touch: false,
		/**
			Specify a type of scrolling. The enyo Scroller will attempt to automatically select
			a strategy compatbile with the runtime environment. A specific strategy can also be chosen:

			* ScrollStrategy is the default and implements no scrolling, relying instead on the environment to scroll properly.
			* TouchScrollStrategy implements a touch scrolling mechanism.
			* TranslateScrollStrategy implements a touch scrolling mechanism using translations, recommended only for Android 3 and 4 currently.
		*/
		strategyKind: "ScrollStrategy",
		//* set to true to display a scroll thumb in Touch scrollers.
		thumb: true
	},
	events: {
		onScrollStart: "",
		onScroll: "",
		onScrollStop: ""
	},
	handlers: {
		onscroll: "domScroll",
		onScrollStart: "scrollStart",
		onScroll: "scroll",
		onScrollStop: "scrollStop"
	},
	classes: "enyo-scroller",
	/**
		If true, the scroller will not propagate dragstart events that cause it to start scrolling (defaults to true)
	*/
	preventDragPropagation: true,
	/**
		If true, the scroller will not propagate scroll events.
	*/
	preventScrollPropagation: true,
	//* @protected
	statics: {
		osInfo: [
			{os: "android", version: 3},
			{os: "ios", version: 5},
			{os: "webos", version: 1e9}
		],
		//* returns true if platform should have touch events
		hasTouchScrolling: function() {
			for (var i=0, t, m; t=this.osInfo[i]; i++) {
				if (enyo.platform[t.os]) {
					return true;
				}
			}
		},
		//* returns true if the platform has native div scrollers, desktop browsers always have them
		hasNativeScrolling: function() {
			for (var i=0, t, m; t=this.osInfo[i]; i++) {
				if (enyo.platform[t.os] < t.version) {
					return false;
				}
			}
			return true;
		},
		getTouchStrategy: function() {
			return enyo.platform.android >= 3 ? "TranslateScrollStrategy" : "TouchScrollStrategy";
		}
	},
	//* @protected
	controlParentName: "strategy",
	create: function() {
		this.inherited(arguments);
		this.horizontalChanged();
		this.verticalChanged();
	},
	importProps: function(inProps) {
		this.inherited(arguments);
		// allow global overriding of strategy kind
		if (inProps && inProps.strategyKind === undefined && (enyo.Scroller.touchScrolling || this.touch)) {
			this.strategyKind = enyo.Scroller.getTouchStrategy();
		}
	},
	initComponents: function() {
		this.strategyKindChanged();
		this.inherited(arguments);
	},
	teardownChildren: function() {
		this.cacheScrollPosition();
		this.inherited(arguments);
	},
	rendered: function() {
		this.inherited(arguments);
		this.restoreScrollPosition();
	},
	strategyKindChanged: function() {
		if (this.$.strategy) {
			this.$.strategy.destroy();
			this.controlParent = null;
		}
		// note: createComponents automatically updates controlParent.
		this.createStrategy();
		if (this.hasNode()) {
			this.render();
		}
	},
	createStrategy: function() {
		this.createComponents([{name: "strategy", maxHeight: this.maxHeight, kind: this.strategyKind, thumb: this.thumb, preventDragPropagation: this.preventDragPropagation, isChrome: true}]);
	},
	getStrategy: function() {
		return this.$.strategy;
	},
	maxHeightChanged: function() {
		this.$.strategy.setMaxHeight(this.maxHeight);
	},
	showingChanged: function() {
		if (!this.showing) {
			this.cacheScrollPosition();
			this.setScrollLeft(0);
			this.setScrollTop(0);
		}
		this.inherited(arguments);
		if (this.showing) {
			this.restoreScrollPosition();
		}
	},
	thumbChanged: function() {
		this.$.strategy.setThumb(this.thumb);
	},
	cacheScrollPosition: function() {
		this.cachedPosition = {left: this.getScrollLeft(), top: this.getScrollTop()};
	},
	restoreScrollPosition: function() {
		if (this.cachedPosition) {
			this.setScrollLeft(this.cachedPosition.left);
			this.setScrollTop(this.cachedPosition.top);
			this.cachedPosition = null;
		}
	},
	horizontalChanged: function() {
		this.$.strategy.setHorizontal(this.horizontal);
	},
	verticalChanged: function() {
		this.$.strategy.setVertical(this.vertical);
	},
	// FIXME: these properties are virtual; property changed methods are fired only if
	// property value changes, not if getter changes.
	setScrollLeft: function(inLeft) {
		this.scrollLeft = inLeft;
		this.$.strategy.setScrollLeft(this.scrollLeft);
	},
	setScrollTop: function(inTop) {
		this.scrollTop = inTop;
		this.$.strategy.setScrollTop(inTop);
	},
	getScrollLeft: function() {
		return this.$.strategy.getScrollLeft();
	},
	getScrollTop: function() {
		return this.$.strategy.getScrollTop();
	},
	//* @public
	//* returns an object describing the scroll boundaries with height and width properties.
	getScrollBounds: function() {
		return this.$.strategy.getScrollBounds();
	},
	//* scrolls the given control (inControl) into view. If inAlignWithTop is true, inControl is aligned with the top of the scroller.
	scrollIntoView: function(inControl, inAlignWithTop) {
		this.$.strategy.scrollIntoView(inControl, inAlignWithTop);
	},
	//* Scroll to the position given by inX and inY in pixel units.
	scrollTo: function(inX, inY) {
		this.$.strategy.scrollTo(inX, inY);
	},
	//* ensure that the given control is visible in the scroller's viewport.  Unlike scrollIntoView which uses DOM's scrollIntoView, this only affects the current scroller.
	scrollToControl: function(inControl, inAlignWithTop) {
		this.scrollToNode(inControl.hasNode(), inAlignWithTop);
	},
	// ensure that the given node is visible in the scroller's viewport.
	scrollToNode: function(inNode, inAlignWithTop) {
		this.$.strategy.scrollToNode(inNode, inAlignWithTop);
	},
	// normalize scroll event to onScroll.
	domScroll: function(inSender, e) {
		// if a scroll event originated here, pass it to our strategy to handle
		if (this.$.strategy.domScroll && e.originator == this) {
			this.$.strategy.scroll(inSender, e);
		}
		this.doScroll(e);
		return true;
	},
	shouldStopScrollEvent: function(inEvent) {
		return (this.preventScrollPropagation && inEvent.originator.owner != this.$.strategy);
	},
	scrollStart: function(inSender, inEvent) {
		return this.shouldStopScrollEvent(inEvent);
	},
	scroll: function(inSender, inEvent) {
		// note: scroll event can be native dom or generated.
		if (inEvent.dispatchTarget) {
			// allow a dom event if it orignated with this scroller or its strategy
			return this.preventScrollPropagation && !(inEvent.originator == this || inEvent.originator.owner == this.$.strategy);
		} else {
			return this.shouldStopScrollEvent(inEvent);
		}
	},
	scrollStop: function(inSender, inEvent) {
		return this.shouldStopScrollEvent(inEvent);
	},
	scrollToTop: function() {
		this.setScrollTop(0);
	},
	scrollToBottom: function() {
		this.setScrollTop(this.getScrollBounds().maxTop);
	},
	scrollToRight: function() {
		this.setScrollTop(this.getScrollBounds().maxLeft);
	},
	scrollToLeft: function() {
		this.setScrollLeft(0);
	}
});

// provide a touch scrolling solution by default when the environment is mobile
if (enyo.Scroller.hasTouchScrolling()) {
	enyo.Scroller.prototype.strategyKind = enyo.Scroller.getTouchStrategy();
}
<|endoftext|>


<commit_before>
/**
 * An animal that is part of an attack wave.
 */
function Animal() {
    /** @type AnimalType */
    this.type = null;
    this.health = 10;
    this.currentHealth = 10;
    this.speed = 10;
    this.armor = 0;
    this.carry = 1;
    this.burden = 0;
    this.damage = 1;
    //last structure the animal damaged. Used to figure out if they have
    //attacked the structure they are passing through yet
    this.lastTile = null;
    //Distance from start of path to current position. How far the animal has traveled.
    this.distance = 0;
    this.angle = 0;
    this.finished = false;
    this.dead = false;
    //actual coordinates on the map in pixels
    this.mapX = 0;
    this.mapY = 0;
    //coordinates of the tile most recently reached
    this.tileX = 0;
    this.tileY = 0;
    this.spawnTime = 0;
    /** @type Path */
    this.path = null;
    //marks that the animal is being displaced in the timeline by the current
    //drag operation
    this.moved = false;
    this.classType = 'Animal';
}

/**
 * Definition of an animal type
 */
function AnimalType() {
    this.single = '';
    this.plural = '';
    this.health = 10;
    this.healthGrowth = 1;
    this.speed = 10;
    this.speedGrowth = 0;
    this.armor = 0;
    this.armorGrowth = 0;
    this.carry = 1;
    this.carryGrowth = 0;
    this.damage = 1;
    this.damageGrowth = 0;
    //these tags are used for matching modifier abilities like
    //"increase the armor of all 'birds'"
    this.tags = [];
    this.spriteIndex = 0;
    this.numberOfFrames = 6;
}
/**
 * @param {Object} data
 */
function createAnimalType(data) {
    var type = new AnimalType();
    for (var i in data) {
        if (data.hasOwnProperty(i)) {
            type[i] = data[i];
        }
    }
    return type;
}

var animalTypes = {
    'cardinal': createAnimalType({'single': 'cardinal', 'plural': 'cardinals',
                                    'health': 10, 'healthGrowth': 1, 'speed': 20, 'speedGrowth': 1,
                                    'armor': 0, 'armorGrowth': 0, 'carry': 1, 'carryGrowth': 0,
                                    'damage': 1, 'damageGrowth': .1, tags: ['flying', 'bird'],
                                    'spriteIndex': 0, 'numberOfFrames': 5}),
    'snake': createAnimalType({'single': 'snake', 'plural': 'snakes',
                                    'health': 10, 'healthGrowth': 1, 'speed': 15, 'speedGrowth': 0,
                                    'armor': 0, 'armorGrowth': .2, 'carry': 1, 'carryGrowth': 0,
                                    'damage': 2, 'damageGrowth': .5, tags: ['reptile'], 'spriteIndex': 2}),
    'penguin': createAnimalType({'single': 'penguin', 'plural': 'penguins',
                                    'health': 20, 'healthGrowth': 2, 'speed': 10, 'speedGrowth': 0,
                                    'armor': 0, 'armorGrowth': .2, 'carry': 2, 'carryGrowth': 0,
                                    'damage': 1, 'damageGrowth': .1, tags: ['bird'], 'spriteIndex': 1}),
    'zebra': createAnimalType({'single': 'zebra', 'plural': 'zebras',
                                    'health': 30, 'healthGrowth': 3, 'speed': 20, 'speedGrowth': 0,
                                    'armor': 1, 'armorGrowth': .1, 'carry': 1, 'carryGrowth': 0,
                                    'damage': 2, 'damageGrowth': .1, tags: ['mammal'],
                                    'spriteIndex': 3, 'numberOfFrames': 4}),
}

/**
 * Creates an animal for the given type string.
 *
 * @param {String} typeString  id of the type to use
 * @return {Animal}  The instantiated animatl
 */
function createAnimal(typeString) {
    var animal = new Animal();
    animal.type = animalTypes[typeString];
    updateAnimal(state, animal);
    animal.currentHealth = animal.health;
    return animal;
}

/**
 * Updates the stats of an animal based on the current state, applying growth
 * modifiers and ability modifiers to determine the actual stats.
 *
 * @param {State} state  state of the game
 * @param {Animal} animal  the animal to update
 */
function updateAnimal(state, animal) {
    //This code should be like:
    var modifiers = [];
    $.each(animal.type.tags.concat('animal'), function (index, tag) {
        if (state.levelModifiers[tag]) {
            modifiers = modifiers.concat(state.levelModifiers[tag]);
        }
        if (state.waveModifiers[tag]) {
            modifiers = modifiers.concat(state.waveModifiers[tag]);
        }
    });
    $.each(['health', 'speed', 'armor', 'carry', 'damage'], function (index, stat) {
        var plusModifier = 0;
        var multiplier = 1;
        $.each(modifiers, function (index, modifier) {
            if (modifier.name == stat + 'Plus') {
                plusModifier += modifier.value;
            }
            if (modifier.name == stat + 'Times') {
                multiplier *= modifier.value;
            }
        });
        animal[stat] = Math.floor((animal.type[stat] + animal.type[stat + 'Growth'] * state.waveNumber + plusModifier) * multiplier);
    });
}

/**
 * Draw an animal sprite to a given context.
 *
 * @param {context} context  The context to draw to
 * @param {Number} x  The x coordinate to draw to
 * @param {Number} y  The y coordinate to draw to
 * @param {Animal} animal  The animal to draw
 * @param {Number} time  The time in milliseconds
 * @param {Number} rotation  The rotation to draw the sprite at
 */
function drawAnimalSprite(context, x, y, animal, time, rotation){ //temporarily just the penguin. srcY determines what row (0 is penguin)
    var frameDuration = 200;
    var numberOfFrames = animal.type.numberOfFrames;
    var sourceX = (Math.floor(time / frameDuration) % numberOfFrames);
    drawTileRotated(context, x, y, new TileSource(game.images.animals, sourceX, animal.type.spriteIndex, defaultTileSize), rotation);
}

/**
 *
 * @param {context} context  The context to draw to
 * @param {Animal} animal
 * @param {Number} x  The x coordinate to draw to
 * @param {Number} y  The y coordinate to draw to
 */
function drawAnimalHealth(context, animal, x, y) {
    var percent = animal.currentHealth / animal.health;
    context.fillStyle = "black";
    context.fillRect(x + 2, y + 2, 26, 2);
    if (percent > .6) {
        context.fillStyle = "#0C0";
    } else if (percent > .3) {
        context.fillStyle = "yellow";
    } else {
        context.fillStyle = "red";
    }
    context.fillRect(x + 2, y + 2, Math.ceil(percent * 26), 2);
}

/**
 * @param {Animal} animal
 */
function updateAnimalPosition(animal) {
    var tileSize = 30;
    var points = animal.path.points;
    var index = Math.floor(animal.distance / tileSize);
    //has already finished or not yet started
    if (index < 0) {
        return;
    }
    if (index >= points.length - 1) {
        animal.finished = true;
        return;
    }
    var percent = (animal.distance % tileSize) / tileSize;
    var x2 = points[index + 1][0];
    var y2 = points[index + 1][1];
    var x1 = points[index][0];
    var y1 = points[index][1];
    animal.tileX = x1;
    animal.tileY = y1;
    animal.mapX = (x1 + percent * (x2 - x1)) * tileSize;
    animal.mapY = (y1 + percent * (y2 - y1)) * tileSize;
    animal.angle = atan2(x1, y1, x2, y2);
}

/**
 * @param {State} state
 * @return {Array}
 */
function getAnimals(state) {
    var animals = [];
    for (var i = 0; i < state.paths.length; i++) {
        for (var j = 0; j < state.paths[i].slots.length; j++) {
            /** @type Animal */
            var animal = state.paths[i].slots[j];
            if (animal) {
                animal.path = state.paths[i];
                animal.spawnTime = j * 200;
                animals.push(animal);
            }
        }
    }
    return animals;
}

function damageAnimal(animal, damage) {
    animal.currentHealth -= damage;
    if (animal.currentHealth <= 0) {
        animal.currentHealth = 0;
        animal.finished = true;
        animal.dead = true;
    }
}<commit_msg>When changing the max health of animals, update their current health proportional to the max health. That is if the animal has full life, if we double its health, it should remain at full life instead of 50% health.<commit_after>
/**
 * An animal that is part of an attack wave.
 */
function Animal() {
    /** @type AnimalType */
    this.type = null;
    this.health = 10;
    this.currentHealth = 10;
    //used to update currentHealth proportionally when health changes
    this.healthPercent = 1;
    this.speed = 10;
    this.armor = 0;
    this.carry = 1;
    this.burden = 0;
    this.damage = 1;
    //last structure the animal damaged. Used to figure out if they have
    //attacked the structure they are passing through yet
    this.lastTile = null;
    //Distance from start of path to current position. How far the animal has traveled.
    this.distance = 0;
    this.angle = 0;
    this.finished = false;
    this.dead = false;
    //actual coordinates on the map in pixels
    this.mapX = 0;
    this.mapY = 0;
    //coordinates of the tile most recently reached
    this.tileX = 0;
    this.tileY = 0;
    this.spawnTime = 0;
    /** @type Path */
    this.path = null;
    //marks that the animal is being displaced in the timeline by the current
    //drag operation
    this.moved = false;
    this.classType = 'Animal';
}

/**
 * Definition of an animal type
 */
function AnimalType() {
    this.single = '';
    this.plural = '';
    this.health = 10;
    this.healthGrowth = 1;
    this.speed = 10;
    this.speedGrowth = 0;
    this.armor = 0;
    this.armorGrowth = 0;
    this.carry = 1;
    this.carryGrowth = 0;
    this.damage = 1;
    this.damageGrowth = 0;
    //these tags are used for matching modifier abilities like
    //"increase the armor of all 'birds'"
    this.tags = [];
    this.spriteIndex = 0;
    this.numberOfFrames = 6;
}
/**
 * @param {Object} data
 */
function createAnimalType(data) {
    var type = new AnimalType();
    for (var i in data) {
        if (data.hasOwnProperty(i)) {
            type[i] = data[i];
        }
    }
    return type;
}

var animalTypes = {
    'cardinal': createAnimalType({'single': 'cardinal', 'plural': 'cardinals',
                                    'health': 10, 'healthGrowth': 1, 'speed': 20, 'speedGrowth': 1,
                                    'armor': 0, 'armorGrowth': 0, 'carry': 1, 'carryGrowth': 0,
                                    'damage': 1, 'damageGrowth': .1, tags: ['flying', 'bird'],
                                    'spriteIndex': 0, 'numberOfFrames': 5}),
    'snake': createAnimalType({'single': 'snake', 'plural': 'snakes',
                                    'health': 10, 'healthGrowth': 1, 'speed': 15, 'speedGrowth': 0,
                                    'armor': 0, 'armorGrowth': .2, 'carry': 1, 'carryGrowth': 0,
                                    'damage': 2, 'damageGrowth': .5, tags: ['reptile'], 'spriteIndex': 2}),
    'penguin': createAnimalType({'single': 'penguin', 'plural': 'penguins',
                                    'health': 20, 'healthGrowth': 2, 'speed': 10, 'speedGrowth': 0,
                                    'armor': 0, 'armorGrowth': .2, 'carry': 2, 'carryGrowth': 0,
                                    'damage': 1, 'damageGrowth': .1, tags: ['bird'], 'spriteIndex': 1}),
    'zebra': createAnimalType({'single': 'zebra', 'plural': 'zebras',
                                    'health': 30, 'healthGrowth': 3, 'speed': 20, 'speedGrowth': 0,
                                    'armor': 1, 'armorGrowth': .1, 'carry': 1, 'carryGrowth': 0,
                                    'damage': 2, 'damageGrowth': .1, tags: ['mammal'],
                                    'spriteIndex': 3, 'numberOfFrames': 4}),
}

/**
 * Creates an animal for the given type string.
 *
 * @param {String} typeString  id of the type to use
 * @return {Animal}  The instantiated animatl
 */
function createAnimal(typeString) {
    var animal = new Animal();
    animal.type = animalTypes[typeString];
    updateAnimal(state, animal);
    animal.currentHealth = animal.health;
    return animal;
}

/**
 * Updates the stats of an animal based on the current state, applying growth
 * modifiers and ability modifiers to determine the actual stats.
 *
 * @param {State} state  state of the game
 * @param {Animal} animal  the animal to update
 */
function updateAnimal(state, animal) {
    //This code should be like:
    var modifiers = [];
    $.each(animal.type.tags.concat('animal'), function (index, tag) {
        if (state.levelModifiers[tag]) {
            modifiers = modifiers.concat(state.levelModifiers[tag]);
        }
        if (state.waveModifiers[tag]) {
            modifiers = modifiers.concat(state.waveModifiers[tag]);
        }
    });
    $.each(['health', 'speed', 'armor', 'carry', 'damage'], function (index, stat) {
        var plusModifier = 0;
        var multiplier = 1;
        $.each(modifiers, function (index, modifier) {
            if (modifier.name == stat + 'Plus') {
                plusModifier += modifier.value;
            }
            if (modifier.name == stat + 'Times') {
                multiplier *= modifier.value;
            }
        });
        animal[stat] = Math.floor((animal.type[stat] + animal.type[stat + 'Growth'] * state.waveNumber + plusModifier) * multiplier);
    });
    animal.currentHealth = animal.health * animal.healthPercent;
}

/**
 * Draw an animal sprite to a given context.
 *
 * @param {context} context  The context to draw to
 * @param {Number} x  The x coordinate to draw to
 * @param {Number} y  The y coordinate to draw to
 * @param {Animal} animal  The animal to draw
 * @param {Number} time  The time in milliseconds
 * @param {Number} rotation  The rotation to draw the sprite at
 */
function drawAnimalSprite(context, x, y, animal, time, rotation){ //temporarily just the penguin. srcY determines what row (0 is penguin)
    var frameDuration = 200;
    var numberOfFrames = animal.type.numberOfFrames;
    var sourceX = (Math.floor(time / frameDuration) % numberOfFrames);
    drawTileRotated(context, x, y, new TileSource(game.images.animals, sourceX, animal.type.spriteIndex, defaultTileSize), rotation);
}

/**
 *
 * @param {context} context  The context to draw to
 * @param {Animal} animal
 * @param {Number} x  The x coordinate to draw to
 * @param {Number} y  The y coordinate to draw to
 */
function drawAnimalHealth(context, animal, x, y) {
    var percent = animal.currentHealth / animal.health;
    context.fillStyle = "black";
    context.fillRect(x + 2, y + 2, 26, 2);
    if (percent > .6) {
        context.fillStyle = "#0C0";
    } else if (percent > .3) {
        context.fillStyle = "yellow";
    } else {
        context.fillStyle = "red";
    }
    context.fillRect(x + 2, y + 2, Math.ceil(percent * 26), 2);
}

/**
 * @param {Animal} animal
 */
function updateAnimalPosition(animal) {
    var tileSize = 30;
    var points = animal.path.points;
    var index = Math.floor(animal.distance / tileSize);
    //has already finished or not yet started
    if (index < 0) {
        return;
    }
    if (index >= points.length - 1) {
        animal.finished = true;
        return;
    }
    var percent = (animal.distance % tileSize) / tileSize;
    var x2 = points[index + 1][0];
    var y2 = points[index + 1][1];
    var x1 = points[index][0];
    var y1 = points[index][1];
    animal.tileX = x1;
    animal.tileY = y1;
    animal.mapX = (x1 + percent * (x2 - x1)) * tileSize;
    animal.mapY = (y1 + percent * (y2 - y1)) * tileSize;
    animal.angle = atan2(x1, y1, x2, y2);
}

/**
 * @param {State} state
 * @return {Array}
 */
function getAnimals(state) {
    var animals = [];
    for (var i = 0; i < state.paths.length; i++) {
        for (var j = 0; j < state.paths[i].slots.length; j++) {
            /** @type Animal */
            var animal = state.paths[i].slots[j];
            if (animal) {
                animal.path = state.paths[i];
                animal.spawnTime = j * 200;
                animals.push(animal);
            }
        }
    }
    return animals;
}

/**
 * @param {Animal} animal
 * @param {Number} damage
 */
function damageAnimal(animal, damage) {
    animal.currentHealth -= damage;
    if (animal.currentHealth <= 0) {
        animal.currentHealth = 0;
        animal.finished = true;
        animal.dead = true;
    }
    animal.healthPercent = animal.currentHealth / animal.health;
}<|endoftext|>


<commit_before>var mongoose = require('mongoose')
var schema = require('../server/schema')
var utils = require('../shared/utils')
var _ = require('lodash')
var xlsx = require('xlsx')
var xls = require('xlsjs')
var async = require('async')
var enums = require('../shared/enums')

var providerFieldMap = {
  'address.city':    'Postitoimipaikka',
  'address.country': 'Maa',
  'address.street':  'Lähiosoite',
  'address.zip':     'Postinro',
  'contactName':     'Tarjoajan yhteyshenkilö',
  'emailAddresses':  'Sähköposti',
  'language':        'Asiointikieli',
  'name':            'Yritys/Toiminimi',
  'phoneNumber':     'Puhelinnumero',
  'yTunnus':         'Y-tunnus'
}

var billingFieldMap = {
  'billing.address.city':    'Postitoimipaikka',
  //'billing.address.country': 'Maa', // not imported, though in schema and in UI
  'billing.address.street':  'Lähiosoite',
  'billing.address.zip':     'Postinro',
  'billing.invoiceText':     'Laskulle haluttava teksti',
  'eInvoice.address':        'Verkkolaskuosoite (OVT/IBAN)',
  'eInvoice.operator':       'Välittäjätunnus'
}

// Invert
var locationFieldMap = {
  'address.city':     'Postitoimipaikka',
  'address.street':   'Lähiosoite',
  'address.zip':      'Postinro',
  'adultContent':     'K18 ohjelmia',
  'contactName':      'Tarjoamispaikan yhteyshenkilö',
  'emailAddresses':   'Sähköpostiosoite',
  'gamesWithoutPegi': 'Pelejä (ei PEGI)',
  'isPayer':          'Lasku Tarjoamispaikalle',
  'name':             'Tarjoamispaikan nimi',
  'phoneNumber':      'Puhelinnumero',
  //'providingType':    '', // special case
  'url':              'www-osoite'
}

exports.import = function(file, callback) {
  try {
    var parser = false

    if (file.search(/.xls$/) > 1) parser = xls
    else if (file.search(/.xlsx$/) > 1) parser = xlsx
    else return callback({ message: '.xlsx or .xls required' })

    var providerAndLocations = getProviderAndLocationsFromSpreadSheet(parser.readFile(file).Sheets['Tarjoajaksi ilmoittautuminen'])
  } catch (err) {
    return callback(err)
  }

  var provider = createProvider(providerAndLocations.provider, providerAndLocations.billing)
  provider.locations = createLocations(providerAndLocations.locations)

  return new schema.Provider(_.omit(provider, 'other')).save(function(err, provider) { return callback(err, provider) })

  function createProvider(providerData, billingData) {
    var provider = createObjectAndSetValuesWithMap(providerData, providerFieldMap)
    provider.deleted = false
    provider.active = false
    provider.creationDate = new Date()
    provider.language = (provider.language && provider.language.toLowerCase() == 'svenska') ? 'SV' : 'FI'
    provider.address.country = enums.getCountryCode(provider.address.country) || provider.address.country

    var billing = createObjectAndSetValuesWithMap(billingData, billingFieldMap)

    provider = _.merge({}, provider, billing)

    if (provider.billing && provider.billing.address) {
      var address = provider.billing.address
      provider.billing.address.country = enums.getCountryCode(address.country) || address.country
      provider.billingPreference = 'address'
    }

    if (provider.eInvoice) {
      provider.billingPreference = 'eInvoice'
    }

    return provider
  }

  function createLocations(locationsData) {
    var locations = []

    _.forEach(locationsData, function(location) {
      location = createObjectAndSetValuesWithMap(location, locationFieldMap, {
        providingType: {
          fields: [
            'Tallenteiden tarjoaminen', 'Julkinen esittäminen', 'Valtakunnallinen TV-ohjelmisto',
            'Alueellinen ohjelmisto', 'Ulkomailta välitetty ohjelm.', 'Tilausohjelma-palvelu'
          ],
          toValue: enums.getProvidingType
        }
      })
      location.deleted = false
      location.active = true
      location.isPayer = stringXToBoolean(location.isPayer)
      location.adultContent = stringXToBoolean(location.adultContent)
      location.gamesWithoutPegi = stringXToBoolean(location.gamesWithoutPegi)

      locations.push(location)
    })

    return locations
  }

  function stringXToBoolean(x) { return _.isString(x) ? x.toLowerCase() === 'x' : false }
}

function getProviderAndLocationsFromSpreadSheet(providerSheet) {
  // TODO: check
  var requiredProviderFields = {
    name: 'Tarjoaja',
    values: ['name', 'yTunnus', 'contactName', 'address.street', 'address.zip', 'address.city', 'phoneNumber', 'language'],
    fieldMap: providerFieldMap
  }

  // TODO: check
  var requiredLocationFields = {
    name: 'Tarjoamispaikkojen tiedot ja tarjoamistavat',
    values: ['name', 'contactName', 'address.street', 'address.zip', 'address.city', 'phoneNumber'],
    fieldMap: locationFieldMap
  }

  var errors = []

  var providerData = parseFieldRowAndValueRowAfterColumn('Tarjoaja', requiredProviderFields)
  var billingData = parseFieldRowAndValueRowAfterColumn('Lasku eri osoitteeseen kuin Tarjoajan osoite')
  var eInvoiceData = parseFieldRowAndValueRowAfterColumn('TAI verkkolasku')
  var locationsData = parseFieldRowAndValueRowsAfterColumn('Tarjoamispaikkojen tiedot ja tarjoamistavat', requiredLocationFields)

  if (locationsData.length === 0) errors.push('Tarjoamispaikkojen tiedot puuttuvat')

  if (errors.length > 0) throw { message: errors.join(', ') }

  return {
    provider: providerData,
    billing: _.merge({}, billingData, eInvoiceData),
    locations: locationsData
  }

  function parseFieldRowAndValueRowAfterColumn(column, requiredFields) {
    var rowNumber = findColumnRowByValue(providerSheet, column)
    return toObject(getFields(providerSheet, rowNumber + 1), getValues(providerSheet, rowNumber + 2), requiredFields)
  }

  function findColumnRowByValue(from, value) {
    var columnRowNumber = undefined

    _.find(from, function(field, key) {
      if (field.v && (field.v.trim().toLowerCase() == value.toLowerCase())) {
        columnRowNumber = parseRowNumber(key)
        return true
      }
    })

    return columnRowNumber

    function parseRowNumber(key) { return parseInt(key.substring(key.search(/\d/))) }
  }

  function toObject(fields, values, requiredFields) {
    fields = _.clone(fields)
    var object = _.reduce(values, function(result, value) {
      result[fields[value[0]]] = value[1]
      delete fields[value[0]]
      return result
    }, {})

    if (requiredFields) {
      var reqFields = _.map(requiredFields.values, function(value) {
        return requiredFields.fieldMap[value]
      })

      _.forEach(fields, function (field) {
        if (_.contains(reqFields, field)) {
          var errorString = '<%- name %> pakollinen kenttä "<%- field %>" puuttuu'
          if (requiredFields.row) errorString = 'Rivillä <%- row %>. ' + errorString

          errors.push(_.template(errorString, {
            name: requiredFields.name, field: field, row: requiredFields.row
          }))
        }
      })
    }

    return object
  }

  function getFields(from, row) {
    return _.reduce(getValues(from, row), function(result, value) {
      result[value[0]] = value[1]
      return result
    }, {})
  }

  function getValues(from, row) {
    return _.reduce(from, function(result, field, key) {
      if (key.match(new RegExp('.' + row))) result.push([parseColumnKey(key), field.v])
      return result
    }, [])

    function parseColumnKey(key) { return key.substring(0, key.search(/\d/)) }
  }

  function parseFieldRowAndValueRowsAfterColumn(column, requiredFields) {
    var rowNumber = findColumnRowByValue(providerSheet, column)
    var fields = getFields(providerSheet, rowNumber + 1)

    var index = rowNumber + 2
    var values = []

    while (true) {
      var currentValues = getValues(providerSheet, index)
      if (currentValues.length > 0) {
        values.push(toObject(fields, currentValues, _.merge(requiredFields, { row: index })))
        index += 1
      }
      else return values
    }
  }
}

function createObjectAndSetValuesWithMap(object, map, specialFields) {
  var newObject = {}

  map = _.invert(map)

  _.forEach(utils.flattenObject(object), function(value, key) {
    if (map[key]) {
      utils.setValueForPath(map[key].split('.'), newObject, value)
    } else {
      var arrayProperty = getArrayPropertyKeyAndValue(key)
      if (arrayProperty) {
        if (_.isArray(newObject[arrayProperty[0]])) {
          newObject[arrayProperty[0]].push(arrayProperty[1])
        } else {
          newObject[arrayProperty[0]] = [arrayProperty[1]]
        }
      } else {
        utils.setValueForPath(['other', key], newObject, value)
      }
    }
  })

  return newObject

  function getArrayPropertyKeyAndValue(key) {
    var propertyName = _.findKey(specialFields, function(property) { return _.contains(property.fields, key) })

    if (propertyName) {
      var property = specialFields[propertyName]

      return [ propertyName, property.toValue(_.indexOf(property.fields, key)) ]
    } else {
      return undefined
    }
  }
}
<commit_msg>Turn forEach() into a map()<commit_after>var mongoose = require('mongoose')
var schema = require('../server/schema')
var utils = require('../shared/utils')
var _ = require('lodash')
var xlsx = require('xlsx')
var xls = require('xlsjs')
var async = require('async')
var enums = require('../shared/enums')

var providerFieldMap = {
  'address.city':    'Postitoimipaikka',
  'address.country': 'Maa',
  'address.street':  'Lähiosoite',
  'address.zip':     'Postinro',
  'contactName':     'Tarjoajan yhteyshenkilö',
  'emailAddresses':  'Sähköposti',
  'language':        'Asiointikieli',
  'name':            'Yritys/Toiminimi',
  'phoneNumber':     'Puhelinnumero',
  'yTunnus':         'Y-tunnus'
}

var billingFieldMap = {
  'billing.address.city':    'Postitoimipaikka',
  //'billing.address.country': 'Maa', // not imported, though in schema and in UI
  'billing.address.street':  'Lähiosoite',
  'billing.address.zip':     'Postinro',
  'billing.invoiceText':     'Laskulle haluttava teksti',
  'eInvoice.address':        'Verkkolaskuosoite (OVT/IBAN)',
  'eInvoice.operator':       'Välittäjätunnus'
}

// Invert
var locationFieldMap = {
  'address.city':     'Postitoimipaikka',
  'address.street':   'Lähiosoite',
  'address.zip':      'Postinro',
  'adultContent':     'K18 ohjelmia',
  'contactName':      'Tarjoamispaikan yhteyshenkilö',
  'emailAddresses':   'Sähköpostiosoite',
  'gamesWithoutPegi': 'Pelejä (ei PEGI)',
  'isPayer':          'Lasku Tarjoamispaikalle',
  'name':             'Tarjoamispaikan nimi',
  'phoneNumber':      'Puhelinnumero',
  //'providingType':    '', // special case
  'url':              'www-osoite'
}

exports.import = function(file, callback) {
  try {
    var parser = false

    if (file.search(/.xls$/) > 1) parser = xls
    else if (file.search(/.xlsx$/) > 1) parser = xlsx
    else return callback({ message: '.xlsx or .xls required' })

    var providerAndLocations = getProviderAndLocationsFromSpreadSheet(parser.readFile(file).Sheets['Tarjoajaksi ilmoittautuminen'])
  } catch (err) {
    return callback(err)
  }

  var provider = createProvider(providerAndLocations.provider, providerAndLocations.billing)
  provider.locations = createLocations(providerAndLocations.locations)

  return new schema.Provider(_.omit(provider, 'other')).save(function(err, provider) { return callback(err, provider) })

  function createProvider(providerData, billingData) {
    var provider = createObjectAndSetValuesWithMap(providerData, providerFieldMap)
    provider.deleted = false
    provider.active = false
    provider.creationDate = new Date()
    provider.language = (provider.language && provider.language.toLowerCase() == 'svenska') ? 'SV' : 'FI'
    provider.address.country = enums.getCountryCode(provider.address.country) || provider.address.country

    var billing = createObjectAndSetValuesWithMap(billingData, billingFieldMap)

    provider = _.merge({}, provider, billing)

    if (provider.billing && provider.billing.address) {
      var address = provider.billing.address
      provider.billing.address.country = enums.getCountryCode(address.country) || address.country
      provider.billingPreference = 'address'
    }

    if (provider.eInvoice) {
      provider.billingPreference = 'eInvoice'
    }

    return provider
  }

  function createLocations(locationsData) {
    return _.map(locationsData, function(location) {
      var location = createObjectAndSetValuesWithMap(location, locationFieldMap, {
        providingType: {
          fields: [
            'Tallenteiden tarjoaminen', 'Julkinen esittäminen', 'Valtakunnallinen TV-ohjelmisto',
            'Alueellinen ohjelmisto', 'Ulkomailta välitetty ohjelm.', 'Tilausohjelma-palvelu'
          ],
          toValue: enums.getProvidingType
        }
      })

      return utils.merge(location, {
        deleted: false,
        active: true,
        isPayer: stringXToBoolean(location.isPayer),
        adultContent: stringXToBoolean(location.adultContent),
        gamesWithoutPegi: stringXToBoolean(location.gamesWithoutPegi)
      })
    })
  }
  function stringXToBoolean(x) { return _.isString(x) ? x.toLowerCase() === 'x' : false }
}

function getProviderAndLocationsFromSpreadSheet(providerSheet) {
  // TODO: check
  var requiredProviderFields = {
    name: 'Tarjoaja',
    values: ['name', 'yTunnus', 'contactName', 'address.street', 'address.zip', 'address.city', 'phoneNumber', 'language'],
    fieldMap: providerFieldMap
  }

  // TODO: check
  var requiredLocationFields = {
    name: 'Tarjoamispaikkojen tiedot ja tarjoamistavat',
    values: ['name', 'contactName', 'address.street', 'address.zip', 'address.city', 'phoneNumber'],
    fieldMap: locationFieldMap
  }

  var errors = []

  var providerData = parseFieldRowAndValueRowAfterColumn('Tarjoaja', requiredProviderFields)
  var billingData = parseFieldRowAndValueRowAfterColumn('Lasku eri osoitteeseen kuin Tarjoajan osoite')
  var eInvoiceData = parseFieldRowAndValueRowAfterColumn('TAI verkkolasku')
  var locationsData = parseFieldRowAndValueRowsAfterColumn('Tarjoamispaikkojen tiedot ja tarjoamistavat', requiredLocationFields)

  if (locationsData.length === 0) errors.push('Tarjoamispaikkojen tiedot puuttuvat')

  if (errors.length > 0) throw { message: errors.join(', ') }

  return {
    provider: providerData,
    billing: _.merge({}, billingData, eInvoiceData),
    locations: locationsData
  }

  function parseFieldRowAndValueRowAfterColumn(column, requiredFields) {
    var rowNumber = findColumnRowByValue(providerSheet, column)
    return toObject(getFields(providerSheet, rowNumber + 1), getValues(providerSheet, rowNumber + 2), requiredFields)
  }

  function findColumnRowByValue(from, value) {
    var columnRowNumber = undefined

    _.find(from, function(field, key) {
      if (field.v && (field.v.trim().toLowerCase() == value.toLowerCase())) {
        columnRowNumber = parseRowNumber(key)
        return true
      }
    })

    return columnRowNumber

    function parseRowNumber(key) { return parseInt(key.substring(key.search(/\d/))) }
  }

  function toObject(fields, values, requiredFields) {
    fields = _.clone(fields)
    var object = _.reduce(values, function(result, value) {
      result[fields[value[0]]] = value[1]
      delete fields[value[0]]
      return result
    }, {})

    if (requiredFields) {
      var reqFields = _.map(requiredFields.values, function(value) {
        return requiredFields.fieldMap[value]
      })

      _.forEach(fields, function (field) {
        if (_.contains(reqFields, field)) {
          var errorString = '<%- name %> pakollinen kenttä "<%- field %>" puuttuu'
          if (requiredFields.row) errorString = 'Rivillä <%- row %>. ' + errorString

          errors.push(_.template(errorString, {
            name: requiredFields.name, field: field, row: requiredFields.row
          }))
        }
      })
    }

    return object
  }

  function getFields(from, row) {
    return _.reduce(getValues(from, row), function(result, value) {
      result[value[0]] = value[1]
      return result
    }, {})
  }

  function getValues(from, row) {
    return _.reduce(from, function(result, field, key) {
      if (key.match(new RegExp('.' + row))) result.push([parseColumnKey(key), field.v])
      return result
    }, [])

    function parseColumnKey(key) { return key.substring(0, key.search(/\d/)) }
  }

  function parseFieldRowAndValueRowsAfterColumn(column, requiredFields) {
    var rowNumber = findColumnRowByValue(providerSheet, column)
    var fields = getFields(providerSheet, rowNumber + 1)

    var index = rowNumber + 2
    var values = []

    while (true) {
      var currentValues = getValues(providerSheet, index)
      if (currentValues.length > 0) {
        values.push(toObject(fields, currentValues, _.merge(requiredFields, { row: index })))
        index += 1
      }
      else return values
    }
  }
}

function createObjectAndSetValuesWithMap(object, map, specialFields) {
  var newObject = {}

  map = _.invert(map)

  _.forEach(utils.flattenObject(object), function(value, key) {
    if (map[key]) {
      utils.setValueForPath(map[key].split('.'), newObject, value)
    } else {
      var arrayProperty = getArrayPropertyKeyAndValue(key)
      if (arrayProperty) {
        if (_.isArray(newObject[arrayProperty[0]])) {
          newObject[arrayProperty[0]].push(arrayProperty[1])
        } else {
          newObject[arrayProperty[0]] = [arrayProperty[1]]
        }
      } else {
        utils.setValueForPath(['other', key], newObject, value)
      }
    }
  })

  return newObject

  function getArrayPropertyKeyAndValue(key) {
    var propertyName = _.findKey(specialFields, function(property) { return _.contains(property.fields, key) })

    if (propertyName) {
      var property = specialFields[propertyName]

      return [ propertyName, property.toValue(_.indexOf(property.fields, key)) ]
    } else {
      return undefined
    }
  }
}
<|endoftext|>


<commit_before>var helpTopics = {};

function addModule (module) {
	var entry = {
		name : module.displayname,
		description : module.description,
		commands : []
	};

	if (module.commands) {
		var command;
		for (var i = 0; i < module.commands.length; i++) {
			command = module.commands[i];
			entry.commands.push({
				name : command.name,
				description : command.description,
				usage : command.usage
			});
		}
	}

	helpTopics[module.displayname] = entry;
}

function initHelp(bot, modules) {
	// Add all previously loaded modules
	for (var i = 0; i < modules.loaded.length; i++) {
		addModule(modules.loaded[i]);
	}

	// Add all future modules
	bot.events.on('moduleLoaded', addModule);
}

function displayHelp (route) {
	var out = ['Available commands:'];
	var module;
	var command;

	for (var module_name in helpTopics) {
		module = helpTopics[module_name];
		for (var i = 0; i < module.commands.length; i++) {
			command = module.commands[i];
			out.push('(' + module_name + ') ' + command.name + ' - ' + command.usage);
		}
	}

	route.direct().send(out.join('\n'));
}

module.exports = {
	displayname : 'Help',
	description : 'Online chat manual',

	init : initHelp,

	commands : [{
		name : 'Show Commands',
		description : 'Show all available commands',
		usage : 'help [topic]',
		trigger : /help/i,
		func : displayHelp,
		core : true
	}]
};<commit_msg>adding help link<commit_after>var helpTopics = {};

function addModule (module) {
	var entry = {
		name : module.displayname,
		description : module.description,
		commands : []
	};

	if (module.commands) {
		var command;
		for (var i = 0; i < module.commands.length; i++) {
			command = module.commands[i];
			entry.commands.push({
				name : command.name,
				description : command.description,
				usage : command.usage
			});
		}
	}

	helpTopics[module.displayname] = entry;
}

function initHelp(bot, modules) {
	// Add all previously loaded modules
	for (var i = 0; i < modules.loaded.length; i++) {
		addModule(modules.loaded[i]);
	}

	// Add all future modules
	bot.events.on('moduleLoaded', addModule);
}

function displayHelp (route) {
	var out = ['Available commands:'];
	var module;
	var command;

	for (var module_name in helpTopics) {
		module = helpTopics[module_name];
		for (var i = 0; i < module.commands.length; i++) {
			command = module.commands[i];
			out.push('(' + module_name + ') ' + command.name + ' - ' + command.usage);
		}
	}

	if (this.web && this.config.web_host) {
		out.push('Additional information available at http://' + this.config.web_host + '/commands');
	}

	route.direct().send(out.join('\n'));
}

module.exports = {
	displayname : 'Help',
	description : 'Online chat manual',

	init : initHelp,

	commands : [{
		name : 'Show Commands',
		description : 'Show all available commands',
		usage : 'help [topic]',
		trigger : /help/i,
		func : displayHelp,
		core : true
	}]
};<|endoftext|>


<commit_before>import rule, { ruleName, messages } from "..";

testRule(rule, {
  ruleName,
  config: [true],
  syntax: "scss",

  accept: [
    {
      code: "// comment",
      description: "Double slash comments"
    }
  ],
  reject: [
    {
      code: `
      /* comment */
    `,
      description: "One line with optional ending",
      message: messages.expected
    },
    {
      code: "/* comment",
      description: "One line with no ending",
      message: messages.expected
    },
    {
      code: `
        /* comment line 1
           comment line 2
    `,
      description: "Multline comment with no ending",
      message: messages.expected
    },
    {
      code: `
        /* comment line 1
           comment line 2
        */
    `,
      description: "Multline comment with optional ending on next line",
      message: messages.expected
    }
  ]
});
<commit_msg>test changes<commit_after>import rule, { ruleName, messages } from "..";

testRule(rule, {
  ruleName,
  config: [true],
  syntax: "scss",

  accept: [
    {
      code: "// comment",
      description: "Double slash comments"
    }
  ],
  reject: [
    {
      code: `
      /* comment */
    `,
      description: "One line with optional ending",
      message: messages.expected
    },
    {
      code: `
        /* comment line 1
           comment line 2
        */
    `,
      description: "Multline comment with optional ending on next line",
      message: messages.expected
    }
  ]
});
<|endoftext|>


<commit_before>/*

	tdapp_controller_auth.js

*/
var tdapp = require('./tdapp');

tdapp.controller("AuthCtrl",function($scope,$http,$auth,$cookies,$window,$timeout,appdata,TDMgr,Backend,Autologin, $firebaseAuth){

	// Injection

	Autologin.setScope($scope);
	Backend.setScope($scope);

	// Satellizer

	$scope.authenticate = function(provider){
		$auth.authenticate(provider)
		.then(function(res){
			if($auth.isAuthenticated){
				var dtok = jwt_decode(res.data.token);
				$scope.user = {};
				$scope.user.id = dtok.sub;
			}
		});
	};

	// Login

	function gomain(){
		$timeout(function(){
			$window.location = "/#/main";
		},1000);
	}

	function locallogin(){ // No basic authentication (just communicate with localhost)
		$window.location = "/#/working";
		appdata.server = appdata.localserver;
		var now = new Date();
		var exp = new Date(now.getFullYear(), now.getMonth()+1, now.getDate());
		$cookies.put(appdata.cookiename,'bla',{expires:exp});
		$scope.filtertag = 'All'; // set filtertag before calling Backend.getTodos()
		//Backend.getTodos();
		$(".fkts").css("visibility","visible");
		$scope.errormsg = "";
		gomain();
	}

	$scope.dologin = function(){
		if($window.location.host=="localhost"){
			locallogin();
			return;
		}
		$auth.login($scope.user)
			.then(function(response){
				$window.location = "/#/working";
				// Create cookie
				var now = new Date();
				var exp = new Date(now.getFullYear(), now.getMonth()+1, now.getDate());
				$cookies.put(appdata.cookiename,response.data.token,{expires:exp});
				// Modifiy headers
				$http.defaults.headers.common['Authorization'] = "Basic " + response.data.token;
				$scope.filtertag = 'All'; // set filtertag before calling Backend.getTodos()
				//Backend.getTodos();
				$(".fkts").css("visibility","visible");
				$scope.errormsg = "";
				gomain();
			})
			.catch(function(error){
				$scope.errormsg = "Login-Error.";
				console.log(error);
			});
	}

	$scope.dologinWithGoogle = function() {
		var auth = $firebaseAuth();

		// login with google
		auth.$signInWithPopup("google").then(function(firebaseUser) {
		console.log("Signed in as:", firebaseUser.user.email);
		console.log("Credentials:", firebaseUser.credential);
		firebaseUser.user.getToken(false).then(function(idToken) {
			console.log("Token: ", idToken);
		});
		$scope.email = firebaseUser.user.email;
	  }).catch(function(error) {
		console.log("Authentication failed:", error);
	  });

	}

	// Keyboard
	$scope.loginKeydown = function(e){
		var k = e.keyCode;
		if(k==13){//ret
			e.preventDefault();
			$scope.dologin();
		}
	}

	// Register

	$scope.showRegister = function(){
		$window.location = "/#/register";
	};

	// Finish

	$("#liusername").focus()

	Autologin.check(); // do automatic login if cookie/token is available
});
<commit_msg>Vorbereitung Nutzung Firebase/Google-Token<commit_after>/*

	tdapp_controller_auth.js

*/
var tdapp = require('./tdapp');

tdapp.controller("AuthCtrl",function($scope,$http,$auth,$cookies,$window,$timeout,appdata,TDMgr,Backend,Autologin, $firebaseAuth){

	// Injection

	Autologin.setScope($scope);
	Backend.setScope($scope);

	// Satellizer

	$scope.authenticate = function(provider){
		$auth.authenticate(provider)
		.then(function(res){
			if($auth.isAuthenticated){
				var dtok = jwt_decode(res.data.token);
				$scope.user = {};
				$scope.user.id = dtok.sub;
			}
		});
	};

	// Login

	function gomain(){
		$timeout(function(){
			$window.location = "/#/main";
		},1000);
	}

	function locallogin(){ // No basic authentication (just communicate with localhost)
		$window.location = "/#/working";
		appdata.server = appdata.localserver;
		var now = new Date();
		var exp = new Date(now.getFullYear(), now.getMonth()+1, now.getDate());
		$cookies.put(appdata.cookiename,'bla',{expires:exp});
		$scope.filtertag = 'All'; // set filtertag before calling Backend.getTodos()
		$scope.errormsg = "";
		gomain();
	}

	$scope.dologin = function(){
		if($window.location.host=="localhost"){
			locallogin();
			return;
		}
		$auth.login($scope.user)
			.then(function(response){
				$window.location = "/#/working";
				// Create cookie
				var now = new Date();
				var exp = new Date(now.getFullYear(), now.getMonth()+1, now.getDate());
				$cookies.put(appdata.cookiename,response.data.token,{expires:exp});
				// Modifiy headers
				$http.defaults.headers.common['Authorization'] = "Basic " + response.data.token;
				$scope.filtertag = 'All'; // set filtertag before calling Backend.getTodos()
				$scope.errormsg = "";
				gomain();
			})
			.catch(function(error){
				$scope.errormsg = "Login-Error.";
				console.log(error);
			});
	}

	$scope.dologinWithGoogle = function(){
		var auth = $firebaseAuth();
		// Login with Firebase/Google
		auth.$signInWithPopup("google").then(function(firebaseUser){
			console.log("Signed in as: ", firebaseUser.user.email);
			console.log("Credentials: ", firebaseUser.credential);
			$scope.email = firebaseUser.user.email;
			firebaseUser.user.getToken(false).then(function(idToken){
				console.log("Token: ", idToken);
				// Create cookie
				var now = new Date();
				var exp = new Date(now.getFullYear(), now.getMonth()+1, now.getDate());
				$cookies.put(appdata.cookiename,idToken,{expires:exp});
				// Modifiy headers
				$http.defaults.headers.common['Authorization'] = "Basic " + idToken;
				$scope.filtertag = 'All'; // set filtertag before calling Backend.getTodos()
				$scope.errormsg = "";
				gomain();
			});
		}).catch(function(error){
			$scope.errormsg = "Login-Error."
			console.log("Authentication failed: ", error);
		});
	}

	// Keyboard

	$scope.loginKeydown = function(e){
		var k = e.keyCode;
		if(k==13){//ret
			e.preventDefault();
			$scope.dologin();
		}
	}

	// Register

	$scope.showRegister = function(){
		$window.location = "/#/register";
	};

	// Finish

	$("#liusername").focus()

	Autologin.check(); // do automatic login if cookie/token is available
});
<|endoftext|>


<commit_before>/**
 * Created by JunHo on 2015-04-23.
 */

THREE.JSONLoader = function(manager){

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.JSONLoader.prototype = {

    constructor:THREE.ObjectLoader,

    load:function( url, onLoad, onProgress, onError ){

        var scope = this;

        var loader = new THREE.XHRLoader(scope.manager);
        loader.setCrossOrigin(this.crossOrigin);
        loader.load( url, function(text){

            onLoad( scope.parse( JSON.parse(text) ) );
        }, onProgress, onError );

    },

    setCrossOrigin:function(value){

        this.crossOrigin = value;

    },

    parse:function(json){

        var materials = this.parseMaterials(json);

        var meshes = this.parseObjects( json, materials );

        return meshes;

    },

    parseMaterials:function(json){

        var materials = {};

        for( var i = 0, l = json.materials.length; i < l; i++ ){

            var data = json.materials[i];

            var texture = THREE.ImageUtils.loadTexture( '../models/' + data.diffuseTexture.name );

            var material = new THREE.MeshLambertMaterial({color:0xFFFFFF});
            material.color.fromArray(data.diffuse);
            //material.name = data.name;
            //material.id = data.id;
            //material.opacity = data.alpha;

            materials[data.id] = material;

        }

        return materials;

    },

    parseGeometry:function(json){

        var verticesArray = json.vertices;

        var indicesArray = json.indices;

        var uvCount = json.uvCount;

        var verticesStep = 1;

        switch( uvCount ){
            case 0:
                verticesStep = 6;
                break;
            case 1:
                verticesStep = 8;
                break;
            case 2:
                verticesStep = 10;
                break;
        }

        var verticeCount = verticesArray.length / verticesStep;

        var facesCount = indicesArray.length / 3;

        var geometry = new THREE.BufferGeometry;

        var positions = [];

        var normals = [];

        var uvs = [];

        var indices = [];

        var i = 0;

        for( ; i < verticeCount; i++ ){

            var x = verticesArray[i * verticesStep];

            var y = verticesArray[i * verticesStep + 1];

            var z = verticesArray[i * verticesStep + 2];

            var nx = verticesArray[i * verticesStep + 3];

            var ny = verticesArray[i * verticesStep + 4];

            var nz = verticesArray[i * verticesStep + 5];

            positions.push( x, y, z );
            normals.push( nx, ny, nz );

            if( uvCount > 0 ){

                var u = verticesArray[i * verticesStep + 6];

                var v = verticesArray[i * verticesStep + 7];

                // Three 좌표로 변환 필요.
                uvs.push( u, v );

            }else{

                uvs.push( 0, 0 );

            }
        }

        for( i = 0; i < facesCount; i++ ){

            var a = indicesArray[i * 3 + 2];

            var b = indicesArray[i * 3 + 1];

            var c = indicesArray[i * 3 ];

            indices.push( a, b, c );

        }

        var position = json.position;

        geometry.addAttribute( 'index', new THREE.BufferAttribute( new Uint16Array(indices), 1 ) );

        geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array(positions), 3 ) );

        geometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array(normals), 3 ) );

        geometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array(uvs), 2 ) );

        return geometry;

    },

    parseObjects:function( json, materials ){

        var datas = json.meshes;

        var meshes = [];

        for( var i = 0, l = datas.length; i < l; i++ ){

            var data = datas[i];

            var geometry = this.parseGeometry(data);

            var mesh = new THREE.Mesh( geometry, materials[data.materialId] );
            mesh.name = data.name;
            mesh.position.set( data.position[0], data.position[1], data.position[2] );
            mesh.rotation.fromArray(data.rotation);

            meshes.push(mesh);

        }

        return meshes;

    }
};<commit_msg>texture.flipY = false;<commit_after>/**
 * Created by JunHo on 2015-04-23.
 */

THREE.JSONLoader = function(manager){

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.JSONLoader.prototype = {

    constructor:THREE.ObjectLoader,

    load:function( url, onLoad, onProgress, onError ){

        var scope = this;

        var loader = new THREE.XHRLoader(scope.manager);
        loader.setCrossOrigin(this.crossOrigin);
        loader.load( url, function(text){

            onLoad( scope.parse( JSON.parse(text) ) );
        }, onProgress, onError );

    },

    setCrossOrigin:function(value){

        this.crossOrigin = value;

    },

    parse:function(json){

        var materials = this.parseMaterials(json);

        var meshes = this.parseObjects( json, materials );

        return meshes;

    },

    parseMaterials:function(json){

        var materials = {};

        for( var i = 0, l = json.materials.length; i < l; i++ ){

            var data = json.materials[i];

            var texture = THREE.ImageUtils.loadTexture( '../models/' + data.diffuseTexture.name );
            texture.flipY = false;

            var material = new THREE.MeshLambertMaterial({map:texture});
            material.color.fromArray(data.diffuse);
            material.name = data.name;
            material.id = data.id;
            material.opacity = data.alpha;

            materials[data.id] = material;

        }

        return materials;

    },

    parseGeometry:function(json){

        var verticesArray = json.vertices;

        var indicesArray = json.indices;

        var uvCount = json.uvCount;

        var verticesStep = 1;

        switch( uvCount ){
            case 0:
                verticesStep = 6;
                break;
            case 1:
                verticesStep = 8;
                break;
            case 2:
                verticesStep = 10;
                break;
        }

        var verticeCount = verticesArray.length / verticesStep;

        var facesCount = indicesArray.length / 3;

        var geometry = new THREE.BufferGeometry;

        var positions = [];

        var normals = [];

        var uvs = [];

        var indices = [];

        var i = 0;

        for( ; i < verticeCount; i++ ){

            var x = verticesArray[i * verticesStep];

            var y = verticesArray[i * verticesStep + 1];

            var z = verticesArray[i * verticesStep + 2];

            var nx = verticesArray[i * verticesStep + 3];

            var ny = verticesArray[i * verticesStep + 4];

            var nz = verticesArray[i * verticesStep + 5];

            positions.push( x, y, z );
            normals.push( nx, ny, nz );

            if( uvCount > 0 ){

                var u = verticesArray[i * verticesStep + 6];

                var v = verticesArray[i * verticesStep + 7];

                // Three 좌표로 변환 필요.
                uvs.push( u, v );

            }else{

                uvs.push( 0, 0 );

            }
        }

        for( i = 0; i < facesCount; i++ ){

            var a = indicesArray[i * 3 + 2];

            var b = indicesArray[i * 3 + 1];

            var c = indicesArray[i * 3 ];

            indices.push( a, b, c );

        }

        var position = json.position;

        geometry.addAttribute( 'index', new THREE.BufferAttribute( new Uint16Array(indices), 1 ) );

        geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array(positions), 3 ) );

        geometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array(normals), 3 ) );

        geometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array(uvs), 2 ) );

        return geometry;

    },

    parseObjects:function( json, materials ){

        var datas = json.meshes;

        var meshes = [];

        for( var i = 0, l = datas.length; i < l; i++ ){

            var data = datas[i];

            var geometry = this.parseGeometry(data);

            var mesh = new THREE.Mesh( geometry, materials[data.materialId] );
            mesh.name = data.name;
            mesh.position.set( data.position[0], data.position[1], data.position[2] );
            mesh.rotation.fromArray(data.rotation);

            meshes.push(mesh);

        }

        return meshes;

    }
};<|endoftext|>


<commit_before>"use strict";

// stdlib
var fs        = require('fs');
var path      = require('path');

// 3rd-party
var _         = require('underscore');
var chai      = require('chai');
var async     = require('async');
var Mocha     = require('mocha');
var fstools   = require('fs-tools');

////////////////////////////////////////////////////////////////////////////////

chai.should();

////////////////////////////////////////////////////////////////////////////////

module.exports.parserParameters = {
  addHelp: true,
  help: 'run test suites',
  description: 'Run all tests of enabled apps'
};

module.exports.commandLineArguments = [
  {
    args: ['app'],
    options: {
      metavar: 'APP_NAME',
      help: 'Run tests of specific application only',
      nargs: '?',
      defaultValue: null
    }
  }
];

////////////////////////////////////////////////////////////////////////////////

module.exports.run = function (N, args, callback) {
  if (!process.env.NODECA_ENV) {
    callback("You must provide NODECA_ENV in order to run nodeca test");
    return;
  }

  async.series(
    _.map([
      require('../lib/system/init/redis'),
      require('../lib/system/init/mongoose'),
      require('../lib/system/init/models'),
      require('../lib/system/init/stores'),
      require('../lib/system/init/check_migrations'),
      require('../lib/system/init/bundle'),
      // router needs to go after bundle,
      // as server tree got polluted in bundle
      require('../lib/system/init/router'),

      function (next) {
        var mocha = new Mocha();

        mocha.reporter('spec');
        mocha.ui('bdd');

        _.each(N.runtime.apps, function (app) {
          if (!args.app || args.app === app.name) {
            fstools.walkSync(app.root + '/test', function (file) {
              if ((/\.js$/).test(file) && '.' !== path.basename(file)[0]) {
                mocha.files.push(file);
              }
            });
          }
        });

        mocha.run(next);
      }
    ], function (fn) { return async.apply(fn, N); })

    , function (err) {
      if (err) {
        callback(err);
        return;
      }

      process.exit(0);
    }
  );
};
<commit_msg>fixed test launcher CLI<commit_after>"use strict";

// stdlib
var path      = require('path');

// 3rd-party
var _         = require('underscore');
var chai      = require('chai');
var async     = require('async');
var Mocha     = require('mocha');
var fstools   = require('fs-tools');

////////////////////////////////////////////////////////////////////////////////

chai.should();

////////////////////////////////////////////////////////////////////////////////

module.exports.parserParameters = {
  addHelp: true,
  help: 'run test suites',
  description: 'Run all tests of enabled apps'
};

module.exports.commandLineArguments = [
  {
    args: ['app'],
    options: {
      metavar: 'APP_NAME',
      help: 'Run tests of specific application only',
      nargs: '?',
      defaultValue: null
    }
  }
];

////////////////////////////////////////////////////////////////////////////////

module.exports.run = function (N, args, callback) {
  if (!process.env.NODECA_ENV) {
    callback("You must provide NODECA_ENV in order to run nodeca test");
    return;
  }

  async.series(
    _.map([
      require('../lib/system/init/redis'),
      require('../lib/system/init/mongoose'),
      require('../lib/system/init/models'),
      require('../lib/system/init/stores'),
      require('../lib/system/init/check_migrations'),
      require('../lib/system/init/bundle')
    ], function (fn) { return async.apply(fn, N); })

    , function (err) {
      if (err) {
        callback(err);
        return;
      }

      var mocha = new Mocha();
      var applications = N.runtime.apps;

      mocha.reporter('spec');
      mocha.ui('bdd');

      // if app set, chack that it's valid
      if (args.app) {
        if (!_.find(applications, function (app) { return app.name === args.app; })) {
          console.log('Invalid application name: ' + args.app);
          console.log(
            'Valid apps are:  ',
             _.map(applications, function (app) { return app.name; }).join(', ')
          );
          process.exit(1);
        }
      }

      _.each(applications, function (app) {
        if (!args.app || args.app === app.name) {
          fstools.walkSync(app.root + '/test', function (file) {
            if ((/\.js$/).test(file) && '.' !== path.basename(file)[0]) {
              mocha.files.push(file);
            }
          });
        }
      });

      mocha.run(function (err) {
        if (err) {
          callback(err);
          return;
        }

        process.exit(0);
      });
    }
  );
};
<|endoftext|>


<commit_before>odoo.define('sms.fields', function (require) {
"use strict";

var basic_fields = require('web.basic_fields');
var core = require('web.core');
var session = require('web.session');

var _t = core._t;

/**
 * Override of FieldPhone to use add a button calling SMS composer if option activated
 */

var Phone = basic_fields.FieldPhone;
Phone.include({
    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------

    /**
     * Open SMS composer wizard
     *
     * @private
     */
    _onClickSMS: function (ev) {
        ev.preventDefault();

        var context = session.user_context;
        context = _.extend({}, context, {
            default_res_model: this.model,
            default_res_id: parseInt(this.res_id),
            default_number_field_name: this.name,
            default_composition_mode: 'comment',
        });
        var self = this;
        return this.do_action({
            title: _t('Send SMS Text Message'),
            type: 'ir.actions.act_window',
            res_model: 'sms.composer',
            target: 'new',
            views: [[false, 'form']],
            context: context,
        }, {
        on_close: function () {
            self.trigger_up('reload');
        }});
    },

    /**
     * Add a button to call the composer wizard
     *
     * @override
     * @private
     */
    _renderReadonly: function () {
        var def = this._super.apply(this, arguments);
        if (this.nodeOptions.enable_sms) {
            var $composerButton = $('<a>', {
                title: _t('Send SMS Text Message'),
                href: '',
                class: 'btn-sm btn-link mb4 fa fa-comment o_field_phone_sms',
            });
            $composerButton.on('click', this._onClickSMS.bind(this));
            this.$el = $('<div/>').append(this.$el).append($composerButton);
        }

        return def;
    },
});

return Phone;

});
<commit_msg>[REF] sms: redesign composer button<commit_after>odoo.define('sms.fields', function (require) {
"use strict";

var basic_fields = require('web.basic_fields');
var core = require('web.core');
var session = require('web.session');

var _t = core._t;

/**
 * Override of FieldPhone to use add a button calling SMS composer if option activated
 */

var Phone = basic_fields.FieldPhone;
Phone.include({
    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------

    /**
     * Open SMS composer wizard
     *
     * @private
     */
    _onClickSMS: function (ev) {
        ev.preventDefault();

        var context = session.user_context;
        context = _.extend({}, context, {
            default_res_model: this.model,
            default_res_id: parseInt(this.res_id),
            default_number_field_name: this.name,
            default_composition_mode: 'comment',
        });
        var self = this;
        return this.do_action({
            title: _t('Send SMS Text Message'),
            type: 'ir.actions.act_window',
            res_model: 'sms.composer',
            target: 'new',
            views: [[false, 'form']],
            context: context,
        }, {
        on_close: function () {
            self.trigger_up('reload');
        }});
    },

    /**
     * Add a button to call the composer wizard
     *
     * @override
     * @private
     */
    _renderReadonly: function () {
        var def = this._super.apply(this, arguments);
        if (this.nodeOptions.enable_sms) {
            var $composerButton = $('<a>', {
                title: _t('Send SMS Text Message'),
                href: '',
                class: 'ml-3 d-inline-flex align-items-center o_field_phone_sms',
                html: $('<small>', {class: 'font-weight-bold ml-1', html: 'SMS'}),
            });
            $composerButton.prepend($('<i>', {class: 'fa fa-mobile'}));
            $composerButton.on('click', this._onClickSMS.bind(this));
            this.$el = $('<div/>').append(this.$el).append($composerButton);
        }

        return def;
    },
});

return Phone;

});
<|endoftext|>


<commit_before>Plugin.extend({
    _type: 'com.plantyouridea.ecmlpreview',
    _isContainer: false,
    _render: true,
    initPlugin: function(data) {
        console.log('data', data);
        this._self = new createjs.Shape();
        var graphics = this._self.graphics;
        var dims = this.relativeDims();

        if (data.fill) {
            graphics.beginFill(data.fill);
        }

        if (data.stroke) {
            graphics.beginStroke(data.stroke);
        }

        if (data['strokeWidth']) {
            graphics.setStrokeStyle(data['strokeWidth']);
        }

        // Radius for circle
        graphics.dc(0, 0, data.radius || dims.w || 10);
        if (data.hitArea) {
            var hit = new createjs.Shape();
            hit.graphics.beginFill("#000").dc(0, 0, dims.w);
            this._self.hitArea = hit;
        }

        if (data.rotate) {
            this._self.regX = dims.w / 2;
            this._self.regY = dims.h / 2;
            this._self.rotation = data.rotate;
        }
        if (data.opacity)
            this._self.alpha = data.opacity;

        this._self.x = dims.x + data.radius + data.strokeWidth/2;
        this._self.y = dims.y + data.radius + data.strokeWidth/2;
    },
    drawBorder: function() {

    }

});

//# sourceURL=preview.js<commit_msg>Removed renderer<commit_after><|endoftext|>


<commit_before>import React from 'react';
import Chartist from 'chartist';
import classnames from 'classnames';

export default class Chart extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      datapoint: {
        name: '',
        value: '',
      },
      tooltip: {
        top: 0,
        left: 0
      }
    };

    this.onMouseOver = this.onMouseOver.bind(this);
  }

  componentDidMount() {
    this.updateChart(this.props);
  }

  componentWillReceiveProps(newProps) {
    this.updateChart(newProps);
  }

  componentWillUnmount() {
    this.chartist.detach();
  }

  render() {
    return (

        <div ref = "tooltip"
             className = {['ct-tooltip', this.state.tooltip.classname].join(' ').trim()}
             style = {{
               position: 'absolute',
               top: this.state.tooltip.top,
               left: this.state.tooltip.left,
               padding: '0.25rem 1rem',
               border: '1px #fff solid',
               textAlign: 'center',
               fontSize: 12,
               lineHeight: 1.4,
               color: '#fff',
               opacity: 0.75
             }}>

{this.state.datapoint.name}

{this.state.datapoint.value}

    );
  }

  updateChart(props) {
    const {type, data, options = {}, responsiveOptions = [], events = {}} = props,
          create = () => {
            this.chartist = new Chartist[type](React.findDOMNode(this.refs.chart), data, options, responsiveOptions);

            Object.keys(events).forEach(x => this.chartist.on(x, events[x].bind(this.chartist)));
          };

    this.chartist ? this.chartist.update(data, options, true) :
    data.series ? create() :
    null;
  }

  onMouseOver({target}) {
    let $parent = target.parentNode;

    const targetRect = target.getBoundingClientRect(),
          chartRect = React.findDOMNode(this.refs.chart).getBoundingClientRect(),
          tooltipRect = React.findDOMNode(this.refs.tooltip).getBoundingClientRect(),
          name = $parent.attributes['ct:series-name'],
          value = target.attributes['ct:value'];

    value && this.setState({
               datapoint: {
                 name: name ? name.value : '',
                 value: value.value
               },
               tooltip: {
                 classname: `ct-tooltip-${$parent.classList[1].substr(3)}`,
                 top: targetRect.top - chartRect.top - tooltipRect.height,
                 left: targetRect.left - chartRect.left - 1
               }
             });
  }
}

Chart.propTypes = {
  type: React.PropTypes.string.isRequired,
  classnames: React.PropTypes.oneOfType([
    React.PropTypes.string,
    React.PropTypes.object
  ]),
  data: React.PropTypes.shape({
    labels: React.PropTypes.array,
    series: React.PropTypes.array
  }),
  options: React.PropTypes.object,
  responsiveOptions: React.PropTypes.array,
  events: React.PropTypes.object
};
<commit_msg>Allow transformations to tooltip name & value<commit_after>import React from 'react';
import Chartist from 'chartist';
import classnames from 'classnames';

const identity = x => x;

export default class Chart extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      datapoint: {
        name: '',
        value: '',
      },
      tooltip: {
        top: 0,
        left: 0
      }
    };

    this.onMouseOver = this.onMouseOver.bind(this);
  }

  componentDidMount() {
    this.updateChart(this.props);
  }

  componentWillReceiveProps(newProps) {
    this.updateChart(newProps);
  }

  componentWillUnmount() {
    this.chartist.detach();
  }

  render() {
    let props = this.props,
        state = this.state;

    return (

{(props.tooltip.transform.name || identity)(state.datapoint.name)}

{(props.tooltip.transform.value || identity)(state.datapoint.value)}

    );
  }

  updateChart(props) {
    const {type, data, options, responsiveOptions, events} = props,
          create = () => {
            this.chartist = new Chartist[type](React.findDOMNode(this.refs.chart), data, options, responsiveOptions);

            Object.keys(events).forEach(x => this.chartist.on(x, events[x].bind(this.chartist)));
          };

    this.chartist ? this.chartist.update(data, options, true) :
    data.series ? create() :
    null;
  }

  onMouseOver({target}) {
    let $parent = target.parentNode;

    const targetRect = target.getBoundingClientRect(),
          chartRect = React.findDOMNode(this.refs.chart).getBoundingClientRect(),
          tooltipRect = React.findDOMNode(this.refs.tooltip).getBoundingClientRect(),
          name = $parent.attributes['ct:series-name'],
          value = target.attributes['ct:value'];

    value && this.setState({
               datapoint: {
                 name: name ? name.value : '',
                 value: value.value
               },
               tooltip: {
                 classname: `ct-tooltip-${$parent.classList[1].substr(3)}`,
                 top: targetRect.top - chartRect.top - tooltipRect.height,
                 left: targetRect.left - chartRect.left - 1
               }
             });
  }
}

Chart.propTypes = {
  type: React.PropTypes.string.isRequired,
  classnames: React.PropTypes.oneOfType([
    React.PropTypes.string,
    React.PropTypes.object
  ]),
  data: React.PropTypes.shape({
    labels: React.PropTypes.array,
    series: React.PropTypes.array
  }),
  options: React.PropTypes.object,
  responsiveOptions: React.PropTypes.array,
  events: React.PropTypes.object,
  tooltip: React.PropTypes.shape({
    transform: React.PropTypes.shape({
      name: React.PropTypes.func,
      value: React.PropTypes.func
    })
  })
};

Chart.defaultProps = {
  options: {},
  responsiveOptions: [],
  events: {},
  tooltip: {
    transform: {
      name: identity,
      value: identity
    }
  }
};
<|endoftext|>


<commit_before>const { sortBy } = require('lodash')

const { EditController } = require('../../../controllers')
const { getAdvisers } = require('../../../../adviser/repos')
const { transformObjectToOption } = require('../../../../transformers')

class EditSubscribersController extends EditController {
  async configure (req, res, next) {
    const advisers = await getAdvisers(req.session.token)
    const options = advisers.results.map(transformObjectToOption)

    req.form.options.fields.subscribers.options = sortBy(options, 'label')
    super.configure(req, res, next)
  }
}

module.exports = EditSubscribersController
<commit_msg>Use custom success handler for saving list of subscribers<commit_after>const { sortBy, pick } = require('lodash')

const { EditController } = require('../../../controllers')
const { getAdvisers } = require('../../../../adviser/repos')
const { transformObjectToOption, transformIdToObject } = require('../../../../transformers')
const { Order } = require('../../../models')

class EditSubscribersController extends EditController {
  async configure (req, res, next) {
    const advisers = await getAdvisers(req.session.token)
    const options = advisers.results.map(transformObjectToOption)

    req.form.options.fields.subscribers.options = sortBy(options, 'label')
    super.configure(req, res, next)
  }

  async successHandler (req, res, next) {
    const data = pick(req.sessionModel.toJSON(), Object.keys(req.form.options.fields))
    const subscribers = data.subscribers.map(transformIdToObject)

    try {
      await Order.saveSubscribers(req.session.token, res.locals.order.id, subscribers)

      req.journeyModel.reset()
      req.journeyModel.destroy()
      req.sessionModel.reset()
      req.sessionModel.destroy()

      req.flash('success', 'Order updated')
      res.redirect(`/omis/${res.locals.order.id}`)
    } catch (error) {
      next(error)
    }
  }
}

module.exports = EditSubscribersController
<|endoftext|>