<commit_before>var pluginQRCodes = {
	libraryArguments : false, // Used to create qrcodes.
	autoDisplay : false, // True if qrcode must be shown on print.
	maxDimension : {
		width : 400,
		height : 400
	},
	minDimension : {
		width : 100,
		height : 100
	},
	defaultStyleData : {
		padding : 8,
	}, // Object containing style

	/**
	 * Get qrcode container in the dom. Or create one if does not exists.
	 *
	 * @returns valid qrcode container.
	 */
	getContainer : function() {
		// Check if container already exists
		var container = document.getElementById('extension-qrcodes-qrcode');
		if (container != null) {
			return container;
		}
		// Otherwise, create One
		container = document.createElement('div');
		container.id = 'extension-qrcodes-qrcode';

		// Add styles
		container.style[this.defaultStyleData.position.vertical] = '0px';
		container.style[this.defaultStyleData.position.horizontal] = '0px';
		container.style.width = this.defaultStyleData.dimension.width + 'px';
		container.style.height = this.defaultStyleData.dimension.height + 'px';
		container.style.padding = this.defaultStyleData.padding + 'px';

		var step = 30;

		// WHere buttons will be inserted.
		var buttonsContainer = document.createElement('div');
		buttonsContainer.id = 'buttons-container';
		buttonsContainer.className = 'btn-group-vertical';
		buttonsContainer.style[this.defaultStyleData.position.vertical] = this.defaultStyleData.padding
				+ 'px';
		buttonsContainer.style[this.defaultStyleData.position.horizontal] = this.defaultStyleData.padding
				+ 'px';
		container.appendChild(buttonsContainer);
		// Disable container motion with buttons.
		buttonsContainer.addEventListener('mousedown', function(event) {
			event.stopPropagation();
		});

		// Add buttons
		var zoomOut = document.createElement('button');
		zoomOut.type = 'button';
		zoomOut.className = 'btn btn-default';
		var zoomContent = document.createElement('span');
		zoomContent.className = 'glyphicon';
		zoomOut.appendChild(zoomContent);

		var zoomIn = zoomOut.cloneNode(true);
		zoomIn.id = 'zoom-in';
		zoomOut.id = 'zoom-out';
		zoomIn.lastChild.className += ' glyphicon-plus';
		zoomOut.lastChild.className += ' glyphicon-minus';

		zoomIn.addEventListener('click', function() {
			var dimension = pluginQRCodes.getDimension();
			dimension.width += step;
			dimension.height += step;

			// Keep qrcode centered at its position.
			/*
			 * var dimensionOld = this.getDimension(); var position =
			 * this.getPosition(); position.x += (dimensionOld.width -
			 * dimension.width) / 2; position.y += (dimensionOld.width -
			 * dimension.width) / 2; this.setPosition(position);
			 */

			pluginQRCodes.setDimension(dimension);
		});
		zoomOut.addEventListener('click', function() {
			var dimension = pluginQRCodes.getDimension();
			dimension.width -= step;
			dimension.height -= step;

			// Keep qrcode centered at its position.
			/*
			 * var dimensionOld = this.getDimension(); var position =
			 * this.getPosition(); position.x += (dimensionOld.width -
			 * dimension.width) / 2; position.y += (dimensionOld.width -
			 * dimension.width) / 2; this.setPosition(position);
			 */

			pluginQRCodes.setDimension(dimension);
		});
		buttonsContainer.appendChild(zoomIn);
		buttonsContainer.appendChild(zoomOut);

		// Put qrcode in the dom.
		document.body.appendChild(container);

		return container;
	},

	/**
	 * Display qrcode if hidden, else hide it.
	 */
	switchDisplay : function() {
		var container = this.getContainer();
		if (!container) {
			return;
		}
		if (container.style.display == 'block') {
			container.style.display = 'none';
			pluginQRCodes
					.setDimension(pluginQRCodes.defaultStyleData.dimension);
		} else {
			window.onbeforeprint();
			container.style.display = 'block';
		}
	},

	/**
	 * Get current dimension of the qrcode.
	 *
	 * @returns {(int) width, (int) height}
	 */
	getDimension : function() {
		var container = this.getContainer();
		var padding = this.defaultStyleData.padding * 2;

		return dimension = {
			width : parseInt(container.style.width) + padding,
			height : parseInt(container.style.height) + padding
		};
	},

	/**
	 * Set dimension for the qrcode.
	 *
	 * @params {(int) width, (int) height}
	 */
	setDimension : function(dimension) {
		var container = this.getContainer();
		var padding = this.defaultStyleData.padding * 2;

		if (dimension.width > this.maxDimension.width) {
			dimension.width = this.maxDimension.width;
		} else if (dimension.width < this.minDimension.width) {
			dimension.width = this.minDimension.width;
		}

		if (dimension.height > this.maxDimension.height) {
			dimension.height = this.maxDimension.height;
		} else if (dimension.height < this.minDimension.height) {
			dimension.height = this.minDimension.height;
		}

		var innerWidth = window.innerWidth;
		if (dimension.width > innerWidth) {
			dimension.width = innerWidth - 1;
		}

		var innerHeight = window.innerHeight;
		if (dimension.height > innerHeight) {
			dimension.height = innerHeight - 1;
		}

		this.setPosition(this.getPosition());

		container.style.width = parseInt(dimension.width - padding) + 'px';
		container.style.height = parseInt(dimension.height - padding) + 'px';
	},

	/**
	 * Get current position of the qrcode.
	 *
	 * @returns {(int) x, (int) y}
	 */
	getPosition : function() {
		var container = this.getContainer();

		return {
			x : parseInt(container.style[pluginQRCodes.defaultStyleData.position.horizontal]),
			y : parseInt(container.style[pluginQRCodes.defaultStyleData.position.vertical])
		};
	},

	/**
	 * Define a position for the qrcode and fix them to fit in current window.
	 *
	 * @param position -
	 *            new position of the qrcode.
	 */
	setPosition : function(position) {
		var dimension = this.getDimension();

		// use clientWidth instead of innerWidth to remove scroll bar from
		// calculus.
		if (position.x < 0) {
			position.x = 0;
		} else {
			var maxWidthArea = document.body.clientWidth - dimension.width;
			if (position.x > maxWidthArea) {
				position.x = maxWidthArea - 1;
			}
		}

		// clientHeight does not work here because, page is taller than windows
		if (position.y < 0) {
			position.y = 0;
		} else {
			var maxHeightArea = window.innerHeight - dimension.height;
			if (position.y > maxHeightArea) {
				position.y = maxHeightArea - 1;
			}
		}

		var container = this.getContainer();

		container.style[pluginQRCodes.defaultStyleData.position.horizontal] = parseInt(position.x)
				+ 'px';
		container.style[pluginQRCodes.defaultStyleData.position.vertical] = parseInt(position.y)
				+ 'px';
	},

	/**
	 * Move the qrcode thanks to event.movementX and event.movementY.
	 *
	 * @param event -
	 *            Object containing movement.
	 */
	move : function(event) {
		var container = pluginQRCodes.getContainer();

		var position = pluginQRCodes.getPosition();
		var moveX = event.movementX;
		if (pluginQRCodes.defaultStyleData.position.horizontal == 'right') {
			moveX *= -1;
		}
		position.x += moveX;
		var moveY = event.movementY;
		if (pluginQRCodes.defaultStyleData.position.vertical == 'bottom') {
			moveY *= -1;
		}
		position.y += moveY;
		pluginQRCodes.setPosition(position);
	},

	createQrcode : function() {
		var container = pluginQRCodes.getContainer();

		this.libraryArguments.text = window.location.href;
		try {
			new QRCode(container, this.libraryArguments);
		} catch (e) {
			console.log(e);
			// Empty container
			while (container.firstChild) {
				container.removeChild(container.firstChild);
			}
			// Add error message
			var errorMessage = chrome.i18n.getMessage('tooLongURL');
			var errorNode = document.createTextNode(errorMessage);
			container.className = 'error';
			container.appendChild(errorNode);
			setTimeout(function() {
				container.style.display = 'none';
			}, 5000);
			chrome.runtime.sendMessage({
				log : {
					level : 'notice',
					message : errorMessage
				},
				disable : true
			}, function(response) {
				if (chrome.runtime.lastError) {
					console.log('[error]' + chrome.runtime.lastError.message);
				}
			});
		}
	},

	/**
	 * Set print event.
	 */
	initPrintEvents : function() {
		var beforePrint = function() {
			/**
			 * Display qrcode if autoDisplay is true.
			 */
			function displayIfAuto() {
				if (this.autoDisplay && container.style.display != 'block') {
					container.style.display = 'block';
				}
			}
			window.onbeforeprint = displayIfAuto;

			pluginQRCodes.createQrcode();

			window.onbeforeprint();
		};
		window.onbeforeprint = beforePrint;

		var afterPrint = function() {
			if (!container) {
				return;
			}
			container.style.display = 'none';
		}
		window.onafterprint = afterPrint;

		if (window.matchMedia) {
			var mediaQueryList = window.matchMedia('print');
			mediaQueryList.addListener(function(mql) {
				if (mql.matches) {
					window.onbeforeprint();
				} else {
					window.onafterprint();
				}
			});
		}
	},

	/**
	 * Initialize event of mouse.
	 */
	initMouseEvents : function() {
		var container = this.getContainer();

		function activeMotion(event) {
			// Check if it's left button
			if (0 != event.button) {
				return;
			}
			event.preventDefault();
			container.removeEventListener('mousedown', activeMotion);
			window.addEventListener('mousemove', pluginQRCodes.move);
			window.addEventListener('mouseup', deactiveMotion);
			container.style.cursor = 'move';
		}
		function deactiveMotion(event) {
			// Check if it's left button
			if (0 != event.button) {
				return;
			}
			window.removeEventListener('mousemove', pluginQRCodes.move);
			window.removeEventListener('mouseup', deactiveMotion);
			container.addEventListener('mousedown', activeMotion);
			container.style.cursor = 'pointer';
		}
		container.addEventListener('mousedown', activeMotion);

		var step = 15;
		function deactiveWheel(event) {
			if (event.movementX != 0 || event.movementY != 0) {
				window.removeEventListener('mousemove', deactiveWheel);
				document.body
						.removeEventListener('mousewheel', changeDimension);
			}
		}

		function changeDimension(event) {
			event.preventDefault();
			var dimension = pluginQRCodes.getDimension();
			if (event.wheelDelta > 0) {
				dimension.width += step;
				dimension.height += step;
			} else if (event.wheelDelta < 0) {
				dimension.width -= step;
				dimension.height -= step;
			}

			// Keep qrcode centered at its position.
			/*
			 * var dimensionOld = this.getDimension(); var position =
			 * this.getPosition(); position.x += (dimensionOld.width -
			 * dimension.width) / 2; position.y += (dimensionOld.width -
			 * dimension.width) / 2; this.setPosition(position);
			 */

			pluginQRCodes.setDimension(dimension);
			document.body.addEventListener('mousewheel', changeDimension);
			window.addEventListener('mousemove', deactiveWheel);
		}
		container.addEventListener('mousewheel', changeDimension);
	},

	/**
	 * Initialize event of window object.
	 */
	initWindowEvents : function() {
		var innerWidth = window.innerWidth;
		var innerHeight = window.innerHeight;

		var timeout;

		function fixPosition(event) {
			clearTimeout(timeout);
			timeout = setTimeout(function() {
				var position = pluginQRCodes.getPosition();
				var dimension = pluginQRCodes.getDimension();
				var widthArea = window.innerWidth;
				var heightArea = window.innerHeight;

				var newInnerWidth = event.target.innerWidth;
				var newInnerHeight = event.target.innerHeight;
				position.x = (position.x * newInnerWidth) / innerWidth;
				position.y = (position.y * newInnerHeight) / innerHeight;
				innerWidth = newInnerWidth;
				innerHeight = newInnerHeight;

				pluginQRCodes.setPosition(position);
				pluginQRCodes.setDimension(dimension);
			}, 170);
		}
		window.addEventListener('resize', fixPosition);

		function resetQrcode() {
			var container = pluginQRCodes.getContainer();
			while (container.lastChild) {
				container.removeChild(container.lastChild);
			}
			pluginQRCodes.createQrcode();
		}
		window.addEventListener('hashchange', resetQrcode, false);
	},

	/**
	 * Initialize plugin.
	 */
	init : function() {
		chrome.storage.sync.get(pluginQRCodesgetDefaultOptions(), function(
				items) {
			// Set variables thanks to stored settings.
			pluginQRCodes.autoDisplay = items.autoDisplay;

			pluginQRCodes.libraryArguments = {
				width : pluginQRCodes.maxDimension.width,
				height : pluginQRCodes.maxDimension.height,
				useSVG : true,
				colorDark : items.color,
				colorLight : '#ffffff',
				correctLevel : QRCode.CorrectLevel.H
			};

			pluginQRCodes.defaultStyleData.position = {
				vertical : items.verticalPosition,
				horizontal : items.horizontalPosition
			};

			var size = parseInt(items.size)
					+ pluginQRCodes.defaultStyleData.padding * 2;
			pluginQRCodes.defaultStyleData.dimension = {
				width : size,
				height : size
			};

			// Inits events listeners
			pluginQRCodes.initPrintEvents();
			pluginQRCodes.initWindowEvents();
			pluginQRCodes.initMouseEvents();
		});
	}
};
(function() {
	pluginQRCodes.init();
})();<commit_msg>Fix: Comments<commit_after>var pluginQRCodes = {
	libraryArguments : false, // Used to create qrcodes.
	autoDisplay : false, // True if qrcode must be shown on print.
	maxDimension : {
		width : 400,
		height : 400
	},
	minDimension : {
		width : 100,
		height : 100
	},
	defaultStyleData : {
		padding : 8,
	}, // Object containing style

	/**
	 * Get qrcode container in the dom. Or create one if does not exists.
	 *
	 * @returns valid qrcode container.
	 */
	getContainer : function() {
		// Check if container already exists
		var container = document.getElementById('extension-qrcodes-qrcode');
		if (container != null) {
			return container;
		}
		// Otherwise, create One
		container = document.createElement('div');
		container.id = 'extension-qrcodes-qrcode';

		// Add styles
		container.style[this.defaultStyleData.position.vertical] = '0px';
		container.style[this.defaultStyleData.position.horizontal] = '0px';
		container.style.width = this.defaultStyleData.dimension.width + 'px';
		container.style.height = this.defaultStyleData.dimension.height + 'px';
		container.style.padding = this.defaultStyleData.padding + 'px';

		var step = 30;

		// WHere buttons will be inserted.
		var buttonsContainer = document.createElement('div');
		buttonsContainer.id = 'buttons-container';
		buttonsContainer.className = 'btn-group-vertical';
		buttonsContainer.style[this.defaultStyleData.position.vertical] = this.defaultStyleData.padding
				+ 'px';
		buttonsContainer.style[this.defaultStyleData.position.horizontal] = this.defaultStyleData.padding
				+ 'px';
		container.appendChild(buttonsContainer);
		// Disable container motion with buttons.
		buttonsContainer.addEventListener('mousedown', function(event) {
			event.stopPropagation();
		});

		// Add buttons
		var zoomOut = document.createElement('button');
		zoomOut.type = 'button';
		zoomOut.className = 'btn btn-default';
		var zoomContent = document.createElement('span');
		zoomContent.className = 'glyphicon';
		zoomOut.appendChild(zoomContent);

		var zoomIn = zoomOut.cloneNode(true);
		zoomIn.id = 'zoom-in';
		zoomOut.id = 'zoom-out';
		zoomIn.lastChild.className += ' glyphicon-plus';
		zoomOut.lastChild.className += ' glyphicon-minus';

		zoomIn.addEventListener('click', function() {
			var dimension = pluginQRCodes.getDimension();
			dimension.width += step;
			dimension.height += step;

			// Keep qrcode centered at its position.
			/*
			 * var dimensionOld = this.getDimension(); var position =
			 * this.getPosition(); position.x += (dimensionOld.width -
			 * dimension.width) / 2; position.y += (dimensionOld.width -
			 * dimension.width) / 2; this.setPosition(position);
			 */

			pluginQRCodes.setDimension(dimension);
		});
		zoomOut.addEventListener('click', function() {
			var dimension = pluginQRCodes.getDimension();
			dimension.width -= step;
			dimension.height -= step;

			// Keep qrcode centered at its position.
			/*
			 * var dimensionOld = this.getDimension(); var position =
			 * this.getPosition(); position.x += (dimensionOld.width -
			 * dimension.width) / 2; position.y += (dimensionOld.width -
			 * dimension.width) / 2; this.setPosition(position);
			 */

			pluginQRCodes.setDimension(dimension);
		});
		buttonsContainer.appendChild(zoomIn);
		buttonsContainer.appendChild(zoomOut);

		// Put qrcode in the dom.
		document.body.appendChild(container);

		return container;
	},

	/**
	 * Display qrcode if hidden, else hide it.
	 */
	switchDisplay : function() {
		var container = this.getContainer();
		if (!container) {
			return;
		}
		if (container.style.display == 'block') {
			container.style.display = 'none';
			pluginQRCodes
					.setDimension(pluginQRCodes.defaultStyleData.dimension);
		} else {
			window.onbeforeprint();
			container.style.display = 'block';
		}
	},

	/**
	 * Get current dimension of the qrcode.
	 *
	 * @returns {(int) width, (int) height}
	 */
	getDimension : function() {
		var container = this.getContainer();
		var padding = this.defaultStyleData.padding * 2;

		return dimension = {
			width : parseInt(container.style.width) + padding,
			height : parseInt(container.style.height) + padding
		};
	},

	/**
	 * Set dimension for the qrcode.
	 *
	 * @params {(int) width, (int) height}
	 */
	setDimension : function(dimension) {
		var container = this.getContainer();
		var padding = this.defaultStyleData.padding * 2;

		if (dimension.width > this.maxDimension.width) {
			dimension.width = this.maxDimension.width;
		} else if (dimension.width < this.minDimension.width) {
			dimension.width = this.minDimension.width;
		}

		if (dimension.height > this.maxDimension.height) {
			dimension.height = this.maxDimension.height;
		} else if (dimension.height < this.minDimension.height) {
			dimension.height = this.minDimension.height;
		}

		var innerWidth = window.innerWidth;
		if (dimension.width > innerWidth) {
			dimension.width = innerWidth - 1;
		}

		var innerHeight = window.innerHeight;
		if (dimension.height > innerHeight) {
			dimension.height = innerHeight - 1;
		}

		this.setPosition(this.getPosition());

		container.style.width = parseInt(dimension.width - padding) + 'px';
		container.style.height = parseInt(dimension.height - padding) + 'px';
	},

	/**
	 * Get current position of the qrcode.
	 *
	 * @returns {(int) x, (int) y}
	 */
	getPosition : function() {
		var container = this.getContainer();

		return {
			x : parseInt(container.style[pluginQRCodes.defaultStyleData.position.horizontal]),
			y : parseInt(container.style[pluginQRCodes.defaultStyleData.position.vertical])
		};
	},

	/**
	 * Define a position for the qrcode and fix them to fit in current window.
	 *
	 * @param position -
	 *            new position of the qrcode.
	 */
	setPosition : function(position) {
		var dimension = this.getDimension();

		if (position.x < 0) {
			position.x = 0;
		} else {
			// use clientWidth instead of innerWidth to remove scroll bar width.
			var maxWidthArea = document.body.clientWidth - dimension.width;
			if (position.x > maxWidthArea) {
				position.x = maxWidthArea - 1;
			}
		}

		if (position.y < 0) {
			position.y = 0;
		} else {
			// clientHeight does not work here because, page is taller than
			// windows
			var maxHeightArea = window.innerHeight - dimension.height;
			if (position.y > maxHeightArea) {
				position.y = maxHeightArea - 1;
			}
		}

		var container = this.getContainer();

		container.style[pluginQRCodes.defaultStyleData.position.horizontal] = parseInt(position.x)
				+ 'px';
		container.style[pluginQRCodes.defaultStyleData.position.vertical] = parseInt(position.y)
				+ 'px';
	},

	/**
	 * Move the qrcode thanks to event.movementX and event.movementY.
	 *
	 * @param event -
	 *            Object containing movement.
	 */
	move : function(event) {
		var container = pluginQRCodes.getContainer();

		var position = pluginQRCodes.getPosition();
		var moveX = event.movementX;
		if (pluginQRCodes.defaultStyleData.position.horizontal == 'right') {
			moveX *= -1;
		}
		position.x += moveX;
		var moveY = event.movementY;
		if (pluginQRCodes.defaultStyleData.position.vertical == 'bottom') {
			moveY *= -1;
		}
		position.y += moveY;
		pluginQRCodes.setPosition(position);
	},

	createQrcode : function() {
		var container = pluginQRCodes.getContainer();

		this.libraryArguments.text = window.location.href;
		try {
			new QRCode(container, this.libraryArguments);
		} catch (e) {
			console.log(e);
			// Empty container
			while (container.firstChild) {
				container.removeChild(container.firstChild);
			}
			// Add error message
			var errorMessage = chrome.i18n.getMessage('tooLongURL');
			var errorNode = document.createTextNode(errorMessage);
			container.className = 'error';
			container.appendChild(errorNode);
			setTimeout(function() {
				container.style.display = 'none';
			}, 5000);
			chrome.runtime.sendMessage({
				log : {
					level : 'notice',
					message : errorMessage
				},
				disable : true
			}, function(response) {
				if (chrome.runtime.lastError) {
					console.log('[error]' + chrome.runtime.lastError.message);
				}
			});
		}
	},

	/**
	 * Set print event.
	 */
	initPrintEvents : function() {
		var beforePrint = function() {
			/**
			 * Display qrcode if autoDisplay is true.
			 */
			function displayIfAuto() {
				if (this.autoDisplay && container.style.display != 'block') {
					container.style.display = 'block';
				}
			}
			window.onbeforeprint = displayIfAuto;

			pluginQRCodes.createQrcode();

			window.onbeforeprint();
		};
		window.onbeforeprint = beforePrint;

		var afterPrint = function() {
			if (!container) {
				return;
			}
			container.style.display = 'none';
		}
		window.onafterprint = afterPrint;

		if (window.matchMedia) {
			var mediaQueryList = window.matchMedia('print');
			mediaQueryList.addListener(function(mql) {
				if (mql.matches) {
					window.onbeforeprint();
				} else {
					window.onafterprint();
				}
			});
		}
	},

	/**
	 * Initialize event of mouse.
	 */
	initMouseEvents : function() {
		var container = this.getContainer();

		function activeMotion(event) {
			// Check if it's left button
			if (0 != event.button) {
				return;
			}
			event.preventDefault();
			container.removeEventListener('mousedown', activeMotion);
			window.addEventListener('mousemove', pluginQRCodes.move);
			window.addEventListener('mouseup', deactiveMotion);
			container.style.cursor = 'move';
		}
		function deactiveMotion(event) {
			// Check if it's left button
			if (0 != event.button) {
				return;
			}
			window.removeEventListener('mousemove', pluginQRCodes.move);
			window.removeEventListener('mouseup', deactiveMotion);
			container.addEventListener('mousedown', activeMotion);
			container.style.cursor = 'pointer';
		}
		container.addEventListener('mousedown', activeMotion);

		var step = 15;
		function deactiveWheel(event) {
			if (event.movementX != 0 || event.movementY != 0) {
				window.removeEventListener('mousemove', deactiveWheel);
				document.body
						.removeEventListener('mousewheel', changeDimension);
			}
		}

		function changeDimension(event) {
			event.preventDefault();
			var dimension = pluginQRCodes.getDimension();
			if (event.wheelDelta > 0) {
				dimension.width += step;
				dimension.height += step;
			} else if (event.wheelDelta < 0) {
				dimension.width -= step;
				dimension.height -= step;
			}

			// Keep qrcode centered at its position.
			/*
			 * var dimensionOld = this.getDimension(); var position =
			 * this.getPosition(); position.x += (dimensionOld.width -
			 * dimension.width) / 2; position.y += (dimensionOld.width -
			 * dimension.width) / 2; this.setPosition(position);
			 */

			pluginQRCodes.setDimension(dimension);
			document.body.addEventListener('mousewheel', changeDimension);
			window.addEventListener('mousemove', deactiveWheel);
		}
		container.addEventListener('mousewheel', changeDimension);
	},

	/**
	 * Initialize event of window object.
	 */
	initWindowEvents : function() {
		var innerWidth = window.innerWidth;
		var innerHeight = window.innerHeight;

		var timeout;

		function fixPosition(event) {
			clearTimeout(timeout);
			timeout = setTimeout(function() {
				var position = pluginQRCodes.getPosition();
				var dimension = pluginQRCodes.getDimension();
				var widthArea = window.innerWidth;
				var heightArea = window.innerHeight;

				var newInnerWidth = event.target.innerWidth;
				var newInnerHeight = event.target.innerHeight;
				position.x = (position.x * newInnerWidth) / innerWidth;
				position.y = (position.y * newInnerHeight) / innerHeight;
				innerWidth = newInnerWidth;
				innerHeight = newInnerHeight;

				pluginQRCodes.setPosition(position);
				pluginQRCodes.setDimension(dimension);
			}, 170);
		}
		window.addEventListener('resize', fixPosition);

		function resetQrcode() {
			var container = pluginQRCodes.getContainer();
			while (container.lastChild) {
				container.removeChild(container.lastChild);
			}
			pluginQRCodes.createQrcode();
		}
		window.addEventListener('hashchange', resetQrcode, false);
	},

	/**
	 * Initialize plugin.
	 */
	init : function() {
		chrome.storage.sync.get(pluginQRCodesgetDefaultOptions(), function(
				items) {
			// Set variables thanks to stored settings.
			pluginQRCodes.autoDisplay = items.autoDisplay;

			pluginQRCodes.libraryArguments = {
				width : pluginQRCodes.maxDimension.width,
				height : pluginQRCodes.maxDimension.height,
				useSVG : true,
				colorDark : items.color,
				colorLight : '#ffffff',
				correctLevel : QRCode.CorrectLevel.H
			};

			pluginQRCodes.defaultStyleData.position = {
				vertical : items.verticalPosition,
				horizontal : items.horizontalPosition
			};

			var size = parseInt(items.size)
					+ pluginQRCodes.defaultStyleData.padding * 2;
			pluginQRCodes.defaultStyleData.dimension = {
				width : size,
				height : size
			};

			// Inits events listeners
			pluginQRCodes.initPrintEvents();
			pluginQRCodes.initWindowEvents();
			pluginQRCodes.initMouseEvents();
		});
	}
};
(function() {
	pluginQRCodes.init();
})();<|endoftext|>


