Source: animation_loader.js

"use strict";

var buffer = require("./buffer");
var Animation = require("./animation");

function makeFrame(img, frameWidth, f) {
	return buffer.makeBuffer(frameWidth, img.height, function(ctx) {
		var sx = f * frameWidth;
		ctx.drawImage(img, sx, 0, frameWidth, img.height, 0, 0, frameWidth, img.height);
	});
}

function makeAnimation(img, numFrames, time) {
	var a = new Animation();
	var frameWidth = img.width / numFrames |0;
	for (var f = 0; f < numFrames; f++) {
		a.add(makeFrame(img, frameWidth, f), time);
	}
	return a;
}

function loadImageFromManifest(imageLoader, name, info) {
	if (info.strip !== undefined) {
		imageLoader.load(name, info.strip);
	} else if (info.prefix !== undefined) {
		for (var i = 1; i <= info.frames; i++) {
			var number = "" + i;
			if (info.padNumberTo > 1) {
				while (number.length < info.padNumberTo) {
					number = "0" + number;
				}
			}
			name = info.prefix + number + info.suffix;
			imageLoader.load(name + i, name);
		}
	}
}

function loadImagesFromManifest(imageLoader, manifest) {
	for (var key in manifest) {
		if (manifest.hasOwnProperty(key)) {
			var info = manifest[key];
			loadImageFromManifest(imageLoader, key, info);
		}
	}
}

function makeAnimationFromManifest(images, key, manifestEntry) {
	var animation;
	if (manifestEntry.strip !== undefined) {
		var strip = images.get(key);
		animation = makeAnimation(strip, manifestEntry.frames, manifestEntry.msPerFrame);
	} else if (manifestEntry.prefix !== undefined) {
		animation = new Animation();
		for (var i = 1; i <= manifestEntry.frames; i++) {
			var frame = images.get(key + i);
			animation.add(frame, manifestEntry.msPerFrame);
		}
	}
	if (manifestEntry.repeatAt !== undefined) {
		animation.repeatAt = manifestEntry.repeatAt;
	}
	if (manifestEntry.flip === "horizontal") {
		animation.flipHorizontally();
	}
	if (manifestEntry.flip === "vertical") {
		animation.flipVertically();
	}
	if (manifestEntry.rotate === "cw") {
		animation.rotateClockwise();
	}
	if (manifestEntry.rotate === "180") {
		animation.rotateClockwise().rotateClockwise();
	}
	if (manifestEntry.rotate === "ccw") {
		animation.rotateCounterclockwise();
	}
	animation.name = key;
	return animation;
}

function generateAnimationsFromManifest(images, manifest) {
	var animations = {};
	for (var key in manifest) {
		if (manifest.hasOwnProperty(key)) {
			var info = manifest[key];
			animations[key] = makeAnimationFromManifest(images, key, info);
		}
	}
	return animations;
}

/**
 * Loads and constructs {@link Animation}s from a manifest. An instance of AnimationLoader is available as {@link Splat.Game#animations}.
 * @constructor
 * @param {ImageLoader} imageLoader The ImageLoader used to fetch all {@link external:image}s.
 * @param {object} manifest The list of {@link Animation}s to build.
 * @example
	var manifest = {
		"player-left": { // The Animation's name
			"strip": "img/player-left.png", // The path to a sprite-strip. A sprite strip is multiple frames side-by-side horizontally in a single image.
			"frames": 4, // The number of frames in the Animation
			"msPerFrame": 100 // How many milliseconds to display each frame
		},
		"player-right": {
			"strip": "img/player-left.png", // Re-use the left sprite-strip
			"frames": 4,
			"msPerFrame": 100,
			"flip": "horizontal" // Flip the animation horizontally so we can use the left image for the right.
		},
		"item": { // Create an animation from individual images named "img/item/[0000-0009].png"
			"prefix": "img/item/", // Image filename prefix
			"suffix": ".png", // Image filename suffix
			"padNumberTo": 4, // Number part of image is 4 characters long.
			"frames": 10, // Load 10 separate image files [0-9].
			"msPerFrame": 100,
			"repeatAt": 5, // Loop the animation back at frame 5.
			"rotate": "cw" // Rotate the animation clockwise.
		}
	};
	var imageLoader = new Splat.ImageLoader();
	var animationLoader = new Splat.AnimationLoader(imageLoader, manifest);
 */
function AnimationLoader(imageLoader, manifest) {
	/**
	 * The ImageLoader used to fetch all {@link external:image}s.
	 * @member {ImageLoader}
	 */
	this.imageLoader = imageLoader;
	/**
	 * The list of {@link Animation} metadata.
	 * @member {object}
	 */
	this.manifest = manifest;
	loadImagesFromManifest(imageLoader, manifest);
}
/**
 * Test if all {@link Animation}s are loaded.
 * @returns {boolean}
 */
AnimationLoader.prototype.allLoaded = function() {
	if (this.loaded) {
		return true;
	}
	var loaded = this.imageLoader.allLoaded();
	if (loaded) {
		this.animations = generateAnimationsFromManifest(this.imageLoader, this.manifest);
		this.loaded = true;
	}
	return loaded;
};
/**
 * Load a single {@link Animation}.
 * @param {string} name The name to store the {@link Animation} under. This name will be used to retrieve the Animation from {@link AnimationLoader#get}.
 * @param {object} info A single-animation portion of {@link AnimationLoader#manifest}.
 */
AnimationLoader.prototype.load = function(name, info) {
	this.manifest[name] = info;
	this.loaded = false;
	loadImageFromManifest(this.imageLoader, name, info);
};
/**
 * Fetch a loaded {@link Animation}.
 * @param {string} name The name used to identify the {@link Animation} in the {@link AnimationLoader#manifest}.
 * @returns {Animation}
 */
AnimationLoader.prototype.get = function(name) {
	var anim = this.animations[name];
	if (anim === undefined) {
		console.error("Unknown animation: " + name);
	}
	return anim;
};

module.exports = AnimationLoader;