var TokenStamp = {};
(function (self) {
	var VERSION = "2.6.4";
	var bordersFilePath = "../assets/images/token_stamp_2/borders/";

	self.view;
	self.canvas;
	self.bufferCanvas;
	self.tokenCount = 0;
	self.borderColorPicker;


	self.flags = {
		isEditing: false
	};

	self.settings = {
		skipFrameLag: true,
		fpsLimit: 15,
		imgurClientId: "921941cbfa5c8ba",
		editorCanvasSettings: {
			backgroundColor: "#35221e",
			width: 500,
			height: 500,
			selectionColor: "rgba(255,255,255,0.3)",
			preserveObjectStacking: true,
			stateful: false
		},
		preMaskCanvasSettings: {
			backgroundColor: null,
			renderOnAddRemove: false,
			stateful: false,
			width: 500,
			height: 500
		},
		bufferCanvasSettings: {
			backgroundColor: null,
			renderOnAddRemove: false,
			stateful: false,
			width: 256,
			height: 256
		},
		cloudinaryCredentials: {
			cloud_name: "rolladvantage",
			api_key: "217956772584558"
		},
		workingImageOptions: {
			left: 100,
			top: 100,
			hasControls: true,
			hasBorders: true,
			hasRotatingPoint: true,
			visible: true,
			selectable: true,
			evented: true,
			transparentCorners: false,
			centeredScaling: true,
			centeredRotation: true,
			padding: 0,
			cornerSize: 14,
			rotatingPointOffset: 40,
			borderColor: "rgb(200,200,200)",
			cornerColor: "rgb(200,200,200)"
		},
		staticImageOptions: {
			hasControls: false,
			hasBorders: false,
			hasRotatingPoint: false,
			visible: true,
			selectable: false,
			evented: false,
			transparentCorners: false,
			centeredScaling: false,
			centeredRotation: false
		},
		defaultText: "1",
		textSettings: {
			centerToTopPosition: -100,
			centerToLeftPosition: 40,
			fontFamily: "AleoBold",
			fill: $("#title a").css("color"),
			lineHeight: 1.1,
			fontSize: 72,
			styles: {
			},
			hasControls: true,
			hasBorders: true,
			hasRotatingPoint: true,
			visible: true,
			selectable: true,
			evented: true,
			transparentCorners: false,
			centeredScaling: true,
			centeredRotation: true,
			padding: 0,
			cornerSize: 14,
			rotatingPointOffset: 40,
			borderColor: "rgb(200,200,200)",
			cornerColor: "rgb(200,200,200)"
		},
		colorPickerSettings: {
			size: 2,
			margin: "0.2rem 0 0 -0.2rem",
			animationSpeed: 50, // toggle animation speed
			doRender: ".colour-control", // render color and bgColor of input field (on certain elements if selector)
			opacity: true, // enable / disable alpha slider
			memoryColors: [
				{ r: 255, g: 0, b: 0 },
				{ r: 0, g: 255, b: 0 },
				{ r: 0, g: 0, b: 255 },
				{ r: 255, g: 255, b: 0 },
				{ r: 0, g: 255, b: 255 },
				{ r: 255, g: 0, b: 255 },
				{ r: 255, g: 255, b: 255 }
			],
			buildCallback: function ($elm) {
				var colorInstance = this.color;
				var colorPicker = this;

				$elm.prepend(
					'<div class="cp-panel">' +
					'R <input type="text" class="cp-r" /><br>' +
					'G <input type="text" class="cp-g" /><br>' +
					'B <input type="text" class="cp-b" /><hr>' +
					'H <input type="text" class="cp-h" /><br>' +
					'S <input type="text" class="cp-s" /><br>' +
					'B <input type="text" class="cp-v" /><hr>' +
					'<input type="text" class="cp-HEX" />' +
					'</div>')
					.on("change", "input", function (e) {
						var value = this.value;
						var className = this.className;
						var type = className.split("-")[1];
						var color = {};

						color[type] = value;
						var colorType = type === "HEX" ? "HEX" : /(?:r|g|b)/.test(type) ? "rgb" : "hsv";
						var colorValue = type === "HEX" ? value : color;
						colorInstance.setColor(colorValue, colorType);
						colorPicker.render();
						this.blur();
					});

				$elm.append(
					'<div class="cp-memory">' +
					'<div class="cp-memory-colour"></div>' +
					'<div class="cp-memory-colour"></div>' +
					'<div class="cp-memory-colour"></div>' +
					'<div class="cp-memory-colour"></div>' +
					'<div class="cp-memory-colour"></div>' +
					'<div class="cp-memory-colour"></div>' +
					'<div class="cp-memory-colour"></div>' +
					'</div>'
				);

				$elm.find(".cp-memory  .cp-memory-colour")
					.on("click", function (e) {
						var memoryColour = $(this).css("background-color");
						colorInstance.setColor(memoryColour);
						colorPicker.render();
					});

				var coloursSet = 0;
				$elm.find(".cp-memory div")
					.each(function () {
						var nextColour = colorPicker.color.options.memoryColors[coloursSet++];
						console.dir(nextColour);
						$(this).css({
							background: "rgb({0},{1},{2})".format(nextColour.r, nextColour.g, nextColour.b)
						});
					});
			},
			renderCallback: function (colourControl) {
				var colors = this.color.colors.RND;
				var modes = {
					r: colors.rgb.r,
					g: colors.rgb.g,
					b: colors.rgb.b,

					h: colors.hsv.h,
					s: colors.hsv.s,
					v: colors.hsv.v,

					HEX: this.color.colors.HEX
				};
				/*
				$(colourControl).css({
					background: "#" + this.color.colors.HEX
				});
				*/
				this.$UI.find(".cp-panel input").each(function () {
					this.value = modes[this.className.substr(3)];
				});

				var triggerElement = $(colourControl).parent()[0];
				var rgbaColor = colorsToRGBA(this.color.colors);
				if (triggerElement.className.includes("border"))
					self.layers.borders.colour = rgbaColor;
				else if (triggerElement.className.includes("overlay"))
					self.layers.overlays.colour = rgbaColor;
				else if (triggerElement.className.includes("background"))
					self.layers.images.backgroundColour = rgbaColor;
				else if (triggerElement.className.includes("text"))
					self.layers.text.colour = rgbaColor;
			}
		}
	};

	function colorsToRGBA(colors) {
		var rgb = colors.rgb;
		return "rgba({0},{1},{2},{3})".format(255 * rgb.r, 255 * rgb.g, 255 * rgb.b, colors.alpha);

	}
	self.layers = {
		masks: {
			imageMask: null,
			borderMask: null,
			overlayMask: null
		},
		images: {
			isLayerDirty: true,
			bufferCompositeImage: null,
			colour: "",
			elements: []
		},
		borders: {
			isLayerDirty: true,
			bufferCompositeImage: null,
			elements: []
		},
		overlays: {
			isLayerDirty: true,
			bufferCompositeImage: null,
			elements: []
		},
		text: {
			isLayerDirty: true,
			bufferCompositeImage: null,
			colour: "",
			elements: []
		}
	};

	self.borders = [
		{
			id: "none",
			name: "None",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "",
				overlayMask: "",
				borderImage: "placeholder.gif"
			}
		},
		{
			id: "none-circle",
			name: "None Circle",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "placeholder.gif"
			}
		},
		{
			id: "asc-swrpg-1",
			name: "Star Wars RPG 1",
			defaultBorderColor: "rgb(255, 255, 255)",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "asc-swrpg-mask.png",
				overlayMask: "asc-swrpg-mask.png",
				borderImage: "asc-swrpg-border.png"
			}
		},
		{
			id: "asc-datapad-1",
			name: "Star Wars Datapad 1",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "asc-datapad-mask.png",
				overlayMask: "asc-datapad-mask.png",
				borderImage: "asc-datapad-border.png"
		},
		},
		{
			id: "plain_ring_1",
			name: "Thin Ring",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "plain_ring_1.png"
			}
		},
		{
			id: "plain_ring_2",
			name: "Thick Ring",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "plain_ring_2.png"
			}
		},
		{
			id: "plain_ring_3",
			name: "Super Thin Ring",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "plain_ring_3.png"
			}
		},
		{
			id: "plain_square_1",
			name: "Thin Square",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_square_mask.gif",
				overlayMask: "plain_square_mask.gif",
				borderImage: "plain_square_1.png"
			}
		},
		{
			id: "plain_square_2",
			name: "Thick Square",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_square_mask.gif",
				overlayMask: "plain_square_mask.gif",
				borderImage: "plain_square_2.png"
			}
		},
		{
			id: "fifthed_border_medium",
			name: "Classic 5e Medium",
			defaultBorderColor: "rgb(255, 255, 255)",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "fifthed_border_medium.png"
			}
		},
		{
			id: "fifthed_border_medium_greyscale",
			name: "Classic 5e Medium (Greyscale)",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "fifthed_border_medium_greyscale.png"
			}
		},
		{
			id: "plain_hexagon_1",
			name: "Hexagon",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_hexagon_mask.gif",
				overlayMask: "plain_hexagon_mask.gif",
				borderImage: "plain_hexagon_1.png"
			}
		},
		{
			id: "plain_octagon_1",
			name: "Octagon",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_octagon_mask.gif",
				overlayMask: "plain_octagon_mask.gif",
				borderImage: "plain_octagon_1.png"
			}
		},
		{
			id: "10_sided_ring_1",
			name: "10 Sided Ring",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "10_sided_ring_mask.gif",
				overlayMask: "10_sided_ring_mask.gif",
				borderImage: "10_sided_ring_1.png"
			}
		},
		{
			id: "12_sided_ring_1",
			name: "12 Sided Ring",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "12_sided_ring_mask.gif",
				overlayMask: "12_sided_ring_mask.gif",
				borderImage: "12_sided_ring_1.png"
			}
		},
		{
			id: "double_ring_1",
			name: "Double Ring",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "double_ring_1.png"
			}
		},
		{
			id: "spike_ring_1",
			name: "Spike Ring",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "spike_ring_1.png"
			}
		},
		{
			id: "spike_ring_2",
			name: "Spike Ring 2",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "spike_ring_2.png"
			}
		},
		{
			id: "abstract_ring_1",
			name: "Abstract Ring 1",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "abstract_ring_1.png"
			}
		},
		{
			id: "abstract_ring_1_flat",
			name: "Abstract Ring 1 (Flat)",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "abstract_ring_1_flat.png"
			}
		},
		{
			id: "abstract_ring_2",
			name: "Abstract Ring 2",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "abstract_ring_2.png"
			}
		},
		{
			id: "abstract_ring_2_flat",
			name: "Abstract Ring 2 (Flat)",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "abstract_ring_2_flat.png"
			}
		},
		{
			id: "abstract_ring_3",
			name: "Abstract Ring 3",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "abstract_ring_3.png"
			}
		},
		{
			id: "abstract_ring_3_flat",
			name: "Abstract Ring 3 (Flat)",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "abstract_ring_3_flat.png"
			}
		},
		{
			id: "abstract_double_ring_1",
			name: "Abstract Double Ring",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "abstract_double_ring_1.png"
			}
		},
		{
			id: "abstract_double_ring_1_flat",
			name: "Abstract Double Ring (Flat)",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "abstract_double_ring_1_flat.png"
			}
		},
		{
			id: "abstract_octagon_1",
			name: "Abstract Octagon",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_octagon_mask.gif",
				overlayMask: "plain_octagon_mask.gif",
				borderImage: "abstract_octagon_1.png"
			}
		},
		{
			id: "abstract_octagon_1_flat",
			name: "Abstract Octagon (Flat)",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_octagon_mask.gif",
				overlayMask: "plain_octagon_mask.gif",
				borderImage: "abstract_octagon_1_flat.png"
			}
		},
		{
			id: "abstract_triangle_1",
			name: "Abstract Triangle",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "abstract_triangle_mask.gif",
				overlayMask: "abstract_triangle_mask.gif",
				borderImage: "abstract_triangle_1.png"
			}
		},
		{
			id: "cloud_ring_1",
			name: "Cloud Ring 1",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "cloud_ring_1_mask.gif",
				overlayMask: "cloud_ring_1_mask.gif",
				borderImage: "cloud_ring_1.png"
			}
		},
		{
			id: "wavey_ring_1",
			name: "Wavey Ring 1",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "wavey_ring_1_mask.gif",
				overlayMask: "wavey_ring_1_mask.gif",
				borderImage: "wavey_ring_1.png"
			}
		},
		{
			id: "plain_ring_1_with_arrow_1",
			name: "Thin Ring With Arrow",
			imageMask: null,
			overlayMask: null,
			borderImage: null,
			fileNames: {
				imageMask: "plain_ring_mask.gif",
				overlayMask: "plain_ring_mask.gif",
				borderImage: "plain_ring_1_with_arrow_1.png"
			}
		}
	];

	self.overlays = [
		{
			id: "none",
			name: "None",
			fileName: "placeholder.gif",
			overlayImage: null
		},
		{
			id: "fade_strong_white",
			name: "Strong White Fade",
			fileName: "overlay_fade_strong_white.png",
			overlayImage: null
		},
		{
			id: "fade_strong",
			name: "Strong Fade",
			fileName: "overlay_fade_strong.png",
			overlayImage: null
		},
		{
			id: "fade_medium",
			name: "Medium Fade",
			fileName: "overlay_fade_medium.png",
			overlayImage: null
		},
		{
			id: "fade_soft",
			name: "Soft Fade",
			fileName: "overlay_fade_soft.png",
			overlayImage: null
		},
		{
			id: "scanlines",
			name: "Scanlines",
			fileName: "scanlines.png",
			overlayImage: null
		}
	];

	function receiveFoundryMessage(event) {
		console.dir(event);

		if (event == null)
			return;

		if (event.data.action == "registerSource") {
			self.foundryWindowSource = event.source;
			event.source.postMessage({
				action: "sourceRegistered"
			}, "*");
		}

		if(event.data.action == "uploadPermission"){
			self.view.set("canFoundryUpload", event.data.uploadPermission);
		}
	}

	self.init = function () {
		console.log("Token Stamp 2 Init Start");

		self.foundryDataExport = $("#foundry-data-export");
		self.isFoundryMode = self.foundryDataExport != null;

		if (self.isFoundryMode) {
			window.addEventListener("message", receiveFoundryMessage, false);
		}

		fabric.devicePixelRatio = 1;

		zip.workerScriptsPath = "/assets/js/zipjs/";
		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "version",
			eventLabel: VERSION
		});

		$.cloudinary.config(self.settings.cloudinaryCredentials);

		self.view = new Ractive({
			el: "#site-content",
			append: true,
			template: "#token-stamp-maker-template",
			decorators: {
				imageFileDialog: function (node) {
					node.onchange = function () {
						var file = node.files[0];
						if (file) {
							self.tokenName = file.name;
							self.loadImageFromFile(file);
							ga("send", {
								hitType: "event",
								eventCategory: "token-stamp-2",
								eventAction: "image-source",
								eventLabel: "FILEINPUT"
							});
						}
					};

					return {
						teardown: function () {
							node.onchange = null;
						}
					};
				},
				customBorderFileDialog: function (node) {
					node.onchange = function () {
						var file = node.files[0];
						if (file) {
							self.loadBorderFromFile(file);
							ga("send", {
								hitType: "event",
								eventCategory: "token-stamp-2",
								eventAction: "custom-border",
								eventLabel: "FILEINPUT"
							});
						}
					};

					return {
						teardown: function () {
							node.onchange = null;
						}
					};
				},
				customMaskFileDialog: function (node) {
					node.onchange = function () {
						var file = node.files[0];
						if (file) {
							self.loadMaskFromFile(file);
							ga("send", {
								hitType: "event",
								eventCategory: "token-stamp-2",
								eventAction: "custom-mask",
								eventLabel: "FILEINPUT"
							});
						}
					};

					return {
						teardown: function () {
							node.onchange = null;
						}
					};
				}
			},
			data: {
				version: VERSION,
				isLoading: true,
				isFoundryMode: self.isFoundryMode,
				showStartMessage: false,
				errorMessage: "",
				optionSelections: [],
				outputScale: 1,
				borderOpacity: 1,
				overlayOpacity: 0.5,
				isBatchModeOn: false,
				batchImageUrls: [],
				unprocessedBatchedImages: [],
				processedBatchedImages: [],
				isBatchProcessing: false,

				isCustomBorderSet: false,
				isCustomMaskSet: false,
			},
			oninit: function () {
				var view = this;
				this.on({
					toggleOptionSelector: function (event, pos) {
						var optionSelection = view.get("optionSelections")[pos];
						optionSelection.showOptions = !optionSelection.showOptions;
						view.update();
					},
					selectOption: function (event, pos, id) {
						if (view.get("isBatchProcessing")) return;

						var optionSelection = view.get("optionSelections")[pos];

						if (optionSelection.selectionType == "border" || optionSelection.selectionType == "overlay") {
							if (optionSelection.selectionType == "border")
								self.setActiveBorder(id);
							if (optionSelection.selectionType == "overlay")
								self.setActiveOverlay(id);

							optionSelection.selectedOptionId = id;
							var optionData = _.find(optionSelection.options, function (e) { return e.id === id; });
							optionSelection.selectedOptionImage = optionData.image;
						}

						view.update();
					},

					adjustOutputScale: function (event, amount) {
						var outputScale = view.get("outputScale");
						view.set("outputScale", (outputScale + amount).clamp(0, Number.MAX_VALUE));
					},

					adjustOverlayOpacity: function (event, amount) {
						var overlayOpacity = view.get("overlayOpacity");
						view.set("overlayOpacity", (overlayOpacity + amount).clamp(0, 1));
					},
					adjustBorderOpacity: function (event, amount) {
						var borderOpacity = view.get("borderOpacity");
						view.set("borderOpacity", (borderOpacity + amount).clamp(0, 1));
					},

					buttonClick: function (event, action) {
						if (view.get("isBatchProcessing")) return;

						switch (action) {
							case "chooseFile":
								$(event.node).find("input").click();
								break;
							case "addText":
								self.addTextBox();
								break;
							case "batchModeToggle":
								view.set("isBatchModeOn", !view.get("isBatchModeOn"));
								view.set("showStartMessage", false);
								self.clearImageLayer();
								break;
							case "startBatchProcess":
								self.processBatch();
								break;
							case "upload":
								if (view.get("isBatchModeOn")) return;
								self.uploadStamp(self.createStamp());
								break;
							case "download":
								if (view.get("isBatchModeOn")) return;
								self.downloadStamp(self.createStamp());
								break;
							case "foundryImport":
								self.foundryWindowSource.postMessage({
									action: "importToken",
									tokenName: self.tokenName || "Unknown Token",
									stampData: self.getFoundryData()
								}, "*");
								break;
							case "foundryImgurImport":
								self.uploadStamp(self.getFoundryData(), (tokenId) =>{
									ga("send", {
										hitType: "event",
										eventCategory: "token-stamp-2",
										eventAction: "foundry-upload-stamp-border",
										eventLabel: self.view.get("optionSelections")[0].selectedOptionId
									});
									self.foundryWindowSource.postMessage({
										action: "importTokenUrl",
										tokenUrl: "https://i.imgur.com/{0}.png".format(tokenId)
									}, "*");
								});
								break;
							case "reset":
								if (view.get("isBatchModeOn")) return;
								_.each(self.layers.images.elements,
									function (element) {
										self.resetImagePosition(element);
									});
								ga("send", {
									hitType: "event",
									eventCategory: "token-stamp-2",
									eventAction: "clear-reset",
									eventLabel: "reset-position"
								});
								break;
							case "clear":
								if (view.get("isBatchModeOn")) {
									view.set("batchImageUrls", []);
								} else {
									self.clearImageLayer();
									ga("send", {
										hitType: "event",
										eventCategory: "token-stamp-2",
										eventAction: "clear-reset",
										eventLabel: "clear-images"
									});
								}
								break;
							case "setCustomBorder":
								$(event.node).find("input").click();
								break;
							case "clearCustomBorder":
								view.set("isCustomBorderSet", false);
								var selectedOptionId = view.get("optionSelections")[0].selectedOptionId;
								self.setActiveBorder(selectedOptionId);

								break;
							case "setCustomMask":
								$(event.node).find("input").click();
								break;
							case "clearCustomMask":
								view.set("isCustomMaskSet", false);
								var selectedOptionId = view.get("optionSelections")[0].selectedOptionId;
								self.setActiveBorder(selectedOptionId);
								break;
						}
					}
				});
			},
			onrender: function () {
				var view = this;
				/*
				$(".colour-control-container").click(function () {
					$(this).find(".colour-control").click();

					e.preventDefault && e.preventDefault();
					e.stopPropagation && e.stopPropagation();
					return false;
				});
				*/
				self.settings.colorPickerSettings.opacity = false;
				var borderTint = $(this.find(".border-tint"));
				self.borderColorPicker = borderTint.colorPicker(self.settings.colorPickerSettings);
				self.layers.borders.colour = borderTint.find(".colour-control").css("background-color");

				self.settings.colorPickerSettings.opacity = true;
				var overlayTint = $(this.find(".overlay-tint"));
				overlayTint.colorPicker(self.settings.colorPickerSettings);
				self.layers.overlays.colour = overlayTint.find(".colour-control").css("background-color");

				var backgroundColour = $(this.find(".background-colour"));
				backgroundColour.colorPicker(self.settings.colorPickerSettings);
				self.layers.images.backgroundColour = backgroundColour.find(".colour-control").css("background-color");

				var textColour = $(this.find(".text-colour"));
				textColour.colorPicker(self.settings.colorPickerSettings);
				self.layers.text.colour = textColour.find(".colour-control").css("background-color");

				var editorCanvasNode = this.find("#editor-canvas");
				self.setupEditorCanvas(editorCanvasNode);

				var previewCanvasNode = this.find("#preview-canvas");
				self.bufferCanvas = self.createBufferCanvas(previewCanvasNode);

				self.updateApp();

				setTimeout(function () {
					//self.setActiveBorder("plain_ring_1");
					view.fire("selectOption", null, 0, "asc-swrpg-1");
					view.fire("selectOption", null, 1, "none");
					view.set("isLoading", false);
					view.set("showStartMessage", true);
				}, 500);
			}
		});

		self.setupBorders();
		self.setupOverlays();
		self.setupImageDragDrop();
		self.setupKeyBindings();
	};

	self.setupKeyBindings = function () {
		$(document).on({
			"keyup": function (e) {
				if (self.view.get("isBatchProcessing")) return;
				var code = e.keyCode || e.which;
				//Delete Button
				if (code == 46 && !self.flags.isEditing) {
					var activeObject = self.canvas.getActiveObject();
					self.canvas.remove(activeObject);

					if (_.contains(self.layers.images.elements, activeObject)) {
						self.layers.images.elements.remove(activeObject);
						self.layers.images.isLayerDirty = true;
					}
					if (_.contains(self.layers.text.elements, activeObject)) {
						self.layers.text.elements.remove(activeObject);
						self.layers.text.isLayerDirty = true;
					}
					ga("send", {
						hitType: "event",
						eventCategory: "token-stamp-2",
						eventAction: "shortkey",
						eventLabel: code
					});
				}
			}
		});
	};

	function getFileName (url) {
		if(typeof url != "string")
			return null;
		var nameWithExt = url.split('/')
			.pop()
			.split('#')[0]
			.split('?')[0];
		var dotSplit = nameWithExt.split(".");
		if(dotSplit.length > 1)
			dotSplit.length = dotSplit.length - 1;
		var fileName = dotSplit.join(".");
		return fileName;
	};

	self.setupImageDragDrop = function () {
		$(document).on({
			"dragover": function (e) {
				Keaworks.cancelEvent(e);

				if (self.view.get("isBatchProcessing"))
					return;

				e.originalEvent.dataTransfer.dropEffect = "copy";
			},
			"drop": function (e) {
				Keaworks.cancelEvent(e);

				if (self.view.get("isBatchProcessing")) return;

				self.clearErrorMessage();
				self.view.set("isLoading", true);
				if (self.view.get("isBatchModeOn")) {
					self.view.set("showStartMessage", false);
					var allFiles = e.originalEvent.dataTransfer.files;
					if (allFiles.length > 0) {
						for (var x = 0; x < allFiles.length; x++) {
							self.loadImageFromFile(allFiles[x]);
						}
					} else {
						var eventData = e.originalEvent.dataTransfer.getData("text/html");
						var eventImages = $(eventData).find("img").addBack("img");
						var imageUrls = [];
						eventImages.each(function () {
							imageUrls.push($(this).attr("src"));
						});
						for (var x = 0; x < imageUrls.length; x++) {
							self.loadImageFromUrl(imageUrls[x]);
						}
					}
					self.view.set("isLoading", false);
				}
				else {
					var file = e.originalEvent.dataTransfer.files[0];
					if (file) {
						self.tokenName = getFileName(file.name);
						self.loadImageFromFile(file);
						ga("send", {
							hitType: "event",
							eventCategory: "token-stamp-2",
							eventAction: "image-source",
							eventLabel: "DRAGDROP"
						});
					} else {
						var eventData = e.originalEvent.dataTransfer.getData("text/html");
						var eventImage = $(eventData).find("img").addBack("img");
						var url = eventImage.attr("src");
						if (url) {
							self.tokenName = getFileName(url);
							self.loadImageFromUrl(url);
							ga("send", {
								hitType: "event",
								eventCategory: "token-stamp-2",
								eventAction: "image-source",
								eventLabel: "DRAGDROP"
							});
						} else {
							self.view.set("isLoading", false);
							self.showErrorMessage("Unable to load file. Try dragging from desktop. If the problem persists please contact me@chadkeating.com");
						}
					}
				}
			}
		});
	};

	self.onFileDialogUploadImage = function () {
		self.loadImageFromUrl(imageUrls[x]);
	};

	self.setupEditorCanvas = function (canvasNode) {
		var cNode = $(canvasNode);
		var canvasParent = cNode.parent();

		var newCanvas = new fabric.Canvas(canvasNode, self.settings.editorCanvasSettings);

		newCanvas.setHeight(canvasParent.height().max(self.settings.editorCanvasSettings.height));
		newCanvas.setWidth(canvasParent.width().max(self.settings.editorCanvasSettings.width));

		newCanvas.on({
			"event:selected": function (e) {
				if (e.target)
					e.target.sendToBack();
			},
			//Image Events
			"object:moving": function (e) {
				self.layers.images.isLayerDirty = true;
				self.layers.text.isLayerDirty = true;
			},
			"object:rotating": function (e) {
				self.layers.images.isLayerDirty = true;
				self.layers.text.isLayerDirty = true;
			},
			"object:scaling": function (e) {
				self.layers.images.isLayerDirty = true;
				self.layers.text.isLayerDirty = true;
			},
			//Text Events
			"text:editing:entered": function (e) {
				self.flags.isEditing = true;
			},
			"text:editing:exited": function (e) {
				self.flags.isEditing = false;
				self.layers.text.isLayerDirty = true;
			}
		});

		self.canvas = newCanvas;
	};

	self.createBufferCanvas = function (canvasNode) {
		var newBufferCanvas = new fabric.StaticCanvas(canvasNode, self.settings.bufferCanvasSettings);

		newBufferCanvas.on({
			"after:render": function (e) {
				self.layers.images.isImageRendered = !self.layers.images.isLayerDirty && self.layers.images.isImageReady;
			},
		});

		return newBufferCanvas
	};

	self.isFileReaderAvailable = function () {
		if (typeof window.FileReader === "undefined") {
			console.log("File Reader not available");
			//TODO: show as error, platform not supportted
			self.showErrorMessage("Unable to use FileReader, change browser or OS. If the problem persists please contact me@chadkeating.com");
			return false;
		}
		return true;
	};

	self.readAsDataURL = function (file, callback) {
		var reader = new FileReader();
		reader.onload = callback;
		reader.readAsDataURL(file);
	}

	self.loadImageFromFile = function (file) {
		if (!self.isFileReaderAvailable()) {
			self.view.set("isLoading", false);
			return;
		}

		self.readAsDataURL(file, self.onImageLoaded);
	};

	self.loadBorderFromFile = function (file) {
		if (!self.isFileReaderAvailable()) {
			self.view.set("isLoading", false);
			return;
		}

		self.readAsDataURL(file, function (event) {
			_.each(self.layers.borders.elements, function (e) {
				self.canvas.remove(e);
			});
			self.layers.borders.elements = [];

			var borderImage = fabric.Image.fromURL(
				event.target.result,
				function (canvasImageObject) {
					var maskCanvasEl = fabric.util.createCanvasElement();
					maskCanvasEl.width = canvasImageObject.width;
					maskCanvasEl.height = canvasImageObject.height;

					var maskCanvasContext = maskCanvasEl.getContext('2d');
					maskCanvasContext.drawImage(canvasImageObject.getElement(), 0, 0, maskCanvasEl.width, maskCanvasEl.height);

					self.canvas.add(canvasImageObject);
					self.canvas.centerObject(canvasImageObject);
					canvasImageObject.moveTo(3);
					self.layers.borders.elements = [canvasImageObject];
					self.view.set("isCustomBorderSet", true);
					self.layers.images.isLayerDirty = true;
					self.layers.text.isLayerDirty = true;
				},
				self.settings.staticImageOptions);
		});
	};

	self.loadMaskFromFile = function (file) {
		if (!self.isFileReaderAvailable()) {
			self.view.set("isLoading", false);
			return;
		}

		self.readAsDataURL(file, function (event) {
			var maskImage = fabric.Image.fromURL(
				event.target.result,
				function (canvasImageObject) {
					var maskCanvasEl = fabric.util.createCanvasElement();
					maskCanvasEl.width = canvasImageObject.width;
					maskCanvasEl.height = canvasImageObject.height;

					var maskCanvasContext = maskCanvasEl.getContext('2d');
					maskCanvasContext.drawImage(canvasImageObject.getElement(), 0, 0, maskCanvasEl.width, maskCanvasEl.height);
					self.layers.masks.imageMask = canvasImageObject;
					self.layers.masks.overlayMask = canvasImageObject;
					self.view.set("isCustomMaskSet", true);
					self.layers.images.isLayerDirty = true;
					self.layers.text.isLayerDirty = true;
				},
				self.settings.staticImageOptions);
		});
	};

	self.loadImageFromUrl = function (url) {
		var newImage = new Image();
		newImage.setAttribute('crossOrigin', 'anonymous');
		newImage.onload = function (e) {
			var canvas = document.createElement("canvas");
			var ctx = canvas.getContext("2d");
			canvas.height = newImage.height;
			canvas.width = newImage.width;
			ctx.drawImage(newImage, 0, 0);
			var baseData = canvas.toDataURL("image/png");
			self.onImageLoaded(baseData);
			canvas = null;
		};
		newImage.onerror = function (e) {
			self.loadImageFromUrlCloudinary(url);
		};
		newImage.src = url;
	};

	self.loadImageFromUrlCloudinary = function (url) {
		var newImage = new Image();
		newImage.setAttribute('crossOrigin', 'anonymous');
		newImage.onload = function (e) {
			var canvas = document.createElement("canvas");
			var ctx = canvas.getContext("2d");
			canvas.height = newImage.height;
			canvas.width = newImage.width;
			ctx.drawImage(newImage, 0, 0);
			var baseData = canvas.toDataURL("image/png");
			self.onImageLoaded(baseData);
			canvas = null;
		};
		newImage.onerror = function (e) {
			self.view.set("isLoading", false);
			self.showErrorMessage("Unable to load file. Try dragging from desktop or use the 'Choose Image' button.");
		};
		newImage.src = "http://res.cloudinary.com/rolladvantage/image/fetch/fl_lossy,f_auto,w_1920,h_1920,c_fit/" + url;
	};

	self.onImageLoaded = function (event) {
		var imageToUse;
		if (event.target && event.target.result) {
			//FileReader
			imageToUse = event.target.result;
		} else {
			imageToUse = event;
		}

		if (!imageToUse || imageToUse == "data:image/png;base64,") {
			self.view.set("isLoading", false);
			self.showErrorMessage("Unable to load file. Try dragging from desktop. If the problem persists please contact me@chadkeating.com");
			return;
		}

		if (self.view.get("isBatchModeOn")) {
			self.view.push("batchImageUrls", imageToUse);
		} else {
			self.addImageToWorkCanvas(imageToUse);
		}
	};

	self.addImageToWorkCanvas = function (imageToUse) {
		var fileType = imageToUse.substr(imageToUse.lastIndexOf(".") + 1);
		if (fileType && fileType.toLowerCase() === "svg") {
			fabric.Image.fromElement(imageToUse, self.onAddImageToWorkCanvas, self.settings.workingImageOptions);
		} else {
			fabric.Image.fromURL(imageToUse, self.onAddImageToWorkCanvas, self.settings.workingImageOptions);
		}
	};

	self.onAddImageToWorkCanvas = function (newImageObject) {
		self.clearImageLayer();

		self.currentImage = newImageObject;
		self.resetImagePosition(newImageObject);
		self.layers.images.elements.push(newImageObject);
		self.canvas.add(newImageObject);
		newImageObject.moveTo(0);
		self.canvas.setActiveObject(newImageObject);
		self.layers.images.isLayerDirty = true;

		self.view.set("isLoading", false);
		self.view.set("showStartMessage", false);
	};

	self.clearImageLayer = function () {
		_.each(self.layers.images.elements, function (e) {
			self.canvas.remove(e);
		});
		self.layers.images.elements = [];
		self.layers.images.isLayerDirty = true;
	};

	self.resetImagePosition = function (newImageObject) {
		var imageFitWidth = 256;
		var imageFitHeight = imageFitWidth;

		newImageObject.setScaleX(1);
		newImageObject.setScaleY(1);
		newImageObject.rotate(0);


		if (newImageObject.width === newImageObject.height) {
			newImageObject.width = imageFitWidth;
			newImageObject.height = imageFitHeight;
		}
		else if (newImageObject.width < newImageObject.height) {
			var previousWidth = newImageObject.width;
			newImageObject.width = imageFitWidth;
			newImageObject.height = newImageObject.height * (imageFitHeight / previousWidth);
		}
		else if (newImageObject.width > newImageObject.height) {
			var previousHeight = newImageObject.height;
			newImageObject.height = imageFitHeight;
			newImageObject.width = newImageObject.width * (imageFitWidth / previousHeight);
		}

		/*
				//Max size
				if (newImageObject.width > reducedCanvasWidth || newImageObject.height > reducedCanvasHeight) {
					if (newImageObject.width === newImageObject.height) {
						newImageObject.width = reducedCanvasWidth;
						newImageObject.height = reducedCanvasHeight;
					}
					else if (newImageObject.width > newImageObject.height) {
						var previousWidth = newImageObject.width;
						newImageObject.width = reducedCanvasWidth;
						newImageObject.height = newImageObject.height * (reducedCanvasHeight / previousWidth);
					}
					else if (newImageObject.width < newImageObject.height) {
						var previousHeight = newImageObject.height;
						newImageObject.height = reducedCanvasHeight;
						newImageObject.width = newImageObject.width * (reducedCanvasWidth / previousHeight);
					}
				}
		*/
		self.canvas.centerObject(newImageObject);
		self.layers.images.isLayerDirty = true;
	};

	self.setupOverlays = function () {
		var optionsSelection = {
			selectionType: "overlay",
			label: "Overlays",
			showOptions: false,
			selectedOptionId: "",
			options: []
		};

		_.each(self.overlays, function (e) {
			optionsSelection.options.push({
				id: e.id,
				name: e.name,
				image: bordersFilePath + e.fileName
			});

			if (e.id === "none")
				return;

			fabric.Image.fromURL(
				bordersFilePath + e.fileName,
				function (imageObject) {
					e.overlayImage = imageObject;
				}, self.settings.staticImageOptions);
		});

		self.view.push("optionSelections", optionsSelection);
	};

	self.setupBorders = function () {
		var optionsSelection = {
			selectionType: "border",
			label: "Borders",
			showOptions: false,
			selectedOptionId: "",
			options: []
		};

		_.each(self.borders, function (e) {
			optionsSelection.options.push({
				id: e.id,
				name: e.name,
				image: bordersFilePath + e.fileNames.borderImage
			});

			if (e.id === "none")
				return;

			self.loadBorderFile(bordersFilePath + e.fileNames.imageMask,
				function (canvasImageObject) {
					var maskCanvasEl = fabric.util.createCanvasElement();
					maskCanvasEl.width = canvasImageObject.width;
					maskCanvasEl.height = canvasImageObject.height;

					var maskCanvasContext = maskCanvasEl.getContext('2d');
					maskCanvasContext.drawImage(canvasImageObject.getElement(), 0, 0, maskCanvasEl.width, maskCanvasEl.height);

					e.imageMask = canvasImageObject;
				});

			self.loadBorderFile(
				bordersFilePath + e.fileNames.overlayMask,
				function (canvasImageObject) {

					var maskCanvasEl = fabric.util.createCanvasElement();
					maskCanvasEl.width = canvasImageObject.width;
					maskCanvasEl.height = canvasImageObject.height;

					var maskCanvasContext = maskCanvasEl.getContext('2d');
					maskCanvasContext.drawImage(canvasImageObject.getElement(), 0, 0, maskCanvasEl.width, maskCanvasEl.height);

					e.overlayMask = canvasImageObject;
				});

			self.loadBorderFile(
				bordersFilePath + e.fileNames.borderImage,
				function (imageObject) {
					e.borderImage = imageObject;
				});
		});

		self.view.push("optionSelections", optionsSelection);
	};

	self.loadBorderFile = function (fileName, callback) {
		fabric.Image.fromURL(
			fileName,
			callback,
			self.settings.staticImageOptions);
	};

	self.setActiveOverlay = function (id) {
		_.each(self.layers.overlays.elements, function (e) {
			self.canvas.remove(e);
		});
		self.layers.overlays.elements = [];

		var overlay = _.find(self.overlays, function (e) { return e.id === id; });

		if (overlay.overlayImage !== null) {
			self.canvas.add(overlay.overlayImage);
			self.canvas.centerObject(overlay.overlayImage);
			overlay.overlayImage.moveTo(1);
			self.layers.overlays.elements = [overlay.overlayImage];
		} else {
			self.layers.overlays.elements = [];
		}

		self.layers.overlays.isLayerDirty = true;
	};

	self.setActiveBorder = function (id) {

		if (!self.view.get("isCustomBorderSet")) {
			_.each(self.layers.borders.elements, function (e) {
				self.canvas.remove(e);
			});
			self.layers.borders.elements = [];
		}

		var border = _.find(self.borders, function (e) { return e.id === id; });

		if (!self.view.get("isCustomBorderSet")) {
			if (border.defaultBorderColor != null) {
				self.borderColorPicker.colorPicker.color.setColor(border.defaultBorderColor);
				$(".border-tint .colour-control").css("background-color", border.defaultBorderColor);
				$(".border-tint .colour-control")[0].value = border.defaultBorderColor;
				self.layers.borders.colour = border.defaultBorderColor;
			}

			if (border.borderImage != null) {
				self.canvas.add(border.borderImage);
				self.canvas.centerObject(border.borderImage);
				border.borderImage.moveTo(3);
				self.layers.borders.elements = [border.borderImage];
			} else {
				self.layers.borders.elements = [];
			}
		}

		if (!self.view.get("isCustomMaskSet")) {
			self.layers.masks.imageMask = border.imageMask;
			self.layers.masks.overlayMask = border.overlayMask;
		}

		self.layers.borders.isLayerDirty = true;
		self.layers.overlays.isLayerDirty = true;
		self.layers.images.isLayerDirty = true;
		self.layers.text.isLayerDirty = true;
	};

	self.addTextBox = function () {
		var editorCenterTop = self.canvas.getHeight() / 2;
		var editorCenterLeft = self.canvas.getWidth() / 2;
		var textSettings = $.extend(self.settings.textSettings, {
			fill: self.layers.text.colour,
			top: editorCenterTop + self.settings.textSettings.centerToTopPosition,
			left: editorCenterLeft + self.settings.textSettings.centerToLeftPosition
		});

		var newTextBlock = new fabric.IText(self.settings.defaultText, textSettings);

		_.each(self.layers.text.elements, function (element) {
			self.canvas.remove(element);
		});
		self.layers.text.elements = [];

		self.canvas.add(newTextBlock);
		newTextBlock.moveTo(2);
		self.canvas.setActiveObject(newTextBlock);
		self.layers.text.elements.push(newTextBlock);
		self.layers.text.isLayerDirty = true;
		self.view.set("showStartMessage", false);
	};

	var previousTime = 0;
	var deltaTime = 0;
	self.updateApp = function (currentTime) {
		currentTime = currentTime || 0;
		deltaTime += currentTime - previousTime;
		previousTime = currentTime;

		var msPerFrame = 1000 / self.settings.fpsLimit;
		if (deltaTime >= msPerFrame) {
			if (self.settings.skipFrameLag)
				deltaTime = 0;
			else
				deltaTime -= msPerFrame;

			self.updateBackgroundColour();
			self.updateBorderColour();
			self.updateOverlayColour();
			self.updateTextColour();
			self.updateBufferCanvas(self.canvas, self.bufferCanvas);
			self.updateBatchProcess();
		}
		requestAnimationFrame(self.updateApp);
	};

	self.updateBackgroundColour = function () {
		if (self.canvas.backgroundColor == self.layers.images.backgroundColour)
			return;

		self.canvas.setBackgroundColor(self.layers.images.backgroundColour);
		self.canvas.renderAll();
		self.layers.images.isLayerDirty = true;
	};

	self.updateBorderColour = function () {
		_.each(self.layers.borders.elements,
			function (element) {
				var filter = _.find(element.filters, function (x) {
					return x.type == "Multiply";
				});
				if (filter) {
					if (filter.colour == self.layers.borders.colour)
						return;
					filter.setOptions({
						color: self.layers.borders.colour
					});
				} else {
					filter = new fabric.Image.filters.Multiply({
						color: self.layers.borders.colour
					});
					element.filters.push(filter);
				}
				element.applyFilters(function () {
					self.layers.borders.isLayerDirty = true;
				});
			});
	};

	self.updateOverlayColour = function () {
		_.each(self.layers.overlays.elements,
			function (element) {
				var filter = _.find(element.filters, function (x) {
					return x.type == "Multiply";
				});
				if (filter) {
					if (filter.colour == self.layers.overlays.colour)
						return;
					filter.setOptions({
						color: self.layers.overlays.colour
					});
				} else {
					filter = new fabric.Image.filters.Multiply({
						color: self.layers.overlays.colour
					});
					element.filters.push(filter);
				}
				element.applyFilters(function () {
					self.layers.overlays.isLayerDirty = true;
				});
			});
	};

	self.updateTextColour = function () {
		_.each(self.layers.text.elements, function (element) {
			element.setColor(self.layers.text.colour);
		});
		self.layers.text.isLayerDirty = true;
	}

	self.calculateBufferCanvasObjectPosition = function (originalObject, objectToPosition, newCanvasHeight, newCanvasWidth) {
		var originalTop = originalObject.getTop();
		var originalLeft = originalObject.getLeft();

		var editorCenterTop = self.canvas.getHeight() / 2;
		var editorCenterLeft = self.canvas.getWidth() / 2;

		var distanceFromCenterTop = originalTop - editorCenterTop;
		var distanceFromCenterLeft = originalLeft - editorCenterLeft;

		var newCanvasCenterTop = newCanvasHeight / 2;
		var newCanvasCenterLeft = newCanvasWidth / 2;

		var newTop = distanceFromCenterTop + newCanvasCenterTop;
		var newLeft = distanceFromCenterLeft + newCanvasCenterLeft;

		objectToPosition.setTop(newTop);
		objectToPosition.setLeft(newLeft);
	};

	self.updateBufferCanvas = function (workingCanvas, bufferCanvas) {
		//workingCanvas.deactivateAll();
		workingCanvas.renderAll();

		if (self.layers.borders.isLayerDirty) {
			self.layers.borders.isLayerDirty = false;

			_.each(self.layers.borders.bufferCompositeImages, function (element) {
				bufferCanvas.remove(element);
			});
			self.layers.borders.bufferCompositeImages = [];

			_.each(self.layers.borders.elements, function (element) {
				var elementClone = fabric.util.object.clone(element);

				bufferCanvas.add(elementClone);
				bufferCanvas.centerObject(elementClone);
				elementClone.moveTo(3);
				self.layers.borders.bufferCompositeImages.push(elementClone);
			});
		}

		if (self.layers.text.isLayerDirty) {
			self.layers.text.isLayerDirty = false;

			var preMaskCanvas = new fabric.StaticCanvas(document.createElement("canvas"), self.settings.preMaskCanvasSettings);

			_.each(self.layers.text.elements, function (element) {
				var elementClone = fabric.util.object.clone(element);
				preMaskCanvas.add(elementClone);
				self.calculateBufferCanvasObjectPosition(element, elementClone, preMaskCanvas.getHeight(), preMaskCanvas.getWidth());
			});

			fabric.Image.fromURL(preMaskCanvas.toDataURL("image/png"), function (imageObject) {
				imageObject.filters.push(new fabric.Image.filters.Mask({
					mask: self.layers.masks.imageMask
				}));
				imageObject.applyFilters(function () {
					bufferCanvas.remove(self.layers.text.bufferCompositeImage);
					bufferCanvas.add(imageObject);
					bufferCanvas.centerObject(imageObject);
					imageObject.moveTo(2);
					self.layers.text.bufferCompositeImage = imageObject;
				});
			}, self.settings.staticImageOptions);
		}

		if (self.layers.overlays.isLayerDirty) {
			self.layers.overlays.isLayerDirty = false;

			var preMaskCanvas = new fabric.StaticCanvas(document.createElement("canvas"), self.settings.preMaskCanvasSettings);

			_.each(self.layers.overlays.elements, function (element, i, a) {
				var elementClone = fabric.util.object.clone(element);
				preMaskCanvas.add(elementClone);
				preMaskCanvas.centerObject(elementClone);
			});

			fabric.Image.fromURL(preMaskCanvas.toDataURL("image/png"), function (imageObject) {
				imageObject.filters.push(new fabric.Image.filters.Mask({
					mask: self.layers.masks.overlayMask
				}));

				imageObject.applyFilters(function () {
					bufferCanvas.remove(self.layers.overlays.bufferCompositeImage);
					imageObject.set("opacity", self.view.get("overlayOpacity"));
					bufferCanvas.add(imageObject);
					bufferCanvas.centerObject(imageObject);
					imageObject.moveTo(1);
					self.layers.overlays.bufferCompositeImage = imageObject;
				});
			}, self.settings.staticImageOptions);
		}

		if (self.layers.images.isLayerDirty) {
			self.layers.images.isLayerDirty = false;
			self.layers.images.isImageReady = false;
			var preMaskCanvas = new fabric.StaticCanvas(document.createElement("canvas"), self.settings.preMaskCanvasSettings);
			preMaskCanvas.setBackgroundColor(self.layers.images.backgroundColour);

			_.each(self.layers.images.elements, function (element) {
				var elementClone = fabric.util.object.clone(element);
				preMaskCanvas.sendToBack(elementClone);
				preMaskCanvas.add(elementClone);
				self.calculateBufferCanvasObjectPosition(element, elementClone, preMaskCanvas.getHeight(), preMaskCanvas.getWidth());
			});

			fabric.Image.fromURL(preMaskCanvas.toDataURL("image/png"), function (imageObject) {
				imageObject.filters.push(new fabric.Image.filters.Mask({
					mask: self.layers.masks.imageMask
				}));
				imageObject.applyFilters(function () {
					bufferCanvas.remove(self.layers.images.bufferCompositeImage);
					bufferCanvas.add(imageObject);
					bufferCanvas.centerObject(imageObject);
					imageObject.moveTo(0);
					self.layers.images.bufferCompositeImage = imageObject;
					self.layers.images.isImageReady = !self.layers.images.isLayerDirty;
				});
			}, self.settings.staticImageOptions);
		}

		_.each(bufferCanvas.getObjects(), function (x) {
			x.scale(self.view.get("outputScale"));
			x.center();
		});

		_.each(self.layers.borders.bufferCompositeImages, function (x) {
			x.set("opacity", self.view.get("borderOpacity"));
		});

		if (self.layers.overlays.bufferCompositeImage !== null)
			self.layers.overlays.bufferCompositeImage.set("opacity", self.view.get("overlayOpacity"));

		bufferCanvas.renderAll();
	};

	self.processBatch = function () {
		var imagesToProcess = self.view.get("batchImageUrls");

		if (!self.view.get("isBatchProcessing") && !self.view.get("isLoading") && imagesToProcess != null && imagesToProcess.length > 0) {
			self.view.set("unprocessedBatchedImages", imagesToProcess.slice());
			self.view.set("BATCHPROCESSSTATE", "FREE");
			self.view.set("isBatchProcessing", true);

			ga("send", {
				hitType: "event",
				eventCategory: "token-stamp-2",
				eventAction: "batch-process",
				eventLabel: "images-to-batch-" + imagesToProcess.length
			});
		}
	};

	self.updateBatchProcess = function () {

		if (self.view.get("isBatchProcessing") && !self.view.get("isLoading")) {
			var batchState = self.view.get("BATCHPROCESSSTATE");
			if (batchState == "FREE") {
				var unprocessedBatchedImages = self.view.get("unprocessedBatchedImages");
				var nextImage = unprocessedBatchedImages.pop();
				self.view.set("unprocessedBatchedImages", unprocessedBatchedImages);
				if (nextImage) {
					self.view.set("isLoading", true);
					self.addImageToWorkCanvas(nextImage);
					self.view.set("BATCHPROCESSSTATE", "IMAGEADDED");
				}
				else {
					self.view.set("BATCHPROCESSSTATE", "OUTPUT");
				}
			} else if (batchState == "IMAGEADDED" && !self.layers.images.isLayerDirty && self.layers.images.isImageReady) {
				var stamp = self.createStamp();
				self.view.set("isLoading", false);
				stamp = stamp.replace(/^data:image\/[^;]/, 'data:application/octet-stream');
				self.view.push("processedBatchedImages", stamp);
				self.view.set("BATCHPROCESSSTATE", "FREE");
			} else if (batchState == "OUTPUT") {
				var nextEntryCallback = function (writer) {
					var processedBatchedImages = self.view.get("processedBatchedImages");
					var nextImageToZip = processedBatchedImages.shift();
					self.view.set("processedBatchedImages", processedBatchedImages);
					if (nextImageToZip) {
						writer.add("token_" + ++self.tokenCount + ".png", new zip.Data64URIReader(nextImageToZip), function () {
							// onsuccess callback
							nextEntryCallback(writer);
							// close the zip writer

						}, function (currentIndex, totalIndex) {/* onprogress callback */ });
					}
					else {
						writer.close(function (blob) {
							// blob contains the zip file as a Blob object
							var downloadLink = document.createElement("a");
							downloadLink.href = window.URL.createObjectURL(blob);
							downloadLink.download = "batchedTokens.zip";
							self.openLink(downloadLink);
							self.clearImageLayer();
							self.view.set("BATCHPROCESSSTATE", "FREE");
							self.view.set("isBatchProcessing", false);
							self.view.set("isLoading", false);
						});
					}
				}

				zip.createWriter(new zip.BlobWriter(), nextEntryCallback, function (error) { self.view.set("BATCHPROCESSSTATE", "FREE"); });
				self.view.set("BATCHPROCESSSTATE", "ZIPPING");
			} else if (batchState == "ZIPPING") {

			}
		}
	};


	function onCreateStamp() {
		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "stamp-border",
			eventLabel: self.view.get("optionSelections")[0].selectedOptionId
		});

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "stamp-border-colour",
			eventLabel: self.layers.borders.colour
		});

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "stamp-border-opacity",
			eventLabel: "" + self.view.get("borderOpacity")
		});

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "stamp-background-colour",
			eventLabel: self.layers.images.backgroundColour
		});

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "stamp-scale",
			eventLabel: "" + self.view.get("outputScale")
		});

		var overlayId = self.view.get("optionSelections")[1].selectedOptionId;
		if (overlayId !== "none") {

			ga("send", {
				hitType: "event",
				eventCategory: "token-stamp-2",
				eventAction: "stamp-overlay",
				eventLabel: overlayId
			});

			ga("send", {
				hitType: "event",
				eventCategory: "token-stamp-2",
				eventAction: "stamp-overlay-opacity",
				eventLabel: "" + self.view.get("overlayOpacity")
			});

			ga("send", {
				hitType: "event",
				eventCategory: "token-stamp-2",
				eventAction: "stamp-overlay-colour",
				eventLabel: self.layers.overlays.colour
			});
		}

		if (self.layers.text.elements.length > 0) {
			var text = self.layers.text.elements[0].getText();
			if (text !== null && text.length > 0) {
				ga("send", {
					hitType: "event",
					eventCategory: "token-stamp-2",
					eventAction: "stamp-text-color",
					eventLabel: self.layers.text.colour
				});

				ga("send", {
					hitType: "event",
					eventCategory: "token-stamp-2",
					eventAction: "stamp-text",
					eventLabel: text
				});
			}
		}
	}

	self.createStamp = function () {
		self.view.set("isLoading", true);

		onCreateStamp();
		return self.bufferCanvas.toDataURL("image/png");
	};

	self.getFoundryData = function () {
		self.view.set("isLoading", true);

		var stampData = self.createStamp();
		//stampData = stampData.replace(/^data:image\/[^;]/, 'data:application/octet-stream');

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "foundry-stamp-border",
			eventLabel: self.view.get("optionSelections")[0].selectedOptionId
		});

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "foundry-stamp-overlay",
			eventLabel: self.view.get("optionSelections")[1].selectedOptionId
		});

		var text = "none";
		if (self.layers.text.elements.length > 0) {
			text = self.layers.text.elements[0].getText();
		}

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "foundry-stamp-text",
			eventLabel: text
		});

		self.view.set("isLoading", false);
		return stampData;
	};

	self.downloadStamp = function (stampData) {
		stampData = stampData.replace(/^data:image\/[^;]/, 'data:application/octet-stream');
		var downloadLink = document.createElement("a");
		downloadLink.href = stampData;
		downloadLink.download = "token_" + ++self.tokenCount + ".png";
		self.openLink(downloadLink);
		self.view.set("isLoading", false);

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "download-stamp-border",
			eventLabel: self.view.get("optionSelections")[0].selectedOptionId
		});

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "download-stamp-overlay",
			eventLabel: self.view.get("optionSelections")[1].selectedOptionId
		});

		var text = "none";
		if (self.layers.text.elements.length > 0) {
			text = self.layers.text.elements[0].getText();
		}

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "download-stamp-text",
			eventLabel: text
		});
	};

	self.openLink = function (downloadLink) {
		document.body.appendChild(downloadLink);
		downloadLink.click();
		document.body.removeChild(downloadLink);
	};

	self.uploadStamp = function (stampData, callbackSuccess) {
		$.ajax({
			url: "https://api.imgur.com/3/image",
			method: "POST",
			headers: {
				Authorization: "Client-ID " + self.settings.imgurClientId
			},
			data: {
				image: stampData.replace("data:image/png;base64,", ""),
				type: "base64",
				title: "Roll Advantage - Token " + ++self.tokenCount,
				description: "Made with http://rolladvantage.com - " + VERSION,
			},
			dataType: "json",
			error: function (jqXHR, textStatus, errorThrown) {
				self.view.set("isLoading", false);
				self.showErrorMessage("There was an error uploading the stamp. Please try again. If the problem persists please contact me@chadkeating.com");
			},
			success: function (result) {
				var id = result.data.id;
				if(callbackSuccess) {
					callbackSuccess(id);
				} else {
					var url = "https://imgur.com/{0}".format(id);
					var w = window.open("about:blank");
					w.location = url;
				}
				self.view.set("isLoading", false);
			}
		});

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "upload-stamp-border",
			eventLabel: self.view.get("optionSelections")[0].selectedOptionId
		});

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "upload-stamp-overlay",
			eventLabel: self.view.get("optionSelections")[1].selectedOptionId
		});

		var text = "none";
		if (self.layers.text.elements.length > 0) {
			text = self.layers.text.elements[0].getText();
		}

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "upload-stamp-text",
			eventLabel: text
		});
	};

	self.showErrorMessage = function (message) {
		message = message || "There was an error please try again. If the problem persists please contact me@chadkeating.com";
		self.view.set("errorMessage", message);

		ga("send", {
			hitType: "event",
			eventCategory: "token-stamp-2",
			eventAction: "error",
			eventLabel: message
		});
	};

	self.clearErrorMessage = function () {
		self.view.set("errorMessage", "");
	};

})(TokenStamp);
RollAdvantage.InitRegister.push(TokenStamp.init);