<commit_before>/*! apojop - v1.0.1-beta - 2013-10-18
* https://github.com/lorem--ipsum/apojop
* Copyright (c) 2013 Lorem Ipsum  Licensed ,  */
angular.module("apojop",["apojop.utils"]).filter("pretty",["prettils",function(a){return function(b,c,d){return c=void 0===c?2:+c,"columns"===d?a.columns(b,c,"  "):a.levels(b,c,"  ")}}]),angular.module("apojop.utils",[]).factory("check",function(){return{primitive:function(a){return angular.isDate(a)?!0:null===a||void 0===a?!0:-1!==["string","number","function","boolean"].indexOf(typeof a)}}}).factory("$length",["format","check",function(a,b){return{of:function(c){if(b.primitive(c))return a.primitive(c).raw.length;var d=[];if(angular.isArray(c))for(var e=0;e<c.length;e++)d.push(this.of(c[e]));else for(var f in c)d.push(a.key(f).raw.length+this.of(c[f]));return d.reduce(function(a,b){return a+b},0)+2+2*d.length}}}]).factory("format",function(){return{color:function(a,b){return''+a+""},key:function(a){return{colored:this.color(a,"#B919A8")+": ",raw:a+": "}},primitive:function(a){var b="",c="";return angular.isDate(a)?this.date(a):(null===a&&(c=this.color("null","green"),b="null"),void 0===a&&(c=this.color("undefined","green"),b="undefined"),"string"==typeof a&&(c=this.color('"'+a+'"',"red"),b='"'+a+'"'),"number"==typeof a&&(c=this.color(a,"blue"),b=""+a),"function"==typeof a&&(c=this.color(a.toString(),""),b=a.toString()),"boolean"==typeof a&&(c=this.color(a.toString(),"green"),b=a.toString()),{raw:b,colored:c})},date:function(a){return{colored:this.color("new","green")+" Date("+this.primitive(a.getTime()).colored+")",raw:"new Date("+this.primitive(a.getTime()).raw+")"}}}}).factory("prettils",["format","check","$length",function(a,b,c){return{levels:function(c,d,e){if(b.primitive(c))return a.primitive(c).colored;if(0===d)return this.flatten(c);var f=angular.isArray(c),g=[];if(f){for(var h=0;h<c.length;h++)g.push(e+this.levels(c[h],d-1,e+"  "));g=this.compactLines(g)}else for(var i in c)g.push(e+a.key(i).colored+this.levels(c[i],d-1,e+"  "));return e=e.replace(/.{2}$/,""),this.joinLines(g,f,e)},columns:function(d,e,f){if(b.primitive(d))return a.primitive(d).colored;if(c.of(d)<=e)return this.flatten(d);var g=angular.isArray(d),h=[];if(g){for(var i=0;i<d.length;i++)h.push(f+this.columns(d[i],e,f+"  "));h=this.compactLines(h)}else for(var j in d)h.push(f+a.key(j).colored+this.columns(d[j],e,f+"  "));return f=f.replace(/.{2}$/,""),this.joinLines(h,g,f)},joinLines:function(a,b,c){var d=(b?"[":"{")+"\n";return d+=a.join(",\n").replace(/\s(\(x\d+\)),\n/g,", $1\n")+"\n",d+=c+(b?"]":"}")},compactLines:function(a){for(var b=0,c=[a[0]],d=1;d0&&(c[c.length-1]=c[c.length-1]+" (x"+(b+1)+")",b=0),c.push(a[d])):b++;return c},flatten:function(c){if(b.primitive(c))return a.primitive(c).colored;var d=angular.isArray(c),e=[];if(d)for(var f=0;f<c.length;f++)e.push(this.flatten(c[f]));else for(var g in c)e.push(a.key(g).colored+this.flatten(c[g]));return(d?"[":"{")+e.join(", ")+(d?"]":"}")}}}]);<commit_msg>Bumped version number<commit_after>/*! apojop - v1.0.2-beta - 2013-10-18
* https://github.com/lorem--ipsum/apojop
* Copyright (c) 2013 Lorem Ipsum  Licensed ,  */
angular.module("apojop",["apojop.utils"]).filter("pretty",["prettils",function(a){return function(b,c,d){return c=void 0===c?2:+c,"columns"===d?a.columns(b,c,"  "):a.levels(b,c,"  ")}}]),angular.module("apojop.utils",[]).factory("check",function(){return{primitive:function(a){return angular.isDate(a)?!0:null===a||void 0===a?!0:-1!==["string","number","function","boolean"].indexOf(typeof a)}}}).factory("$length",["format","check",function(a,b){return{of:function(c){if(b.primitive(c))return a.primitive(c).raw.length;var d=[];if(angular.isArray(c))for(var e=0;e<c.length;e++)d.push(this.of(c[e]));else for(var f in c)d.push(a.key(f).raw.length+this.of(c[f]));return d.reduce(function(a,b){return a+b},0)+2+2*d.length}}}]).factory("format",function(){return{color:function(a,b){return''+a+""},key:function(a){return{colored:this.color(a,"#B919A8")+": ",raw:a+": "}},primitive:function(a){var b="",c="";return angular.isDate(a)?this.date(a):(null===a&&(c=this.color("null","green"),b="null"),void 0===a&&(c=this.color("undefined","green"),b="undefined"),"string"==typeof a&&(c=this.color('"'+a+'"',"red"),b='"'+a+'"'),"number"==typeof a&&(c=this.color(a,"blue"),b=""+a),"function"==typeof a&&(c=this.color(a.toString(),""),b=a.toString()),"boolean"==typeof a&&(c=this.color(a.toString(),"green"),b=a.toString()),{raw:b,colored:c})},date:function(a){return{colored:this.color("new","green")+" Date("+this.primitive(a.getTime()).colored+")",raw:"new Date("+this.primitive(a.getTime()).raw+")"}}}}).factory("prettils",["format","check","$length",function(a,b,c){return{levels:function(c,d,e){if(b.primitive(c))return a.primitive(c).colored;if(0===d)return this.flatten(c);var f=angular.isArray(c),g=[];if(f){for(var h=0;h<c.length;h++)g.push(e+this.levels(c[h],d-1,e+"  "));g=this.compactLines(g)}else for(var i in c)g.push(e+a.key(i).colored+this.levels(c[i],d-1,e+"  "));return e=e.replace(/.{2}$/,""),this.joinLines(g,f,e)},columns:function(d,e,f){if(b.primitive(d))return a.primitive(d).colored;if(c.of(d)<=e)return this.flatten(d);var g=angular.isArray(d),h=[];if(g){for(var i=0;i<d.length;i++)h.push(f+this.columns(d[i],e,f+"  "));h=this.compactLines(h)}else for(var j in d)h.push(f+a.key(j).colored+this.columns(d[j],e,f+"  "));return f=f.replace(/.{2}$/,""),this.joinLines(h,g,f)},joinLines:function(a,b,c){var d=(b?"[":"{")+"\n";return d+=a.join(",\n").replace(/\s(\(x\d+\)),\n/g,", $1\n")+"\n",d+=c+(b?"]":"}")},compactLines:function(a){for(var b=0,c=[a[0]],d=1;d0&&(c[c.length-1]=c[c.length-1]+" (x"+(b+1)+")",b=0),c.push(a[d])):b++;return c},flatten:function(c){if(b.primitive(c))return a.primitive(c).colored;var d=angular.isArray(c),e=[];if(d)for(var f=0;f<c.length;f++)e.push(this.flatten(c[f]));else for(var g in c)e.push(a.key(g).colored+this.flatten(c[g]));return(d?"[":"{")+e.join(", ")+(d?"]":"}")}}}]);<|endoftext|>


<commit_before>'use strict';

var hours = ['6 am', '7 am', '8 am', '9 am', '10 am', '11 am', '12 am', '1 am', '2 pm', '3 pm', '4 pm', '5 pm', '6 pm', '7 pm'];

var allLocations = [];

// Object Constructor
function StoreLocation(locationName, minCustPerHour, maxCustPerHour, avgCookiesPerCust){

//Properties
  this.locationName = locationName;
  this.minCustPerHour = minCustPerHour;
  this.maxCustPerHour = maxCustPerHour;
  this.avgCookiesPerCust = avgCookiesPerCust;
  this.custEachHourArray = [],
  this.cookiesEachHourArray = [];
  this.totalDailyCookieSales = 0;

  //Methods
  //Calculate customers each hour using a random number generator
  this.calcCustEachHour = function(){
    for (var i = 0; i < hours.length; i++) {
      var singleHourCust = Math.floor(Math.random() * (this.maxCustPerHour - this.minCustPerHour + 1) + this.minCustPerHour);
      this.custEachHourArray.push(singleHourCust);
    }
  };

  //Calculate number of cookies sold each hour using previous function output
  this.calcCookiesEachHour = function(){
    this.calcCustEachHour();
    for (var i = 0; i < hours.length; i++) {
      var singleHourCookies = Math.ceil(this.custEachHourArray[i] * this.avgCookiesPerCust);
      this.cookiesEachHourArray.push(singleHourCookies);
      this.totalDailyCookieSales += singleHourCookies;
    }
  };
  this.calcCookiesEachHour();
  allLocations.push(this);
};

//Object Instances / Array Elements

var firstAndPike = new StoreLocation('First and Pike',23,65,6.3);
var seaTacAir = new StoreLocation('SeaTac Airport',34,24,1.2);
var seaCenter = new StoreLocation('Seattle Center',11,38,3.7);
var capHill = new StoreLocation('Capitol Hill',20,38,2.3);
var alkiBeach = new StoreLocation('Alki',2,16,4.6);

//Table

var cookieTable = document.getElementById('salesTable');

//Header Row
function makeHeader() {
  var cookieHeader = document.createElement('thead');
  var cookieHeaderRow = document.createElement('tr');
  var headerElement = document.createElement('th');
  headerElement.textContent = 'Store Locations';
  cookieHeaderRow.appendChild(headerElement);
  for (var i = 0; i < hours.length; i++){
    var cookieHourHeader = document.createElement('th');
    cookieHourHeader.textContent = hours[i];
    cookieHeaderRow.appendChild(cookieHourHeader);
  };
  var totalHeaderElement = document.createElement('th');
  totalHeaderElement.textContent = 'Total Cookies';
  cookieHeaderRow.appendChild(totalHeaderElement);
  cookieHeader.appendChild(cookieHeaderRow);
  cookieTable.appendChild(cookieHeader);
}

makeHeader();

//Location Name Rows
function makeCookieRow(){
  for (var i = 0; i < allLocations.length; i++){
    var cookieTableRow = document.createElement('tr');
    var cookieRowElement = document.createElement('td');
    cookieRowElement.textContent = allLocations[i].locationName;
    cookieTableRow.appendChild(cookieRowElement);
    cookieTable.appendChild(cookieTableRow);
    for (var j = 0; j < hours.length; j++){
      var cookiePerHourElement = document.createElement('td');
      cookiePerHourElement.textContent = allLocations[i].cookiesEachHourArray[j];
      cookieTableRow.appendChild(cookiePerHourElement);
      cookieTable.appendChild(cookieTableRow);
    };
    var totalCookieElement = document.createElement('td');
    totalCookieElement.textContent = allLocations[i].totalDailyCookieSales;
    cookieTableRow.appendChild(totalCookieElement);
    cookieTable.appendChild(cookieTableRow);
  };
}

makeCookieRow();

//Footer
function makeFooter(){
  var cookieFooter = document.createElement('tr');
  var cookieFooterTitle = document.createElement('td');
  cookieFooterTitle.textContent = 'Total Cookies Per Hour';
  cookieFooter.appendChild(cookieFooterTitle);
  for (var i = 0; i < hours.length; i++){
    var cookieFooterElement = document.createElement('td');
    cookieFooterElement.textContent = allLocations[i].cookiesEachHourArray[i];
    cookieFooter.appendChild(cookieFooterElement);
  };
  cookieTable.appendChild(cookieFooter);
}

makeFooter();
<commit_msg>stupid render function wound up not being so stupid<commit_after>'use strict';

var hours = ['6 am', '7 am', '8 am', '9 am', '10 am', '11 am', '12 am', '1 am', '2 pm', '3 pm', '4 pm', '5 pm', '6 pm', '7 pm'];

var allLocations = [];

// Object Constructor
function StoreLocation(locationName, minCustPerHour, maxCustPerHour, avgCookiesPerCust){

//Properties
  this.locationName = locationName;
  this.minCustPerHour = minCustPerHour;
  this.maxCustPerHour = maxCustPerHour;
  this.avgCookiesPerCust = avgCookiesPerCust;
  this.custEachHourArray = [],
  this.cookiesEachHourArray = [];
  this.totalDailyCookieSales = 0;

  //Methods
  //Calculate customers each hour using a random number generator
  this.calcCustEachHour = function(){
    for (var i = 0; i < hours.length; i++) {
      var singleHourCust = Math.floor(Math.random() * (this.maxCustPerHour - this.minCustPerHour + 1) + this.minCustPerHour);
      this.custEachHourArray.push(singleHourCust);
    }
  };

  //Calculate number of cookies sold each hour using previous function output
  this.calcCookiesEachHour = function(){
    this.calcCustEachHour();
    for (var i = 0; i < hours.length; i++) {
      var singleHourCookies = Math.ceil(this.custEachHourArray[i] * this.avgCookiesPerCust);
      this.cookiesEachHourArray.push(singleHourCookies);
      this.totalDailyCookieSales += singleHourCookies;
    }
  };
  this.calcCookiesEachHour();
  allLocations.push(this);
  this.render();
};

//Table

var cookieTable = document.getElementById('salesTable');

//Header Row
function makeHeader() {
  var cookieHeader = document.createElement('thead');
  var cookieHeaderRow = document.createElement('tr');
  var headerElement = document.createElement('th');
  headerElement.textContent = 'Store Locations';
  cookieHeaderRow.appendChild(headerElement);
  for (var i = 0; i < hours.length; i++){
    var cookieHourHeader = document.createElement('th');
    cookieHourHeader.textContent = hours[i];
    cookieHeaderRow.appendChild(cookieHourHeader);
  };
  var totalHeaderElement = document.createElement('th');
  totalHeaderElement.textContent = 'Total Cookies';
  cookieHeaderRow.appendChild(totalHeaderElement);
  cookieHeader.appendChild(cookieHeaderRow);
  cookieTable.appendChild(cookieHeader);
}

makeHeader();

//Location Name Rows
StoreLocation.prototype.render = function(){
  for (var i = 0; i < allLocations.length; i++){
    var cookieTableRow = document.createElement('tr');
    var cookieRowElement = document.createElement('td');
    cookieRowElement.textContent = allLocations[i].locationName;
    cookieTableRow.appendChild(cookieRowElement);
    cookieTable.appendChild(cookieTableRow);
    for (var j = 0; j < hours.length; j++){
      var cookiePerHourElement = document.createElement('td');
      cookiePerHourElement.textContent = allLocations[i].cookiesEachHourArray[j];
      cookieTableRow.appendChild(cookiePerHourElement);
      cookieTable.appendChild(cookieTableRow);
    };
    var totalCookieElement = document.createElement('td');
    totalCookieElement.textContent = allLocations[i].totalDailyCookieSales;
    cookieTableRow.appendChild(totalCookieElement);
    cookieTable.appendChild(cookieTableRow);
  };
};

//Object Instances / Array Elements

var firstAndPike = new StoreLocation('First and Pike',23,65,6.3);
var seaTacAir = new StoreLocation('SeaTac Airport',34,24,1.2);
var seaCenter = new StoreLocation('Seattle Center',11,38,3.7);
var capHill = new StoreLocation('Capitol Hill',20,38,2.3);
var alkiBeach = new StoreLocation('Alki',2,16,4.6);

// //Footer
// function makeFooter(){
//   var cookieFooter = document.createElement('tr');
//   var cookieFooterTitle = document.createElement('td');
//   cookieFooterTitle.textContent = 'Total Cookies Per Hour';
//   cookieFooter.appendChild(cookieFooterTitle);
//   for (var i = 0; i < hours.length; i++){
//     var cookieFooterElement = document.createElement('td');
//     cookieFooterElement.textContent = allLocations[i].cookiesEachHourArray[i];
//     cookieFooter.appendChild(cookieFooterElement);
//   };
//   cookieTable.appendChild(cookieFooter);
// }
//
// makeFooter();
<|endoftext|>


<commit_before>/**
 * Simple dropdown implementation.
 *
 * @author	Alexander Ebert
 * @copyright	2001-2017 WoltLab GmbH
 * @license	GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 * @module	WoltLabSuite/Core/Ui/Dropdown/Simple
 */
define(
	[       'CallbackList', 'Core', 'Dictionary', 'Ui/Alignment', 'Dom/ChangeListener', 'Dom/Traverse', 'Dom/Util', 'Ui/CloseOverlay'],
	function(CallbackList,   Core,   Dictionary,   UiAlignment,    DomChangeListener,    DomTraverse,    DomUtil,    UiCloseOverlay)
{
	"use strict";

	var _availableDropdowns = null;
	var _callbacks = new CallbackList();
	var _didInit = false;
	var _dropdowns = new Dictionary();
	var _menus = new Dictionary();
	var _menuContainer = null;

	/**
	 * @exports	WoltLabSuite/Core/Ui/Dropdown/Simple
	 */
	return {
		/**
		 * Performs initial setup such as setting up dropdowns and binding listeners.
		 */
		setup: function() {
			if (_didInit) return;
			_didInit = true;

			_menuContainer = elCreate('div');
			_menuContainer.className = 'dropdownMenuContainer';
			document.body.appendChild(_menuContainer);

			_availableDropdowns = elByClass('dropdownToggle');

			this.initAll();

			UiCloseOverlay.add('WoltLabSuite/Core/Ui/Dropdown/Simple', this.closeAll.bind(this));
			DomChangeListener.add('WoltLabSuite/Core/Ui/Dropdown/Simple', this.initAll.bind(this));

			document.addEventListener('scroll', this._onScroll.bind(this));

			// expose on window object for backward compatibility
			window.bc_wcfSimpleDropdown = this;
		},

		/**
		 * Loops through all possible dropdowns and registers new ones.
		 */
		initAll: function() {
			for (var i = 0, length = _availableDropdowns.length; i < length; i++) {
				this.init(_availableDropdowns[i], false);
			}
		},

		/**
		 * Initializes a dropdown.
		 *
		 * @param	{Element}	button
		 * @param	{boolean}	isLazyInitialization
		 */
		init: function(button, isLazyInitialization) {
			this.setup();

			if (button.classList.contains('jsDropdownEnabled') || elData(button, 'target')) {
				return false;
			}

			var dropdown = DomTraverse.parentByClass(button, 'dropdown');
			if (dropdown === null) {
				throw new Error("Invalid dropdown passed, button '" + DomUtil.identify(button) + "' does not have a parent with .dropdown.");
			}

			var menu = DomTraverse.nextByClass(button, 'dropdownMenu');
			if (menu === null) {
				throw new Error("Invalid dropdown passed, button '" + DomUtil.identify(button) + "' does not have a menu as next sibling.");
			}

			// move menu into global container
			_menuContainer.appendChild(menu);

			var containerId = DomUtil.identify(dropdown);
			if (!_dropdowns.has(containerId)) {
				button.classList.add('jsDropdownEnabled');
				button.addEventListener(WCF_CLICK_EVENT, this._toggle.bind(this));

				_dropdowns.set(containerId, dropdown);
				_menus.set(containerId, menu);

				if (!containerId.match(/^wcf\d+$/)) {
					elData(menu, 'source', containerId);
				}

				// prevent page scrolling
				if (menu.childElementCount && menu.children[0].classList.contains('scrollableDropdownMenu')) {
					menu = menu.children[0];
					elData(menu, 'scroll-to-active', true);

					var menuHeight = null, menuRealHeight = null;
					menu.addEventListener('wheel', function (event) {
						if (menuHeight === null) menuHeight = menu.clientHeight;
						if (menuRealHeight === null) menuRealHeight = menu.scrollHeight;

						// negative value: scrolling up
						if (event.deltaY < 0 && menu.scrollTop === 0) {
							event.preventDefault();
						}
						else if (event.deltaY > 0 && (menu.scrollTop + menuHeight === menuRealHeight)) {
							event.preventDefault();
						}
					});
				}
			}

			elData(button, 'target', containerId);

			if (isLazyInitialization) {
				setTimeout(function() { Core.triggerEvent(button, WCF_CLICK_EVENT); }, 10);
			}
		},

		/**
		 * Initializes a remote-controlled dropdown.
		 *
		 * @param	{Element}	dropdown	dropdown wrapper element
		 * @param	{Element}	menu		menu list element
		 */
		initFragment: function(dropdown, menu) {
			this.setup();

			var containerId = DomUtil.identify(dropdown);
			if (_dropdowns.has(containerId)) {
				return;
			}

			_dropdowns.set(containerId, dropdown);
			_menuContainer.appendChild(menu);

			_menus.set(containerId, menu);
		},

		/**
		 * Registers a callback for open/close events.
		 *
		 * @param	{string}			containerId	dropdown wrapper id
		 * @param	{function(string, string)}	callback
		 */
		registerCallback: function(containerId, callback) {
			_callbacks.add(containerId, callback);
		},

		/**
		 * Returns the requested dropdown wrapper element.
		 *
		 * @return	{Element}	dropdown wrapper element
		 */
		getDropdown: function(containerId) {
			return _dropdowns.get(containerId);
		},

		/**
		 * Returns the requested dropdown menu list element.
		 *
		 * @return	{Element}	menu list element
		 */
		getDropdownMenu: function(containerId) {
			return _menus.get(containerId);
		},

		/**
		 * Toggles the requested dropdown between opened and closed.
		 *
		 * @param	{string}	containerId	        dropdown wrapper id
		 * @param       {Element=}      referenceElement        alternative reference element, used for reusable dropdown menus
		 */
		toggleDropdown: function(containerId, referenceElement) {
			this._toggle(null, containerId, referenceElement);
		},

		/**
		 * Calculates and sets the alignment of given dropdown.
		 *
		 * @param	{Element}	dropdown	        dropdown wrapper element
		 * @param	{Element}	dropdownMenu	        menu list element
		 * @param       {Element=}      alternateElement        alternative reference element for alignment
		 */
		setAlignment: function(dropdown, dropdownMenu, alternateElement) {
			// check if button belongs to an i18n textarea
			var button = elBySel('.dropdownToggle', dropdown), refDimensionsElement;
			if (button !== null && button.parentNode.classList.contains('inputAddonTextarea')) {
				refDimensionsElement = button;
			}

			UiAlignment.set(dropdownMenu, alternateElement || dropdown, {
				pointerClassNames: ['dropdownArrowBottom', 'dropdownArrowRight'],
				refDimensionsElement: refDimensionsElement || null,

				// alignment
				horizontal: (elData(dropdownMenu, 'dropdown-alignment-horizontal') === 'right') ? 'right' : 'left',
				vertical: (elData(dropdownMenu, 'dropdown-alignment-vertical') === 'top') ? 'top' : 'bottom'
			});
		},

		/**
		 * Calculats and sets the alignment of the dropdown identified by given id.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 */
		setAlignmentById: function(containerId) {
			var dropdown = _dropdowns.get(containerId);
			if (dropdown === undefined) {
				throw new Error("Unknown dropdown identifier '" + containerId + "'.");
			}

			var menu = _menus.get(containerId);

			this.setAlignment(dropdown, menu);
		},

		/**
		 * Returns true if target dropdown exists and is open.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 * @return	{boolean}	true if dropdown exists and is open
		 */
		isOpen: function(containerId) {
			var menu = _menus.get(containerId);
			return (menu !== undefined && menu.classList.contains('dropdownOpen'));
		},

		/**
		 * Opens the dropdown unless it is already open.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 */
		open: function(containerId) {
			var menu = _menus.get(containerId);
			if (menu !== undefined && !menu.classList.contains('dropdownOpen')) {
				this.toggleDropdown(containerId);
			}
		},

		/**
		 * Closes the dropdown identified by given id without notifying callbacks.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 */
		close: function(containerId) {
			var dropdown = _dropdowns.get(containerId);
			if (dropdown !== undefined) {
				dropdown.classList.remove('dropdownOpen');
				_menus.get(containerId).classList.remove('dropdownOpen');
			}
		},

		/**
		 * Closes all dropdowns.
		 */
		closeAll: function() {
			_dropdowns.forEach((function(dropdown, containerId) {
				if (dropdown.classList.contains('dropdownOpen')) {
					dropdown.classList.remove('dropdownOpen');
					_menus.get(containerId).classList.remove('dropdownOpen');

					this._notifyCallbacks(containerId, 'close');
				}
			}).bind(this));
		},

		/**
		 * Destroys a dropdown identified by given id.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 * @return	{boolean}	false for unknown dropdowns
		 */
		destroy: function(containerId) {
			if (!_dropdowns.has(containerId)) {
				return false;
			}

			try {
				this.close(containerId);

				elRemove(_menus.get(containerId));
			}
			catch (e) {
				// the elements might not exist anymore thus ignore all errors while cleaning up
			}

			_menus.delete(containerId);
			_dropdowns.delete(containerId);

			return true;
		},

		/**
		 * Handles dropdown positions in overlays when scrolling in the overlay.
		 *
		 * @param	{Event}		event	event object
		 */
		_onDialogScroll: function(event) {
			var dialogContent = event.currentTarget;
			//noinspection JSCheckFunctionSignatures
			var dropdowns = elBySelAll('.dropdown.dropdownOpen', dialogContent);

			for (var i = 0, length = dropdowns.length; i < length; i++) {
				var dropdown = dropdowns[i];
				var containerId = DomUtil.identify(dropdown);
				var offset = DomUtil.offset(dropdown);
				var dialogOffset = DomUtil.offset(dialogContent);

				// check if dropdown toggle is still (partially) visible
				if (offset.top + dropdown.clientHeight <= dialogOffset.top) {
					// top check
					this.toggleDropdown(containerId);
				}
				else if (offset.top >= dialogOffset.top + dialogContent.offsetHeight) {
					// bottom check
					this.toggleDropdown(containerId);
				}
				else if (offset.left <= dialogOffset.left) {
					// left check
					this.toggleDropdown(containerId);
				}
				else if (offset.left >= dialogOffset.left + dialogContent.offsetWidth) {
					// right check
					this.toggleDropdown(containerId);
				}
				else {
					this.setAlignment(containerId, _menus.get(containerId));
				}
			}
		},

		/**
		 * Recalculates dropdown positions on page scroll.
		 */
		_onScroll: function() {
			_dropdowns.forEach((function(dropdown, containerId) {
				if (dropdown.classList.contains('dropdownOpen')) {
					if (elDataBool(dropdown, 'is-overlay-dropdown-button')) {
						this.setAlignment(dropdown, _menus.get(containerId));
					}
					else {
						this.close(containerId);
					}
				}
			}).bind(this));
		},

		/**
		 * Notifies callbacks on status change.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 * @param	{string}	action		can be either 'open' or 'close'
		 */
		_notifyCallbacks: function(containerId, action) {
			_callbacks.forEach(containerId, function(callback) {
				callback(containerId, action);
			});
		},

		/**
		 * Toggles the dropdown's state between open and close.
		 *
		 * @param	{?Event}	event		        event object, should be 'null' if targetId is given
		 * @param	{string?}	targetId	        dropdown wrapper id
		 * @param       {Element=}      alternateElement        alternative reference element for alignment
		 * @return	{boolean}	'false' if event is not null
		 */
		_toggle: function(event, targetId, alternateElement) {
			if (event !== null) {
				event.preventDefault();
				event.stopPropagation();

				//noinspection JSCheckFunctionSignatures
				targetId = elData(event.currentTarget, 'target');
			}

			var dropdown = _dropdowns.get(targetId), preventToggle = false;
			if (dropdown !== undefined) {
				// check if the dropdown is still the same, as some components (e.g. page actions)
				// re-create the parent of a button
				if (event) {
					var button = event.currentTarget, parent = button.parentNode;
					if (parent !== dropdown) {
						parent.classList.add('dropdown');
						parent.id = dropdown.id;

						// remove dropdown class and id from old parent
						dropdown.classList.remove('dropdown');
						dropdown.id = '';

						dropdown = parent;
						_dropdowns.set(targetId, parent);
					}
				}

				// Repeated clicks on the dropdown button will not cause it to close, the only way
				// to close it is by clicking somewhere else in the document or on another dropdown
				// toggle. This is used with the search bar to prevent the dropdown from closing by
				// setting the caret position in the search input field.
				if (elDataBool(dropdown, 'dropdown-prevent-toggle') && dropdown.classList.contains('dropdownOpen')) {
					preventToggle = true;
				}

				// check if 'isOverlayDropdownButton' is set which indicates that the dropdown toggle is within an overlay
				if (elData(dropdown, 'is-overlay-dropdown-button') === null) {
					var dialogContent = DomTraverse.parentByClass(dropdown, 'dialogContent');
					elData(dropdown, 'is-overlay-dropdown-button', (dialogContent !== null));

					if (dialogContent !== null) {
						dialogContent.addEventListener('scroll', this._onDialogScroll.bind(this));
					}
				}
			}

			// close all dropdowns
			_dropdowns.forEach((function(dropdown, containerId) {
				var menu = _menus.get(containerId);

				if (dropdown.classList.contains('dropdownOpen')) {
					if (preventToggle === false) {
						dropdown.classList.remove('dropdownOpen');
						menu.classList.remove('dropdownOpen');

						this._notifyCallbacks(containerId, 'close');
					}
				}
				else if (containerId === targetId && menu.childElementCount > 0) {
					dropdown.classList.add('dropdownOpen');
					menu.classList.add('dropdownOpen');

					if (menu.childElementCount && elDataBool(menu.children[0], 'scroll-to-active')) {
						var list = menu.children[0];
						list.removeAttribute('data-scroll-to-active');

						var active = null;
						for (var i = 0, length = list.childElementCount; i < length; i++) {
							if (list.children[i].classList.contains('active')) {
								active = list.children[i];
								break;
							}
						}

						if (active) {
							list.scrollTop = Math.max((active.offsetTop + active.clientHeight) - menu.clientHeight, 0);
						}
					}

					var itemList = elBySel('.scrollableDropdownMenu', menu);
					if (itemList !== null) {
						itemList.classList[(itemList.scrollHeight > itemList.clientHeight ? 'add' : 'remove')]('forceScrollbar');
					}

					this._notifyCallbacks(containerId, 'open');

					this.setAlignment(dropdown, menu, alternateElement);
				}
			}).bind(this));

			//noinspection JSDeprecatedSymbols
			window.WCF.Dropdown.Interactive.Handler.closeAll();

			return (event === null);
		}
	};
});
<commit_msg>Fix alignment of dropdowns in scrollable dialogs<commit_after>/**
 * Simple dropdown implementation.
 *
 * @author	Alexander Ebert
 * @copyright	2001-2017 WoltLab GmbH
 * @license	GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 * @module	WoltLabSuite/Core/Ui/Dropdown/Simple
 */
define(
	[       'CallbackList', 'Core', 'Dictionary', 'Ui/Alignment', 'Dom/ChangeListener', 'Dom/Traverse', 'Dom/Util', 'Ui/CloseOverlay'],
	function(CallbackList,   Core,   Dictionary,   UiAlignment,    DomChangeListener,    DomTraverse,    DomUtil,    UiCloseOverlay)
{
	"use strict";

	var _availableDropdowns = null;
	var _callbacks = new CallbackList();
	var _didInit = false;
	var _dropdowns = new Dictionary();
	var _menus = new Dictionary();
	var _menuContainer = null;

	/**
	 * @exports	WoltLabSuite/Core/Ui/Dropdown/Simple
	 */
	return {
		/**
		 * Performs initial setup such as setting up dropdowns and binding listeners.
		 */
		setup: function() {
			if (_didInit) return;
			_didInit = true;

			_menuContainer = elCreate('div');
			_menuContainer.className = 'dropdownMenuContainer';
			document.body.appendChild(_menuContainer);

			_availableDropdowns = elByClass('dropdownToggle');

			this.initAll();

			UiCloseOverlay.add('WoltLabSuite/Core/Ui/Dropdown/Simple', this.closeAll.bind(this));
			DomChangeListener.add('WoltLabSuite/Core/Ui/Dropdown/Simple', this.initAll.bind(this));

			document.addEventListener('scroll', this._onScroll.bind(this));

			// expose on window object for backward compatibility
			window.bc_wcfSimpleDropdown = this;
		},

		/**
		 * Loops through all possible dropdowns and registers new ones.
		 */
		initAll: function() {
			for (var i = 0, length = _availableDropdowns.length; i < length; i++) {
				this.init(_availableDropdowns[i], false);
			}
		},

		/**
		 * Initializes a dropdown.
		 *
		 * @param	{Element}	button
		 * @param	{boolean}	isLazyInitialization
		 */
		init: function(button, isLazyInitialization) {
			this.setup();

			if (button.classList.contains('jsDropdownEnabled') || elData(button, 'target')) {
				return false;
			}

			var dropdown = DomTraverse.parentByClass(button, 'dropdown');
			if (dropdown === null) {
				throw new Error("Invalid dropdown passed, button '" + DomUtil.identify(button) + "' does not have a parent with .dropdown.");
			}

			var menu = DomTraverse.nextByClass(button, 'dropdownMenu');
			if (menu === null) {
				throw new Error("Invalid dropdown passed, button '" + DomUtil.identify(button) + "' does not have a menu as next sibling.");
			}

			// move menu into global container
			_menuContainer.appendChild(menu);

			var containerId = DomUtil.identify(dropdown);
			if (!_dropdowns.has(containerId)) {
				button.classList.add('jsDropdownEnabled');
				button.addEventListener(WCF_CLICK_EVENT, this._toggle.bind(this));

				_dropdowns.set(containerId, dropdown);
				_menus.set(containerId, menu);

				if (!containerId.match(/^wcf\d+$/)) {
					elData(menu, 'source', containerId);
				}

				// prevent page scrolling
				if (menu.childElementCount && menu.children[0].classList.contains('scrollableDropdownMenu')) {
					menu = menu.children[0];
					elData(menu, 'scroll-to-active', true);

					var menuHeight = null, menuRealHeight = null;
					menu.addEventListener('wheel', function (event) {
						if (menuHeight === null) menuHeight = menu.clientHeight;
						if (menuRealHeight === null) menuRealHeight = menu.scrollHeight;

						// negative value: scrolling up
						if (event.deltaY < 0 && menu.scrollTop === 0) {
							event.preventDefault();
						}
						else if (event.deltaY > 0 && (menu.scrollTop + menuHeight === menuRealHeight)) {
							event.preventDefault();
						}
					});
				}
			}

			elData(button, 'target', containerId);

			if (isLazyInitialization) {
				setTimeout(function() { Core.triggerEvent(button, WCF_CLICK_EVENT); }, 10);
			}
		},

		/**
		 * Initializes a remote-controlled dropdown.
		 *
		 * @param	{Element}	dropdown	dropdown wrapper element
		 * @param	{Element}	menu		menu list element
		 */
		initFragment: function(dropdown, menu) {
			this.setup();

			var containerId = DomUtil.identify(dropdown);
			if (_dropdowns.has(containerId)) {
				return;
			}

			_dropdowns.set(containerId, dropdown);
			_menuContainer.appendChild(menu);

			_menus.set(containerId, menu);
		},

		/**
		 * Registers a callback for open/close events.
		 *
		 * @param	{string}			containerId	dropdown wrapper id
		 * @param	{function(string, string)}	callback
		 */
		registerCallback: function(containerId, callback) {
			_callbacks.add(containerId, callback);
		},

		/**
		 * Returns the requested dropdown wrapper element.
		 *
		 * @return	{Element}	dropdown wrapper element
		 */
		getDropdown: function(containerId) {
			return _dropdowns.get(containerId);
		},

		/**
		 * Returns the requested dropdown menu list element.
		 *
		 * @return	{Element}	menu list element
		 */
		getDropdownMenu: function(containerId) {
			return _menus.get(containerId);
		},

		/**
		 * Toggles the requested dropdown between opened and closed.
		 *
		 * @param	{string}	containerId	        dropdown wrapper id
		 * @param       {Element=}      referenceElement        alternative reference element, used for reusable dropdown menus
		 */
		toggleDropdown: function(containerId, referenceElement) {
			this._toggle(null, containerId, referenceElement);
		},

		/**
		 * Calculates and sets the alignment of given dropdown.
		 *
		 * @param	{Element}	dropdown	        dropdown wrapper element
		 * @param	{Element}	dropdownMenu	        menu list element
		 * @param       {Element=}      alternateElement        alternative reference element for alignment
		 */
		setAlignment: function(dropdown, dropdownMenu, alternateElement) {
			// check if button belongs to an i18n textarea
			var button = elBySel('.dropdownToggle', dropdown), refDimensionsElement;
			if (button !== null && button.parentNode.classList.contains('inputAddonTextarea')) {
				refDimensionsElement = button;
			}

			UiAlignment.set(dropdownMenu, alternateElement || dropdown, {
				pointerClassNames: ['dropdownArrowBottom', 'dropdownArrowRight'],
				refDimensionsElement: refDimensionsElement || null,

				// alignment
				horizontal: (elData(dropdownMenu, 'dropdown-alignment-horizontal') === 'right') ? 'right' : 'left',
				vertical: (elData(dropdownMenu, 'dropdown-alignment-vertical') === 'top') ? 'top' : 'bottom'
			});
		},

		/**
		 * Calculats and sets the alignment of the dropdown identified by given id.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 */
		setAlignmentById: function(containerId) {
			var dropdown = _dropdowns.get(containerId);
			if (dropdown === undefined) {
				throw new Error("Unknown dropdown identifier '" + containerId + "'.");
			}

			var menu = _menus.get(containerId);

			this.setAlignment(dropdown, menu);
		},

		/**
		 * Returns true if target dropdown exists and is open.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 * @return	{boolean}	true if dropdown exists and is open
		 */
		isOpen: function(containerId) {
			var menu = _menus.get(containerId);
			return (menu !== undefined && menu.classList.contains('dropdownOpen'));
		},

		/**
		 * Opens the dropdown unless it is already open.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 */
		open: function(containerId) {
			var menu = _menus.get(containerId);
			if (menu !== undefined && !menu.classList.contains('dropdownOpen')) {
				this.toggleDropdown(containerId);
			}
		},

		/**
		 * Closes the dropdown identified by given id without notifying callbacks.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 */
		close: function(containerId) {
			var dropdown = _dropdowns.get(containerId);
			if (dropdown !== undefined) {
				dropdown.classList.remove('dropdownOpen');
				_menus.get(containerId).classList.remove('dropdownOpen');
			}
		},

		/**
		 * Closes all dropdowns.
		 */
		closeAll: function() {
			_dropdowns.forEach((function(dropdown, containerId) {
				if (dropdown.classList.contains('dropdownOpen')) {
					dropdown.classList.remove('dropdownOpen');
					_menus.get(containerId).classList.remove('dropdownOpen');

					this._notifyCallbacks(containerId, 'close');
				}
			}).bind(this));
		},

		/**
		 * Destroys a dropdown identified by given id.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 * @return	{boolean}	false for unknown dropdowns
		 */
		destroy: function(containerId) {
			if (!_dropdowns.has(containerId)) {
				return false;
			}

			try {
				this.close(containerId);

				elRemove(_menus.get(containerId));
			}
			catch (e) {
				// the elements might not exist anymore thus ignore all errors while cleaning up
			}

			_menus.delete(containerId);
			_dropdowns.delete(containerId);

			return true;
		},

		/**
		 * Handles dropdown positions in overlays when scrolling in the overlay.
		 *
		 * @param	{Event}		event	event object
		 */
		_onDialogScroll: function(event) {
			var dialogContent = event.currentTarget;
			//noinspection JSCheckFunctionSignatures
			var dropdowns = elBySelAll('.dropdown.dropdownOpen', dialogContent);

			for (var i = 0, length = dropdowns.length; i < length; i++) {
				var dropdown = dropdowns[i];
				var containerId = DomUtil.identify(dropdown);
				var offset = DomUtil.offset(dropdown);
				var dialogOffset = DomUtil.offset(dialogContent);

				// check if dropdown toggle is still (partially) visible
				if (offset.top + dropdown.clientHeight <= dialogOffset.top) {
					// top check
					this.toggleDropdown(containerId);
				}
				else if (offset.top >= dialogOffset.top + dialogContent.offsetHeight) {
					// bottom check
					this.toggleDropdown(containerId);
				}
				else if (offset.left <= dialogOffset.left) {
					// left check
					this.toggleDropdown(containerId);
				}
				else if (offset.left >= dialogOffset.left + dialogContent.offsetWidth) {
					// right check
					this.toggleDropdown(containerId);
				}
				else {
					this.setAlignment(_dropdowns.get(containerId), _menus.get(containerId));
				}
			}
		},

		/**
		 * Recalculates dropdown positions on page scroll.
		 */
		_onScroll: function() {
			_dropdowns.forEach((function(dropdown, containerId) {
				if (dropdown.classList.contains('dropdownOpen')) {
					if (elDataBool(dropdown, 'is-overlay-dropdown-button')) {
						this.setAlignment(dropdown, _menus.get(containerId));
					}
					else {
						this.close(containerId);
					}
				}
			}).bind(this));
		},

		/**
		 * Notifies callbacks on status change.
		 *
		 * @param	{string}	containerId	dropdown wrapper id
		 * @param	{string}	action		can be either 'open' or 'close'
		 */
		_notifyCallbacks: function(containerId, action) {
			_callbacks.forEach(containerId, function(callback) {
				callback(containerId, action);
			});
		},

		/**
		 * Toggles the dropdown's state between open and close.
		 *
		 * @param	{?Event}	event		        event object, should be 'null' if targetId is given
		 * @param	{string?}	targetId	        dropdown wrapper id
		 * @param       {Element=}      alternateElement        alternative reference element for alignment
		 * @return	{boolean}	'false' if event is not null
		 */
		_toggle: function(event, targetId, alternateElement) {
			if (event !== null) {
				event.preventDefault();
				event.stopPropagation();

				//noinspection JSCheckFunctionSignatures
				targetId = elData(event.currentTarget, 'target');
			}

			var dropdown = _dropdowns.get(targetId), preventToggle = false;
			if (dropdown !== undefined) {
				// check if the dropdown is still the same, as some components (e.g. page actions)
				// re-create the parent of a button
				if (event) {
					var button = event.currentTarget, parent = button.parentNode;
					if (parent !== dropdown) {
						parent.classList.add('dropdown');
						parent.id = dropdown.id;

						// remove dropdown class and id from old parent
						dropdown.classList.remove('dropdown');
						dropdown.id = '';

						dropdown = parent;
						_dropdowns.set(targetId, parent);
					}
				}

				// Repeated clicks on the dropdown button will not cause it to close, the only way
				// to close it is by clicking somewhere else in the document or on another dropdown
				// toggle. This is used with the search bar to prevent the dropdown from closing by
				// setting the caret position in the search input field.
				if (elDataBool(dropdown, 'dropdown-prevent-toggle') && dropdown.classList.contains('dropdownOpen')) {
					preventToggle = true;
				}

				// check if 'isOverlayDropdownButton' is set which indicates that the dropdown toggle is within an overlay
				if (elData(dropdown, 'is-overlay-dropdown-button') === '') {
					var dialogContent = DomTraverse.parentByClass(dropdown, 'dialogContent');
					elData(dropdown, 'is-overlay-dropdown-button', (dialogContent !== null));

					if (dialogContent !== null) {
						dialogContent.addEventListener('scroll', this._onDialogScroll.bind(this));
					}
				}
			}

			// close all dropdowns
			_dropdowns.forEach((function(dropdown, containerId) {
				var menu = _menus.get(containerId);

				if (dropdown.classList.contains('dropdownOpen')) {
					if (preventToggle === false) {
						dropdown.classList.remove('dropdownOpen');
						menu.classList.remove('dropdownOpen');

						this._notifyCallbacks(containerId, 'close');
					}
				}
				else if (containerId === targetId && menu.childElementCount > 0) {
					dropdown.classList.add('dropdownOpen');
					menu.classList.add('dropdownOpen');

					if (menu.childElementCount && elDataBool(menu.children[0], 'scroll-to-active')) {
						var list = menu.children[0];
						list.removeAttribute('data-scroll-to-active');

						var active = null;
						for (var i = 0, length = list.childElementCount; i < length; i++) {
							if (list.children[i].classList.contains('active')) {
								active = list.children[i];
								break;
							}
						}

						if (active) {
							list.scrollTop = Math.max((active.offsetTop + active.clientHeight) - menu.clientHeight, 0);
						}
					}

					var itemList = elBySel('.scrollableDropdownMenu', menu);
					if (itemList !== null) {
						itemList.classList[(itemList.scrollHeight > itemList.clientHeight ? 'add' : 'remove')]('forceScrollbar');
					}

					this._notifyCallbacks(containerId, 'open');

					this.setAlignment(dropdown, menu, alternateElement);
				}
			}).bind(this));

			//noinspection JSDeprecatedSymbols
			window.WCF.Dropdown.Interactive.Handler.closeAll();

			return (event === null);
		}
	};
});
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2014 University Of Southern California
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * Created by dcbriggs on 7/28/14.
 */

define(["angular"],
    function (angular) {
        "use strict"
        var runsCreationController = function ($scope, $window, $http, $state) {

            var file;
            var FIVE_GIGABYTES = 5000000000;
            $scope.alerts = [];
            $scope.getAlertIcon = function (status) {
                return (status == 'success') ? "text-success fa fa-check-circle" : "text-danger fa fa-exclamation-triangle";
            };
            $scope.closeAlert = function (index) {
                $scope.alerts.splice(index, 1);
            };
            $scope.uploadProgress = null;
            $scope.addingRun = null;
            $scope.strandRuleOptions = [
                {rule: "unstranded"},
                {rule: "sense"},
                {rule: "anti-sense"}
            ];
            // Defaults

            // We have to define this out of the run object because we need to link it to .rule in the end
            $scope.strandRule = $scope.strandRuleOptions[0];

            $scope.run = {
                genome: "Genome", // TODO: add this in a bit $scope.genome,
                gtf: "gencode.v19.annotation.gtf", // TODO: Add this in a bit $scope.gtf,
                status: -1, // default status to running
                userName: "genericUser",
                readLength: 100,
                mismatches: 3,
                trimUnmapped: false,
                mapFiltered: false,
                genSplice: false
            };

            $scope.sample = $window.apiLinks.sample;

            $scope.uploadFile = function () {
                /*
                 * Upload the file
                 * Angular does not natively work with a formData object so we have to override
                 * the default behavior.  Refer to the link below
                 * http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs
                 */
                var inputFile = document.getElementById("inputFile");

                if (inputFile && !inputFile.value) {
                    return;
                }

                if (inputFile.files[0].size > FIVE_GIGABYTES) {
                    $scope.alerts.push({
                        "type": "danger",
                        "message": "That file is too large, please select one smaller than five gigabytes"
                    });
                    return;
                }

                $scope.alerts = [];

                var fileData = new FormData(document.getElementById("runForm"));
                var xhr = new XMLHttpRequest();
                // TODO: disable the browse button
                xhr.upload.onprogress = function (e) {
                    $scope.$apply(function () {
                        if (e.lengthComputable) {
                            $scope.uploadProgress = Math.round(e.loaded / e.total * 100);
                            $scope.uploadProgressStyle = {width: $scope.uploadProgress + '%'};
                        }
                    });
                };

                xhr.upload.onload = function (e) {
                    // Event listener for when the file completed uploading
                    $scope.$apply(function () {
                        $scope.uploadProgress = null;
                        $scope.alerts.push({
                            "type": "success",
                            "message": "File uploaded successfully"
                        });
                    });
                };

                xhr.onerror = function (e) {
                    $scope.$apply(function () {
                        $scope.uploadProgress = null;
                        $scope.alerts.push({
                            "type": "danger",
                            "message": "File failed to upload"
                        });
                    });
                };

                xhr.open('POST', $window.apiLinks.upload + "/" + $window.tempFolder);
                xhr.send(fileData);

            };

            $scope.cancel = function () {
                $state.go('runs');
            };

            $scope.addRun = function () {
                $scope.alerts = [];
                var invalid = false;
                if ($scope.form.$invalid) {
                    if ($scope.form.name.$invalid) {
                        invalid = true;
                        $scope.alerts.push({'type': 'danger', 'message': 'The name field is required and must be an alphanumeric value (underscores acceptable)'});
                    }
                    if ($scope.form.mismatches.$invalid) {
                        invalid = true;
                        $scope.alerts.push({'type': 'danger', 'message': 'The mismatches field is required and must be an integer between 0 and 8 (inclusive)'});
                    }
                    if ($scope.form.readLength.$invalid) {
                        invalid = true;
                        $scope.alerts.push({'type': 'danger', 'message': 'The read length field is required and must be an integer between 50 and 128 (inclusive)'});
                    }
                    // Check for the empty string because we could have a case where the user enters and then erases an email
                    if ($scope.form.email.$invalid && $scope.form.email.$viewValue != "") {
                        if ($scope.form.email.$viewValue.indexOf(',') != -1) { // Multiple emails input
                            var emails = $scope.form.email.$viewValue.split(",");
                            for (var i = 0; i < emails.length; i++) {
                                if (emails[i].indexOf("@") == -1) {
                                    invalid = true;
                                    $scope.alerts.push({'type': 'danger', 'message': 'Emails must be properly formatted example@domain.com'});
                                    return;
                                }
                            }
                        }
                        else {
                            invalid = true;
                            $scope.alerts.push({'type': 'danger', 'message': 'Emails must be properly formatted example@domain.com'});
                        }
                    }
                    if (invalid) {
                        return;
                    }
                }

                if ($scope.run["genSplice"] && $scope.run["readLength"] < 75) {
                    $scope.alerts.push({'type': 'danger', 'message': 'When the Generate New Splice Candidates option is true, the read length must be >= 75'});
                    return;
                }

                $scope.run["strandRule"] = $scope.strandRule.rule;
                $scope.run["uploadFolder"] = $window.tempFolder;
                $scope.run.email = $scope.form.email.$viewValue;
                try {
                    $scope.run["filename"] = document.getElementById("inputFile").files[0].name;
                }
                catch (error) {
                    return; // An error message should show to the user so we just need to return
                }
                $scope.addingRun = true;
                // Add the run to the database, the runId will be used to create the folder
                $http.post($window.apiLinks.runs, $scope.run).success(function (data) {
                    $scope.addingRun = null;
                    $state.go('runDetails', {name: data.name});
                }).error(function (data, status) {
                    $scope.addingRun = null;
                    if (data.code && data.message) {
                        $scope.alerts.push({
                            'message': data.message,
                            'type': 'danger'
                        });
                    }
                    else if (status == 400) {
                        for (var i = 0; i < data.errors.length; i++) {
                            $scope.alerts.push({'type': 'danger', 'message': data.errors[i].message});
                        }
                    }
                    else if (data.code && data.code == 503) {
                        $scope.alerts.push({'type': 'danger', 'message': 'Could not connect to server, please check your connection.'});
                    }
                    else {
                        $scope.alerts.push({'type': 'danger', 'message': 'Unknown error occurred while retrieving error analysis.  Please contact the developers'});
                    }

                });

            }
        };

        function getFullConstructor() {
            return ["$scope", "$window", "$http", "$state", runsCreationController];
        }

        // For testing so we can mock the injected variables
        function getMinimalConstructor() {
            return runsCreationController;
        }

        return {
            getFullConstructor: getFullConstructor,
            getName: getMinimalConstructor
        };

    });
<commit_msg>File upload error when file size exceeded doesn't show up. Using scope.apply to force alerts to be rendered.<commit_after>/*
 * Copyright 2007-2014 University Of Southern California
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * Created by dcbriggs on 7/28/14.
 */

define(["angular"],
    function (angular) {
        "use strict"
        var runsCreationController = function ($scope, $window, $http, $state) {

            var file;
            var FIVE_GIGABYTES = 5000000000;
            $scope.alerts = [];
            $scope.getAlertIcon = function (status) {
                return (status == 'success') ? "text-success fa fa-check-circle" : "text-danger fa fa-exclamation-triangle";
            };
            $scope.closeAlert = function (index) {
                $scope.alerts.splice(index, 1);
            };
            $scope.uploadProgress = null;
            $scope.addingRun = null;
            $scope.strandRuleOptions = [
                {rule: "unstranded"},
                {rule: "sense"},
                {rule: "anti-sense"}
            ];
            // Defaults

            // We have to define this out of the run object because we need to link it to .rule in the end
            $scope.strandRule = $scope.strandRuleOptions[0];

            $scope.run = {
                genome: "Genome", // TODO: add this in a bit $scope.genome,
                gtf: "gencode.v19.annotation.gtf", // TODO: Add this in a bit $scope.gtf,
                status: -1, // default status to running
                userName: "genericUser",
                readLength: 100,
                mismatches: 3,
                trimUnmapped: false,
                mapFiltered: false,
                genSplice: false
            };

            $scope.sample = $window.apiLinks.sample;

            $scope.uploadFile = function () {
                /*
                 * Upload the file
                 * Angular does not natively work with a formData object so we have to override
                 * the default behavior.  Refer to the link below
                 * http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs
                 */
                var inputFile = document.getElementById("inputFile");

                if (inputFile && !inputFile.value) {
                    return;
                }

                if (inputFile.files[0].size > FIVE_GIGABYTES) {
                    $scope.$apply(function () {
                        $scope.alerts.push({
                            "type": "danger",
                            "message": "That file is too large, please select one smaller than five gigabytes"
                        });
                    });

                    return;
                }

                $scope.alerts = [];

                var fileData = new FormData(document.getElementById("runForm"));
                var xhr = new XMLHttpRequest();
                // TODO: disable the browse button
                xhr.upload.onprogress = function (e) {
                    $scope.$apply(function () {
                        if (e.lengthComputable) {
                            $scope.uploadProgress = Math.round(e.loaded / e.total * 100);
                            $scope.uploadProgressStyle = {width: $scope.uploadProgress + '%'};
                        }
                    });
                };

                xhr.upload.onload = function (e) {
                    // Event listener for when the file completed uploading
                    $scope.$apply(function () {
                        $scope.uploadProgress = null;
                        $scope.alerts.push({
                            "type": "success",
                            "message": "File uploaded successfully"
                        });
                    });
                };

                xhr.onerror = function (e) {
                    $scope.$apply(function () {
                        $scope.uploadProgress = null;
                        $scope.alerts.push({
                            "type": "danger",
                            "message": "File failed to upload"
                        });
                    });
                };

                xhr.open('POST', $window.apiLinks.upload + "/" + $window.tempFolder);
                xhr.send(fileData);

            };

            $scope.cancel = function () {
                $state.go('runs');
            };

            $scope.addRun = function () {
                $scope.alerts = [];
                var invalid = false;
                if ($scope.form.$invalid) {
                    if ($scope.form.name.$invalid) {
                        invalid = true;
                        $scope.alerts.push({'type': 'danger', 'message': 'The name field is required and must be an alphanumeric value (underscores acceptable)'});
                    }
                    if ($scope.form.mismatches.$invalid) {
                        invalid = true;
                        $scope.alerts.push({'type': 'danger', 'message': 'The mismatches field is required and must be an integer between 0 and 8 (inclusive)'});
                    }
                    if ($scope.form.readLength.$invalid) {
                        invalid = true;
                        $scope.alerts.push({'type': 'danger', 'message': 'The read length field is required and must be an integer between 50 and 128 (inclusive)'});
                    }
                    // Check for the empty string because we could have a case where the user enters and then erases an email
                    if ($scope.form.email.$invalid && $scope.form.email.$viewValue != "") {
                        if ($scope.form.email.$viewValue.indexOf(',') != -1) { // Multiple emails input
                            var emails = $scope.form.email.$viewValue.split(",");
                            for (var i = 0; i < emails.length; i++) {
                                if (emails[i].indexOf("@") == -1) {
                                    invalid = true;
                                    $scope.alerts.push({'type': 'danger', 'message': 'Emails must be properly formatted example@domain.com'});
                                    return;
                                }
                            }
                        }
                        else {
                            invalid = true;
                            $scope.alerts.push({'type': 'danger', 'message': 'Emails must be properly formatted example@domain.com'});
                        }
                    }
                    if (invalid) {
                        return;
                    }
                }

                if ($scope.run["genSplice"] && $scope.run["readLength"] < 75) {
                    $scope.alerts.push({'type': 'danger', 'message': 'When the Generate New Splice Candidates option is true, the read length must be >= 75'});
                    return;
                }

                $scope.run["strandRule"] = $scope.strandRule.rule;
                $scope.run["uploadFolder"] = $window.tempFolder;
                $scope.run.email = $scope.form.email.$viewValue;
                try {
                    $scope.run["filename"] = document.getElementById("inputFile").files[0].name;
                }
                catch (error) {
                    return; // An error message should show to the user so we just need to return
                }
                $scope.addingRun = true;
                // Add the run to the database, the runId will be used to create the folder
                $http.post($window.apiLinks.runs, $scope.run).success(function (data) {
                    $scope.addingRun = null;
                    $state.go('runDetails', {name: data.name});
                }).error(function (data, status) {
                    $scope.addingRun = null;
                    if (data.code && data.message) {
                        $scope.alerts.push({
                            'message': data.message,
                            'type': 'danger'
                        });
                    }
                    else if (status == 400) {
                        for (var i = 0; i < data.errors.length; i++) {
                            $scope.alerts.push({'type': 'danger', 'message': data.errors[i].message});
                        }
                    }
                    else if (data.code && data.code == 503) {
                        $scope.alerts.push({'type': 'danger', 'message': 'Could not connect to server, please check your connection.'});
                    }
                    else {
                        $scope.alerts.push({'type': 'danger', 'message': 'Unknown error occurred while retrieving error analysis.  Please contact the developers'});
                    }

                });

            }
        };

        function getFullConstructor() {
            return ["$scope", "$window", "$http", "$state", runsCreationController];
        }

        // For testing so we can mock the injected variables
        function getMinimalConstructor() {
            return runsCreationController;
        }

        return {
            getFullConstructor: getFullConstructor,
            getName: getMinimalConstructor
        };

    });
<|endoftext|>