From 54ea867ad6761579295d7bbd5b4e42b59b53c127 Mon Sep 17 00:00:00 2001
From: Christopher Fullarton <christopher@fullarton.eu>
Date: Thu, 5 May 2016 19:11:10 +0200
Subject: [PATCH 01/10] Remove session usage where possible, change routing to
 include the hashtag

---
 .../layout/global/scripts/onRendered.js       |  12 +-
 .../layout/global/templates/loading.html      |   3 +
 .../layout/region_footer/footer_helper.js     |  17 +-
 .../client/layout/region_header/header.html   |   4 +-
 .../client/layout/region_header/header.js     |  43 +-
 .../client/layout/region_header/lib.js        |  15 +-
 .../layout/region_header/question_list.js     |  43 +-
 .../view_answeroptions/scripts/events.js      |  11 +-
 .../layout/view_answeroptions/scripts/lib.js  |   3 +-
 .../view_answeroptions/scripts/onCreated.js   |   4 -
 .../view_answeroptions/scripts/onRendered.js  |   2 +-
 .../view_choose_nickname/scripts/events.js    |   7 +-
 .../view_choose_nickname/scripts/onCreated.js |   5 +-
 .../view_hashtag_management/scripts/events.js |  67 +--
 .../view_hashtag_management/scripts/lib.js    |   5 +
 .../scripts/onCreated.js                      |  47 --
 .../scripts/onDestroyed.js                    |   9 +
 .../layout/view_leaderboard/scripts/events.js |   6 +-
 .../view_leaderboard/scripts/helpers.js       |  10 +-
 .../layout/view_leaderboard/scripts/lib.js    |   2 +-
 .../view_leaderboard/scripts/onCreated.js     |   8 +-
 .../view_leaderboard/scripts/onDestroyed.js   |  23 +-
 .../view_leaderboard/scripts/onRendered.js    |  27 +-
 .../view_live_results/scripts/events.js       |  18 +-
 .../view_live_results/scripts/helpers.js      |  14 +-
 .../layout/view_live_results/scripts/lib.js   |   4 +-
 .../view_live_results/scripts/onRendered.js   |   4 +-
 .../layout/view_lobby/scripts/events.js       |  16 +-
 .../layout/view_lobby/scripts/helpers.js      |  19 +-
 .../client/layout/view_lobby/scripts/lib.js   |   4 +-
 .../layout/view_lobby/scripts/onCreated.js    |  47 +-
 .../layout/view_lobby/scripts/onRendered.js   |   4 +-
 .../layout/view_questions/scripts/events.js   |   2 +-
 .../layout/view_questions/scripts/lib.js      |   7 +-
 .../view_questions/scripts/onCreated.js       |   4 -
 .../view_questions/scripts/onRendered.js      |   7 +-
 .../layout/view_timer/scripts/events.js       |  19 +-
 .../client/layout/view_timer/scripts/lib.js   |  17 +-
 .../layout/view_timer/scripts/onCreated.js    |   8 +-
 .../layout/view_timer/scripts/onDestroyed.js  |   5 +-
 .../layout/view_timer/scripts/onRendered.js   |  18 +-
 .../view_timer/templates/createTimerView.html |   2 +-
 .../layout/view_voting/scripts/events.js      |   4 +-
 .../client/layout/view_voting/scripts/lib.js  |   8 +-
 .../layout/view_voting/scripts/onCreated.js   |  25 +-
 arsnova.click/client/lib/connection.js        |   5 +-
 arsnova.click/client/lib/local_storage.js     |   2 +-
 .../event_stack_observer/scripts/lib.js       |   8 +-
 .../client/plugins/sound/scripts/helpers.js   |   5 +-
 .../client/plugins/sound/scripts/lib.js       |   4 +-
 .../plugins/sound/scripts/onRendered.js       |  17 +-
 arsnova.click/client/routes.js                | 425 ++++++++++--------
 52 files changed, 563 insertions(+), 532 deletions(-)
 create mode 100644 arsnova.click/client/layout/global/templates/loading.html
 create mode 100644 arsnova.click/client/layout/view_hashtag_management/scripts/onDestroyed.js

diff --git a/arsnova.click/client/layout/global/scripts/onRendered.js b/arsnova.click/client/layout/global/scripts/onRendered.js
index 6e394b064..3895bb7ab 100644
--- a/arsnova.click/client/layout/global/scripts/onRendered.js
+++ b/arsnova.click/client/layout/global/scripts/onRendered.js
@@ -15,17 +15,25 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import  * as localData from '/client/lib/local_storage.js';
 import {Splashscreen} from '/client/plugins/splashscreen/scripts/lib.js';
 import * as hashtagLib from '/client/layout/view_hashtag_management/scripts/lib.js';
+import {HashtagsCollection} from '/lib/hashtags/collection.js';
+import {EventManagerCollection} from '/lib/eventmanager/collection.js';
 
 Template.home.onRendered(function () {
-	if (Session.get("localStorageAvailable") && localData.getAllHashtags().length > 0) {
+	if (localStorage.getItem("localStorageAvailable") && localData.getAllHashtags().length > 0) {
 		hashtagLib.setHashtagSplashscreen(new Splashscreen({
 			autostart: true,
 			templateName: "showHashtagsSplashscreen"
 		}));
 	}
+	HashtagsCollection.find().observeChanges({
+		added: function (id, doc) {
+			if (doc.hashtag === $("#hashtag-input-field").val()) {
+				$("#addNewHashtag").attr("disabled", "disabled");
+			}
+		}
+	});
 });
diff --git a/arsnova.click/client/layout/global/templates/loading.html b/arsnova.click/client/layout/global/templates/loading.html
new file mode 100644
index 000000000..868859757
--- /dev/null
+++ b/arsnova.click/client/layout/global/templates/loading.html
@@ -0,0 +1,3 @@
+<template name="loading">
+    <p>Loading</p>
+</template>
\ No newline at end of file
diff --git a/arsnova.click/client/layout/region_footer/footer_helper.js b/arsnova.click/client/layout/region_footer/footer_helper.js
index 51df12ca6..8bf09ce6d 100644
--- a/arsnova.click/client/layout/region_footer/footer_helper.js
+++ b/arsnova.click/client/layout/region_footer/footer_helper.js
@@ -16,14 +16,13 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {TAPi18n} from 'meteor/tap:i18n';
 import * as localData from '/client/lib/local_storage.js';
 import {ErrorSplashscreen} from '/client/plugins/splashscreen/scripts/lib.js';
 
 Template.footer.onRendered(function () {
-	Session.set("footerIsHidden", true);
+	localStorage.setItem("footerIsHidden", true);
 });
 
 Template.footer.helpers({
@@ -31,9 +30,9 @@ Template.footer.helpers({
 		return Router.current().route.path() === '/';
 	},
 	isInMemberlistOrPollingPathAndIsInstructor: function () {
-		var currentRouterPath = Router.current().route.path();
+		var currentRouterPath = Router.current().route.getName();
 
-		if (Session.get("isOwner") && (currentRouterPath === '/memberlist' || currentRouterPath === '/results' || currentRouterPath === '/statistics')) {
+		if (localData.containsHashtag(Router.current().params.quizName) && (currentRouterPath === ':quizName.memberlist' || currentRouterPath === ':quizName.results' || currentRouterPath === ':quizName.statistics')) {
 			// TODO currently not working in IE and Webkit
 			var ua = window.navigator.userAgent;
 			var msie = ua.indexOf("MSIE ");
@@ -48,7 +47,7 @@ Template.footer.helpers({
 		}
 	},
 	footerIsHidden: function () {
-		var isFooterHidden = Session.get("footerIsHidden");
+		var isFooterHidden = localStorage.getItem("footerIsHidden");
 		if (!isFooterHidden) {
 			return true;
 		} else {
@@ -69,16 +68,16 @@ Template.footer.helpers({
 			"/impressum",
 			"/ueber"
 		];
-		return (showHomeSl.indexOf(Router.current().route.path()) !== -1) && (Session.get("lastPage") !== undefined) && (showHome.indexOf(Session.get("lastPage")) === -1) && (Router.current().route.path() !== '/');
+		return (showHomeSl.indexOf(Router.current().route.path()) !== -1) && (localStorage.getItem(Router.current().params.quizName + "lastPage") !== undefined) && (showHome.indexOf(localStorage.getItem(Router.current().params.quizName + "lastPage")) === -1) && (Router.current().route.path() !== '/');
 	},
 	getLastPage: function () {
-		return Session.get("lastPage");
+		return localStorage.getItem(Router.current().params.quizName + "lastPage");
 	}
 });
 
 Template.footer.events({
 	"click #toPrevPage": function () {
-		Session.set("lastPage", undefined);
+		localStorage.setItem(Router.current().params.quizName + "lastPage", undefined);
 	},
 	"click #hideShowFooterBar": function () {
 		if ($("#footerBar").hasClass("hide")) {
@@ -86,7 +85,7 @@ Template.footer.events({
 			$("#hideShowFooterBar").addClass("hide");
 			$("#footer-info-div").removeClass("hiddenStyle").addClass("showStyle");
 
-			Session.set("footerIsHidden", false);
+			localStorage.setItem(Router.current().params.quizName + "footerIsHidden", false);
 		}
 	},
 	"click #js-activate-fullscreen": function () {
diff --git a/arsnova.click/client/layout/region_header/header.html b/arsnova.click/client/layout/region_header/header.html
index 6c5a27c47..da868acb9 100644
--- a/arsnova.click/client/layout/region_header/header.html
+++ b/arsnova.click/client/layout/region_header/header.html
@@ -23,7 +23,7 @@
 					<div class="col-md-10 col-md-offset-1 header-title">
 						<div class="container-fluid center-block">
 							<div class="text-center text-size-medium logo-header-chars">
-								<a href="/resetToHome">
+								<a href="{{getCurrentRoute}}/resetToHome">
 									<span class="color-changing-ars">a r s </span><span
 										class="color-changing-nova">n o v a </span>
 									<span class="color-changing-dot">.</span>
@@ -39,7 +39,7 @@
 						<div class="row">
 							<div class="col-xs-2">
 								<div class="arsnova-logo">
-									<img src="images/arsnova_click.svg" alt="arsnova_click_logo"/>
+									<img src="/images/arsnova_click.svg" alt="arsnova_click_logo"/>
 								</div>
 							</div>
 							<div class="col-xs-8">
diff --git a/arsnova.click/client/layout/region_header/header.js b/arsnova.click/client/layout/region_header/header.js
index 5211e1a47..a60b1b5a8 100644
--- a/arsnova.click/client/layout/region_header/header.js
+++ b/arsnova.click/client/layout/region_header/header.js
@@ -16,7 +16,6 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {TAPi18n} from 'meteor/tap:i18n';
 import * as localData from '/client/lib/local_storage.js';
@@ -25,13 +24,16 @@ import {Splashscreen} from "/client/plugins/splashscreen/scripts/lib";
 import {ErrorSplashscreen} from '/client/plugins/splashscreen/scripts/lib.js';
 
 Template.header.onCreated(function () {
-	Session.setDefault("slider2", 80);
-	Session.setDefault("globalVolume", 80);
+	localStorage.setItem("slider2", 80);
+	localStorage.setItem("globalVolume", 80);
 
 	setBuzzsound1('waity.mp3');
 });
 
 Template.header.helpers({
+	getCurrentRoute: function () {
+		return "/" + Router.current().params.quizName;
+	},
 	isInHomePathOrIsStudent: function () {
 		switch (Router.current().route.path()) {
 			case '/':
@@ -41,17 +43,16 @@ Template.header.helpers({
 			case '/impressum':
 				return true;
 		}
-		return !Session.get("isOwner");
+		return !localData.containsHashtag(Router.current().params.quizName);
 	},
 	currentHashtag: function () {
-		return Session.get("hashtag");
+		return Router.current().params.quizName;
 	},
 	isEditingQuestion: function () {
-		switch (Router.current().route.path()) {
-			case '/question':
-			case '/answeroptions':
-			case '/settimer':
-			case '/readconfirmationrequired':
+		switch (Router.current().route.getName()) {
+			case ":quizName.question":
+			case ":quizName.answeroptions":
+			case ":quizName.settimer":
 				return true;
 			default:
 				return false;
@@ -60,8 +61,8 @@ Template.header.helpers({
 });
 
 Template.header.events({
-	'click .kill-session-switch, click .arsnova-logo': function () {
-		if (Session.get("isOwner")) {
+	'click .kill-session-switch-wrapper, click .arsnova-logo': function () {
+		if (localData.containsHashtag(Router.current().params.quizName)) {
 			buzzsound1.stop();
 
 			new Splashscreen({
@@ -70,7 +71,7 @@ Template.header.events({
 				closeOnButton: '#closeDialogButton, #resetSessionButton',
 				onRendered: function (instance) {
 					instance.templateSelector.find('#resetSessionButton').on('click', function () {
-						Meteor.call("Main.killAll", localData.getPrivateKey(), Session.get("hashtag"), function (err) {
+						Meteor.call("Main.killAll", localData.getPrivateKey(), Router.current().params.quizName, function (err) {
 							if (err) {
 								new ErrorSplashscreen({
 									autostart: true,
@@ -83,7 +84,7 @@ Template.header.events({
 				}
 			});
 		} else {
-			Router.go("/resetToHome");
+			Router.go("/" + Router.current().params.quizName + "/resetToHome");
 		}
 	},
 	'click .sound-button': function () {
@@ -94,7 +95,7 @@ Template.header.events({
 			onRendered: function (instance) {
 				instance.templateSelector.find('#soundSelect').on('change', function (event) {
 					buzzsound1.stop();
-					Session.set("soundIsPlaying", false);
+					localStorage.setItem(Router.current().params.quizName + "soundIsPlaying", false);
 					switch ($(event.target).val()) {
 						case "Song1":
 							setBuzzsound1("bensound-thelounge.mp3");
@@ -109,28 +110,28 @@ Template.header.events({
 				});
 
 				instance.templateSelector.find("#js-btn-playStopMusic").on('click', function () {
-					if (Session.get("soundIsPlaying")) {
+					if (localStorage.getItem(Router.current().params.quizName + "soundIsPlaying")) {
 						buzzsound1.stop();
-						Session.set("soundIsPlaying", false);
+						localStorage.setItem(Router.current().params.quizName + "soundIsPlaying", false);
 					} else {
 						buzzsound1.play();
-						Session.set("soundIsPlaying", true);
+						localStorage.setItem(Router.current().params.quizName + "soundIsPlaying", true);
 					}
 				});
 
 				instance.templateSelector.find("#js-btn-hideSoundModal").on('click', function () {
 					buzzsound1.stop();
-					Session.set("soundIsPlaying", false);
+					localStorage.setItem(Router.current().params.quizName + "soundIsPlaying", false);
 				});
 
 				instance.templateSelector.find('#isSoundOnButton').on('click', function () {
 					var btn = $('#isSoundOnButton');
 					btn.toggleClass("down");
 					if (btn.hasClass("down")) {
-						Session.set("togglemusic", true);
+						localStorage.setItem(Router.current().params.quizName + "togglemusic", true);
 						btn.html(TAPi18n.__("plugins.sound.active"));
 					} else {
-						Session.set("togglemusic", false);
+						localStorage.setItem(Router.current().params.quizName + "togglemusic", false);
 						btn.html(TAPi18n.__("plugins.sound.inactive"));
 					}
 				});
diff --git a/arsnova.click/client/layout/region_header/lib.js b/arsnova.click/client/layout/region_header/lib.js
index c66d815be..8d62690f0 100644
--- a/arsnova.click/client/layout/region_header/lib.js
+++ b/arsnova.click/client/layout/region_header/lib.js
@@ -16,7 +16,6 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import {TAPi18n} from 'meteor/tap:i18n';
 import {QuestionGroupCollection} from '/lib/questions/collection.js';
 import {AnswerOptionCollection} from '/lib/answeroptions/collection.js';
@@ -52,7 +51,7 @@ export function addNewQuestion(callback) {
 	var index = QuestionGroupCollection.findOne().questionList.length;
 	Meteor.call("QuestionGroupCollection.addQuestion", {
 		privateKey: localData.getPrivateKey(),
-		hashtag: Session.get("hashtag"),
+		hashtag: Router.current().params.quizName,
 		questionIndex: index,
 		questionText: ""
 	}, (err) => {
@@ -65,7 +64,7 @@ export function addNewQuestion(callback) {
 			for (var i = 0; i < 4; i++) {
 				Meteor.call('AnswerOptionCollection.addOption', {
 					privateKey: localData.getPrivateKey(),
-					hashtag: Session.get("hashtag"),
+					hashtag: Router.current().params.quizName,
 					questionIndex: index,
 					answerOptionNumber: i,
 					answerText: "",
@@ -73,14 +72,14 @@ export function addNewQuestion(callback) {
 				});
 			}
 
-			localData.addQuestion(Session.get("hashtag"), QuestionGroupCollection.findOne().questionList.length, "");
+			localData.addQuestion(Router.current().params.quizName, QuestionGroupCollection.findOne().questionList.length, "");
 
-			var validQuestions = Session.get("validQuestions");
+			var validQuestions = localStorage.getItem(Router.current().params.quizName + "validQuestions");
 			validQuestions[index] = false;
-			Session.set("validQuestions", validQuestions);
+			localStorage.setItem(Router.current().params.quizName + "validQuestions", validQuestions);
 
-			Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Session.get("hashtag"), index, function () {
-				Router.go("/question");
+			Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Router.current().params.quizName, index, function () {
+				Router.go("/" + Router.current().params.quizName + "/question");
 				if (callback) {
 					callback();
 				}
diff --git a/arsnova.click/client/layout/region_header/question_list.js b/arsnova.click/client/layout/region_header/question_list.js
index 49a66913c..3a06950c6 100644
--- a/arsnova.click/client/layout/region_header/question_list.js
+++ b/arsnova.click/client/layout/region_header/question_list.js
@@ -16,7 +16,6 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {Tracker} from 'meteor/tracker';
 import {TAPi18n} from 'meteor/tap:i18n';
@@ -30,11 +29,11 @@ import * as lib from './lib.js';
 var redirectTracker = null;
 
 Template.questionList.onCreated(function () {
-	Session.set("validQuestions", []);
+	localStorage.setItem(Router.current().params.quizName + "validQuestions", []);
 
-	this.subscribe("EventManagerCollection.join", Session.get("hashtag"));
-	this.subscribe('QuestionGroupCollection.questionList', Session.get("hashtag"));
-	this.subscribe('AnswerOptionCollection.instructor', localData.getPrivateKey(), Session.get("hashtag"));
+	this.subscribe("EventManagerCollection.join", Router.current().params.quizName);
+	this.subscribe('QuestionGroupCollection.questionList', Router.current().params.quizName);
+	this.subscribe('AnswerOptionCollection.instructor', localData.getPrivateKey(), Router.current().params.quizName);
 
 	this.autorun(() => {
 		if (this.subscriptionsReady()) {
@@ -43,14 +42,14 @@ Template.questionList.onCreated(function () {
 			}
 
 			var questionList = QuestionGroupCollection.findOne().questionList;
-			var validQuestions = Session.get("validQuestions");
+			var validQuestions = localStorage.getItem(Router.current().params.quizName + "validQuestions");
 			if (questionList.length >= validQuestions.length) {
 				return;
 			}
 
 			validQuestions.splice(questionList.length - 1, validQuestions.length - questionList.length);
 
-			Session.set("validQuestions", validQuestions);
+			localStorage.setItem(Router.current().params.quizName + "validQuestions", validQuestions);
 		}
 	});
 });
@@ -62,7 +61,7 @@ Template.questionList.onDestroyed(function () {
 Template.questionList.onRendered(function () {
 	let handleRedirect = true;
 	redirectTracker = Tracker.autorun(function () {
-		let validQuestions = Session.get("validQuestions");
+		let validQuestions = localStorage.getItem(Router.current().params.quizName + "validQuestions");
 		if (!validQuestions || validQuestions.length === 0) {
 			return;
 		}
@@ -74,14 +73,14 @@ Template.questionList.onRendered(function () {
 				break;
 			}
 		}
-		if (!Session.get("overrideValidQuestionRedirect") && allValid && handleRedirect) {
-			Session.set("overrideValidQuestionRedirect", undefined);
-			Meteor.call("MemberListCollection.removeFromSession", localData.getPrivateKey(), Session.get("hashtag"));
-			Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Session.get("hashtag"), 0);
-			Meteor.call("EventManagerCollection.setSessionStatus", localData.getPrivateKey(), Session.get("hashtag"), 2);
-			Router.go("/memberlist");
+		if (!localStorage.getItem(Router.current().params.quizName + "overrideValidQuestionRedirect") && allValid && handleRedirect) {
+			localStorage.setItem(Router.current().params.quizName + "overrideValidQuestionRedirect", undefined);
+			Meteor.call("MemberListCollection.removeFromSession", localData.getPrivateKey(), Router.current().params.quizName);
+			Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Router.current().params.quizName, 0);
+			Meteor.call("EventManagerCollection.setSessionStatus", localData.getPrivateKey(), Router.current().params.quizName, 2);
+			Router.go("/" + Router.current().params.quizName + "/memberlist");
 		} else {
-			Session.set("overrideValidQuestionRedirect", undefined);
+			localStorage.setItem(Router.current().params.quizName + "overrideValidQuestionRedirect", undefined);
 			handleRedirect = false;
 			redirectTracker.stop();
 		}
@@ -103,28 +102,28 @@ Template.questionList.helpers({
 		return index === EventManagerCollection.findOne().questionIndex;
 	},
 	hasCompleteContent: function (index) {
-		var validQuestions = Session.get("validQuestions");
+		var validQuestions = localStorage.getItem(Router.current().params.quizName + "validQuestions");
 		validQuestions[index] = lib.checkForValidQuestions(index);
-		Session.set("validQuestions", validQuestions);
+		localStorage.setItem(Router.current().params.quizName + "validQuestions", validQuestions);
 		return validQuestions[index];
 	}
 });
 
 Template.questionList.events({
 	'click .questionIcon:not(.active)': function (event) {
-		Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Session.get("hashtag"), parseInt($(event.target).closest(".questionIcon").attr("id").replace("questionIcon_", "")), function () {
+		Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Router.current().params.quizName, parseInt($(event.target).closest(".questionIcon").attr("id").replace("questionIcon_", "")), function () {
 			questionLib.checkForMarkdown();
 		});
 	},
 	'click .removeQuestion': function (event) {
 		var id = parseInt($(event.target).closest(".questionIcon").attr("id").replace("questionIcon_", ""));
 		if (id > 0) {
-			Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Session.get("hashtag"), (id - 1));
+			Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Router.current().params.quizName, (id - 1));
 		}
 
 		Meteor.call('AnswerOptionCollection.deleteOption', {
 			privateKey: localData.getPrivateKey(),
-			hashtag: Session.get("hashtag"),
+			hashtag: Router.current().params.quizName,
 			questionIndex: id,
 			answerOptionNumber: -1
 		}, (err) => {
@@ -136,7 +135,7 @@ Template.questionList.events({
 			} else {
 				Meteor.call("QuestionGroupCollection.removeQuestion", {
 					privateKey: localData.getPrivateKey(),
-					hashtag: Session.get("hashtag"),
+					hashtag: Router.current().params.quizName,
 					questionIndex: id
 				}, (err) => {
 					if (err) {
@@ -145,7 +144,7 @@ Template.questionList.events({
 							errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages." + err.reason)
 						});
 					} else {
-						localData.removeQuestion(Session.get("hashtag"), id);
+						localData.removeQuestion(Router.current().params.quizName, id);
 						if (QuestionGroupCollection.findOne().questionList.length === 0) {
 							lib.addNewQuestion(questionLib.checkForMarkdown);
 						} else {
diff --git a/arsnova.click/client/layout/view_answeroptions/scripts/events.js b/arsnova.click/client/layout/view_answeroptions/scripts/events.js
index 9d0c38409..628774e2f 100644
--- a/arsnova.click/client/layout/view_answeroptions/scripts/events.js
+++ b/arsnova.click/client/layout/view_answeroptions/scripts/events.js
@@ -16,7 +16,6 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {TAPi18n} from 'meteor/tap:i18n';
 import {EventManagerCollection} from '/lib/eventmanager/collection.js';
@@ -42,7 +41,7 @@ Template.createAnswerOptions.events({
 		if (answerOptionsCount < 26) {
 			const answerOption = {
 				privateKey: localData.getPrivateKey(),
-				hashtag: Session.get("hashtag"),
+				hashtag: Router.current().params.quizName,
 				questionIndex: EventManagerCollection.findOne().questionIndex,
 				answerText: "",
 				answerOptionNumber: answerOptionsCount,
@@ -75,11 +74,11 @@ Template.createAnswerOptions.events({
 
 			Meteor.call('AnswerOptionCollection.deleteOption', {
 				privateKey: localData.getPrivateKey(),
-				hashtag: Session.get("hashtag"),
+				hashtag: Router.current().params.quizName,
 				questionIndex: EventManagerCollection.findOne().questionIndex,
 				answerOptionNumber: answerOptionsCount - 1
 			});
-			localData.deleteAnswerOption(Session.get("hashtag"), EventManagerCollection.findOne().questionIndex, answerOptionsCount - 1);
+			localData.deleteAnswerOption(Router.current().params.quizName, EventManagerCollection.findOne().questionIndex, answerOptionsCount - 1);
 
 			answerOptionsCount--;
 			if (answerOptionsCount === 1) {
@@ -90,7 +89,7 @@ Template.createAnswerOptions.events({
 		}
 	},
 	"click #backButton": function () {
-		Router.go('/question');
+		Router.go("/" + Router.current().params.quizName + "/question");
 	},
 	"click #forwardButton": function () {
 		var err = parseAnswerOptionInput(EventManagerCollection.findOne().questionIndex);
@@ -101,7 +100,7 @@ Template.createAnswerOptions.events({
 				errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages." + err.reason)
 			});
 		} else {
-			Router.go("/settimer");
+			Router.go("/" + Router.current().params.quizName + "/settimer");
 		}
 	},
 	"keydown .input-field": function (event) {
diff --git a/arsnova.click/client/layout/view_answeroptions/scripts/lib.js b/arsnova.click/client/layout/view_answeroptions/scripts/lib.js
index d82eea6c7..0d06aa6cf 100644
--- a/arsnova.click/client/layout/view_answeroptions/scripts/lib.js
+++ b/arsnova.click/client/layout/view_answeroptions/scripts/lib.js
@@ -16,7 +16,6 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import {AnswerOptionCollection} from '/lib/answeroptions/collection.js';
 import * as localData from '/client/lib/local_storage.js';
 
@@ -36,7 +35,7 @@ export function parseAnswerOptionInput(index) {
 		var isCorrect = $('div#answerOption-' + i + ' .check-mark-checked').length > 0 ? 1 : 0;
 		var answer = {
 			privateKey: localData.getPrivateKey(),
-			hashtag: Session.get("hashtag"),
+			hashtag: Router.current().params.quizName,
 			questionIndex: index,
 			answerOptionNumber: i,
 			answerText: text,
diff --git a/arsnova.click/client/layout/view_answeroptions/scripts/onCreated.js b/arsnova.click/client/layout/view_answeroptions/scripts/onCreated.js
index f3514dc3e..49053db4e 100644
--- a/arsnova.click/client/layout/view_answeroptions/scripts/onCreated.js
+++ b/arsnova.click/client/layout/view_answeroptions/scripts/onCreated.js
@@ -15,11 +15,7 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
-import * as localData from '/client/lib/local_storage.js';
 
 Template.createAnswerOptions.onCreated(function () {
-	this.subscribe('AnswerOptionCollection.instructor', localData.getPrivateKey(), Session.get("hashtag"));
-	this.subscribe('EventManagerCollection.join', Session.get("hashtag"));
 });
diff --git a/arsnova.click/client/layout/view_answeroptions/scripts/onRendered.js b/arsnova.click/client/layout/view_answeroptions/scripts/onRendered.js
index 78951cc12..7ca2ea658 100644
--- a/arsnova.click/client/layout/view_answeroptions/scripts/onRendered.js
+++ b/arsnova.click/client/layout/view_answeroptions/scripts/onRendered.js
@@ -43,7 +43,7 @@ Template.createAnswerOptions.onRendered(function () {
 		}
 
 		lib.parseAnswerOptionInput(index);
-		Router.go("/question");
+		Router.go("/" + Router.current().params.quizName + "/question");
 	});
 	body.on('click', '.removeQuestion', function () {
 		index = EventManagerCollection.findOne().questionIndex;
diff --git a/arsnova.click/client/layout/view_choose_nickname/scripts/events.js b/arsnova.click/client/layout/view_choose_nickname/scripts/events.js
index 47199a612..5116c9585 100644
--- a/arsnova.click/client/layout/view_choose_nickname/scripts/events.js
+++ b/arsnova.click/client/layout/view_choose_nickname/scripts/events.js
@@ -16,7 +16,6 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {TAPi18n} from 'meteor/tap:i18n';
 import {MemberListCollection} from '/lib/member_list/collection.js';
@@ -29,7 +28,7 @@ Template.nick.events({
 		var nickname = $("#nickname-input-field").val();
 		var bgColor = lib.rgbToHex(lib.getRandomInt(0, 255), lib.getRandomInt(0, 255), lib.getRandomInt(0, 255));
 		Meteor.call('MemberListCollection.addLearner', {
-			hashtag: Session.get("hashtag"),
+			hashtag: Router.current().params.quizName,
 			nick: nickname,
 			backgroundColor: bgColor,
 			foregroundColor: lib.transformForegroundColor(lib.hexToRgb(bgColor))
@@ -39,8 +38,8 @@ Template.nick.events({
 				ErrorSplashscreen.setErrorText(TAPi18n.__("plugins.splashscreen.error.error_messages." + err.reason));
 				ErrorSplashscreen.open();
 			} else {
-				Session.set("nick", nickname);
-				Router.go("/memberlist");
+				localStorage.setItem(Router.current().params.quizName + "nick", nickname);
+				Router.go("/" + Router.current().params.quizName + "/memberlist");
 			}
 		});
 	},
diff --git a/arsnova.click/client/layout/view_choose_nickname/scripts/onCreated.js b/arsnova.click/client/layout/view_choose_nickname/scripts/onCreated.js
index b8338ad32..5d521ad93 100644
--- a/arsnova.click/client/layout/view_choose_nickname/scripts/onCreated.js
+++ b/arsnova.click/client/layout/view_choose_nickname/scripts/onCreated.js
@@ -15,10 +15,9 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 
 Template.nick.onCreated(function () {
-	this.subscribe('MemberListCollection.members', Session.get("hashtag"));
-	this.subscribe("EventManagerCollection.join", Session.get("hashtag"));
+	this.subscribe('MemberListCollection.members', Router.current().params.quizName);
+	this.subscribe("EventManagerCollection.join", Router.current().params.quizName);
 });
diff --git a/arsnova.click/client/layout/view_hashtag_management/scripts/events.js b/arsnova.click/client/layout/view_hashtag_management/scripts/events.js
index 3ca92903f..9240c4204 100644
--- a/arsnova.click/client/layout/view_hashtag_management/scripts/events.js
+++ b/arsnova.click/client/layout/view_hashtag_management/scripts/events.js
@@ -15,7 +15,6 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {TAPi18n} from 'meteor/tap:i18n';
 import {EventManagerCollection} from '/lib/eventmanager/collection.js';
@@ -28,7 +27,6 @@ Template.hashtagView.events({
 	"input #hashtag-input-field": function (event) {
 		var inputHashtag = $(event.target).val();
 		let addNewHashtagItem = $("#addNewHashtag");
-		Session.set("hashtag", inputHashtag);
 		addNewHashtagItem.html(TAPi18n.__("view.hashtag_management.create_session") + '<span class="glyphicon glyphicon-plus glyph-right" aria-hidden="true"></span>');
 		if (inputHashtag.length === 0) {
 			addNewHashtagItem.attr("disabled", "disabled");
@@ -48,9 +46,39 @@ Template.hashtagView.events({
 				addNewHashtagItem.attr("disabled", "disabled");
 			}
 		}
+
+		if (lib.eventManagerHandle) {
+			lib.eventManagerHandle.stop();
+			$("#joinSession").attr("disabled", "disabled");
+		}
+		lib.setEventManagerHandle(Meteor.subscribe("EventManagerCollection.join", $("#hashtag-input-field").val(),function () {
+			if (!EventManagerCollection.findOne() || localData.containsHashtag($("#hashtag-input-field").val()) > -1) {
+				$("#joinSession").attr("disabled", "disabled");
+			}
+			EventManagerCollection.find().observeChanges({
+				changed: function (id, changedFields) {
+					if (!isNaN(changedFields.sessionStatus)) {
+						if (changedFields.sessionStatus === 2) {
+							$("#joinSession").removeAttr("disabled");
+						} else {
+							$("#joinSession").attr("disabled", "disabled");
+						}
+					}
+				},
+				added: function (id, doc) {
+					if (!isNaN(doc.sessionStatus)) {
+						if (doc.sessionStatus === 2) {
+							$("#joinSession").removeAttr("disabled");
+						} else {
+							$("#joinSession").attr("disabled", "disabled");
+						}
+					}
+				}
+			});
+		}));
 	},
 	"click #addNewHashtag": function () {
-		if (!Session.get("localStorageAvailable")) {
+		if (!localStorage.getItem(Router.current().params.quizName + "localStorageAvailable")) {
 			new ErrorSplashscreen({
 				autostart: true,
 				errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages.private_browsing")
@@ -66,11 +94,9 @@ Template.hashtagView.events({
 				var oldHashtagDoc = HashtagsCollection.findOne({hashtag: hashtag});
 				if (oldHashtagDoc) {
 					reenter = true;
-					Session.set("hashtag", hashtag);
-					Session.set("isOwner", true);
 					localData.reenterSession(hashtag);
 					Meteor.call('EventManagerCollection.add', localData.getPrivateKey(), hashtag, function () {
-						Router.go("/question");
+						Router.go("/" + hashtag + "/question");
 					});
 				}
 			}
@@ -110,11 +136,9 @@ Template.hashtagView.events({
 							]
 						});
 
-						Session.set("hashtag", hashtag);
-						Session.set("isOwner", true);
 						localData.addHashtag(hashtag);
 						Meteor.call('EventManagerCollection.add', localData.getPrivateKey(), hashtag, function () {
-							Router.go("/question");
+							Router.go("/" + hashtag + "/question");
 						});
 					}
 				});
@@ -125,8 +149,7 @@ Template.hashtagView.events({
 		var hashtag = $("#hashtag-input-field").val();
 
 		if (EventManagerCollection.findOne().sessionStatus === 2) {
-			Session.set("hashtag", hashtag);
-			Router.go("/nick");
+			Router.go("/" + hashtag + "/nick");
 		} else {
 			$("#joinSession").attr("disabled", "disabled");
 			new ErrorSplashscreen({
@@ -180,11 +203,9 @@ Template.hashtagManagement.events({
 	"click .js-reactivate-hashtag": function (event) {
 		var hashtag = $(event.currentTarget).parent().parent()[0].id;
 		localData.reenterSession(hashtag);
-		Session.set("isOwner", true);
-		Session.set("hashtag", hashtag);
 		Meteor.call('EventManagerCollection.add', localData.getPrivateKey(), hashtag, function () {
-			Session.set("overrideValidQuestionRedirect", true);
-			Router.go("/question");
+			localStorage.setItem(Router.current().params.quizName + "overrideValidQuestionRedirect", true);
+			Router.go("/" + hashtag + "/question");
 		});
 	},
 	"click .js-export": function (event) {
@@ -199,10 +220,10 @@ Template.hashtagManagement.events({
 			a.download = hashtag + "-" + timestring + ".json";
 			a.innerHTML = '';
 			event.target.appendChild(a);
-			if (Session.get("exportReady")) {
-				Session.set("exportReady", undefined);
+			if (localStorage.getItem(Router.current().params.quizName + "exportReady")) {
+				localStorage.setItem(Router.current().params.quizName + "exportReady", undefined);
 			} else {
-				Session.set("exportReady", true);
+				localStorage.setItem(Router.current().params.quizName + "exportReady", true);
 				a.click();
 			}
 		}
@@ -240,9 +261,7 @@ Template.hashtagManagement.events({
 									errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages.update_failed")
 								});
 							} else {
-								Session.set("hashtag", asJSON.hashtagDoc.hashtag);
-								Session.set("isOwner", true);
-								Router.go("/question");
+								Router.go("/" + asJSON.hashtagDoc.hashtag + "/question");
 							}
 						});
 					});
@@ -258,14 +277,12 @@ Template.hashtagManagement.events({
 Template.showHashtagsSplashscreen.events({
 	"click .js-my-hash": function (event) {
 		var hashtag = $(event.currentTarget).text();
-		Session.set("isOwner", true);
-		Session.set("hashtag", hashtag);
 		localData.reenterSession(hashtag);
 		lib.hashtagSplashscreen.destroy();
-		Router.go('/question');
+		Router.go("/" + hashtag + "/question");
 	},
 	"click #js-btn-showHashtagManagement": function () {
 		lib.hashtagSplashscreen.destroy();
-		Router.go('/hashtagmanagement');
+		Router.go("/hashtagmanagement");
 	}
 });
diff --git a/arsnova.click/client/layout/view_hashtag_management/scripts/lib.js b/arsnova.click/client/layout/view_hashtag_management/scripts/lib.js
index 922887e3b..7c14a2a24 100644
--- a/arsnova.click/client/layout/view_hashtag_management/scripts/lib.js
+++ b/arsnova.click/client/layout/view_hashtag_management/scripts/lib.js
@@ -16,7 +16,12 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 export let hashtagSplashscreen = null;
+export let eventManagerHandle = null;
 
 export function setHashtagSplashscreen(instance) {
 	hashtagSplashscreen = instance;
 }
+
+export function setEventManagerHandle(handle) {
+	eventManagerHandle = handle;
+}
diff --git a/arsnova.click/client/layout/view_hashtag_management/scripts/onCreated.js b/arsnova.click/client/layout/view_hashtag_management/scripts/onCreated.js
index 1ec4a7ce7..a975ea89a 100644
--- a/arsnova.click/client/layout/view_hashtag_management/scripts/onCreated.js
+++ b/arsnova.click/client/layout/view_hashtag_management/scripts/onCreated.js
@@ -15,54 +15,7 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
-import {EventManagerCollection} from '/lib/eventmanager/collection.js';
-import {HashtagsCollection} from '/lib/hashtags/collection.js';
-
-import * as localData from '/client/lib/local_storage.js';
-
-Template.hashtagView.onCreated(function () {
-	this.subscribe('HashtagsCollection.public', ()=> {
-		HashtagsCollection.find().observeChanges({
-			added: function (id, doc) {
-				if (doc.hashtag === $("#hashtag-input-field").val()) {
-					$("#addNewHashtag").attr("disabled", "disabled");
-				}
-			}
-		});
-	});
-	this.autorun(()=> {
-		this.subscribe("EventManagerCollection.join", Session.get("hashtag"), ()=> {
-			if (!EventManagerCollection.findOne({hashtag: Session.get("hashtag")}) || localData.containsHashtag(Session.get("hashtag")) > -1) {
-				$("#joinSession").attr("disabled", "disabled");
-				return;
-			}
-			EventManagerCollection.find().observeChanges({
-				/*
-				changed: function (id, changedFields) {
-					if (!isNaN(changedFields.sessionStatus)) {
-						if (changedFields.sessionStatus === 2) {
-							$("#joinSession").removeAttr("disabled");
-						} else {
-							$("#joinSession").attr("disabled", "disabled");
-						}
-					}
-				},
-				*/
-				added: function (id, doc) {
-					if (!isNaN(doc.sessionStatus)) {
-						if (doc.sessionStatus === 2) {
-							$("#joinSession").removeAttr("disabled");
-						} else {
-							$("#joinSession").attr("disabled", "disabled");
-						}
-					}
-				}
-			});
-		});
-	});
-});
 
 Template.hashtagManagement.onCreated(function () {
 	this.subscribe('HashtagsCollection.public');
diff --git a/arsnova.click/client/layout/view_hashtag_management/scripts/onDestroyed.js b/arsnova.click/client/layout/view_hashtag_management/scripts/onDestroyed.js
new file mode 100644
index 000000000..1836cc0b0
--- /dev/null
+++ b/arsnova.click/client/layout/view_hashtag_management/scripts/onDestroyed.js
@@ -0,0 +1,9 @@
+import {Template} from 'meteor/templating';
+import {eventManagerHandle} from './lib.js';
+
+Template.hashtagView.onDestroyed(function () {
+	if (eventManagerHandle) {
+		eventManagerHandle.stop();
+		$("#joinSession").attr("disabled", "disabled");
+	}
+});
diff --git a/arsnova.click/client/layout/view_leaderboard/scripts/events.js b/arsnova.click/client/layout/view_leaderboard/scripts/events.js
index e4a22e29d..5b61b83f9 100644
--- a/arsnova.click/client/layout/view_leaderboard/scripts/events.js
+++ b/arsnova.click/client/layout/view_leaderboard/scripts/events.js
@@ -20,13 +20,13 @@ import {Template} from 'meteor/templating';
 
 Template.leaderBoard.events({
 	'click #showMore': ()=> {
-		Session.set('responsesCountOverride', true);
+		Session.set("responsesCountOverride", true);
 	},
 	'click #showLess': ()=> {
-		Session.set('responsesCountOverride', false);
+		Session.set("responsesCountOverride", false);
 	},
 	'click #js-btn-backToResults': ()=> {
 		Session.set("showGlobalRanking", false);
-		Router.go('/results');
+		Router.go("/" + Router.current().params.quizName + "/results");
 	}
 });
diff --git a/arsnova.click/client/layout/view_leaderboard/scripts/helpers.js b/arsnova.click/client/layout/view_leaderboard/scripts/helpers.js
index ce787d929..4ece48d04 100644
--- a/arsnova.click/client/layout/view_leaderboard/scripts/helpers.js
+++ b/arsnova.click/client/layout/view_leaderboard/scripts/helpers.js
@@ -23,7 +23,7 @@ import {getAllNicksWhichAreAlwaysRight} from './lib.js';
 
 Template.leaderBoard.helpers({
 	hashtag: ()=> {
-		return Session.get("hashtag");
+		return Router.current().params.quizName;
 	},
 	getNormalizedIndex: (index)=> {
 		return index + 1;
@@ -32,10 +32,10 @@ Template.leaderBoard.helpers({
 		return !isNaN(index);
 	},
 	isOwnNick: (nick) => {
-		return nick === Session.get("nick");
+		return nick === localStorage.getItem(Router.current().params.quizName + "nick");
 	},
 	getTitleText: ()=> {
-		if (typeof Session.get("showLeaderBoardId") !== "undefined") {
+		if (Session.get("showLeaderBoardId") !== "undefined") {
 			return TAPi18n.__("view.leaderboard.title.single_question", {questionId: (Session.get("showLeaderBoardId") + 1)});
 		} else {
 			return TAPi18n.__("view.leaderboard.title.all_questions");
@@ -53,10 +53,10 @@ Template.leaderBoard.helpers({
 		return Session.get("allMembersCount") - Session.get("maxResponseButtons");
 	},
 	hasOverridenDefaultButtonCount: ()=> {
-		return Session.get('responsesCountOverride');
+		return Session.get("responsesCountOverride");
 	},
 	hasTooMuchButtons: ()=> {
-		return Session.get('responsesCountOverride') || (Session.get("allMembersCount") - Session.get("maxResponseButtons") > 0);
+		return Session.get("responsesCountOverride") || (Session.get("allMembersCount") - Session.get("maxResponseButtons") > 0);
 	},
 	isGlobalRanking: function () {
 		return Session.get("showGlobalRanking");
diff --git a/arsnova.click/client/layout/view_leaderboard/scripts/lib.js b/arsnova.click/client/layout/view_leaderboard/scripts/lib.js
index 2b255c244..8948c96d2 100644
--- a/arsnova.click/client/layout/view_leaderboard/scripts/lib.js
+++ b/arsnova.click/client/layout/view_leaderboard/scripts/lib.js
@@ -22,7 +22,7 @@ import {MemberListCollection} from '/lib/member_list/collection.js';
 import {ResponsesCollection} from '/lib/responses/collection.js';
 
 export function setMaxResponseButtons(value) {
-	Session.set("maxResponseButtons", value);
+	Session.get("maxResponseButtons", value);
 }
 
 export function calculateButtonCount(allMembersCount) {
diff --git a/arsnova.click/client/layout/view_leaderboard/scripts/onCreated.js b/arsnova.click/client/layout/view_leaderboard/scripts/onCreated.js
index ecaa18cfd..1214a463f 100644
--- a/arsnova.click/client/layout/view_leaderboard/scripts/onCreated.js
+++ b/arsnova.click/client/layout/view_leaderboard/scripts/onCreated.js
@@ -19,11 +19,5 @@ import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 
 Template.leaderBoard.onCreated(function () {
-	this.subscribe('ResponsesCollection.session', Session.get("hashtag"));
-	this.subscribe('AnswerOptionCollection.options', Session.get("hashtag"));
-	this.subscribe('MemberListCollection.members', Session.get("hashtag"));
-	this.subscribe('QuestionGroupCollection.questionList', Session.get("hashtag"));
-	this.subscribe("EventManagerCollection.join", Session.get("hashtag"));
-
-	Session.set('show_all_leaderboard', false);
+	Session.set("show_all_leaderboard", false);
 });
diff --git a/arsnova.click/client/layout/view_leaderboard/scripts/onDestroyed.js b/arsnova.click/client/layout/view_leaderboard/scripts/onDestroyed.js
index fdbc9a181..e67057c03 100644
--- a/arsnova.click/client/layout/view_leaderboard/scripts/onDestroyed.js
+++ b/arsnova.click/client/layout/view_leaderboard/scripts/onDestroyed.js
@@ -1,9 +1,26 @@
-import { Session } from 'meteor/session';
-import { Template } from 'meteor/templating';
+/*
+ * This file is part of ARSnova Click.
+ * Copyright (C) 2016 The ARSnova Team
+ *
+ * ARSnova Click is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Click is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
+
+import {Session} from 'meteor/session';
+import {Template} from 'meteor/templating';
 
 Template.leaderBoard.onDestroyed(function () {
 	Session.set("showLeaderBoardId", undefined);
-	Session.set('show_all_leaderboard', undefined);
+	Session.set("show_all_leaderboard", undefined);
 	Session.set("responsesCountOverride", undefined);
 	Session.set("allMembersCount", undefined);
 });
diff --git a/arsnova.click/client/layout/view_leaderboard/scripts/onRendered.js b/arsnova.click/client/layout/view_leaderboard/scripts/onRendered.js
index a2818c66e..c658fa8ae 100644
--- a/arsnova.click/client/layout/view_leaderboard/scripts/onRendered.js
+++ b/arsnova.click/client/layout/view_leaderboard/scripts/onRendered.js
@@ -1,13 +1,30 @@
-import { Session } from 'meteor/session';
-import { Template } from 'meteor/templating';
-import { calculateButtonCount } from './lib.js';
+/*
+ * This file is part of ARSnova Click.
+ * Copyright (C) 2016 The ARSnova Team
+ *
+ * ARSnova Click is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Click is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
+
+import {Session} from 'meteor/session';
+import {Template} from 'meteor/templating';
+import {calculateButtonCount} from './lib.js';
 
 Template.leaderBoard.onRendered(function () {
 	calculateButtonCount();
 
 	$(window).resize(function () {
-		if (Session.get('responsesCountOverride') && (Session.get("allMembersCount") - Session.get("maxResponseButtons") === 0)) {
-			Session.set('responsesCountOverride', false);
+		if (Session.get("responsesCountOverride") && (Session.get("allMembersCount") - Session.get("maxResponseButtons") === 0)) {
+			Session.get("responsesCountOverride", false);
 		}
 		calculateButtonCount();
 	});
diff --git a/arsnova.click/client/layout/view_live_results/scripts/events.js b/arsnova.click/client/layout/view_live_results/scripts/events.js
index cabedc4a5..8880351b2 100644
--- a/arsnova.click/client/layout/view_live_results/scripts/events.js
+++ b/arsnova.click/client/layout/view_live_results/scripts/events.js
@@ -74,12 +74,12 @@ Template.liveResults.events({
 		event.stopPropagation();
 		var targetId = parseInt($(event.currentTarget).attr("id").replace("js-btn-showLeaderBoard_", ""));
 		Session.set("showLeaderBoardId", targetId);
-		Router.go("/statistics");
+		Router.go("/" + Router.current().params.quizName + "/statistics");
 	},
 	"click #js-btn-export": function (event) {
 		event.stopPropagation();
 		Meteor.call('HashtagsCollection.export', {
-			hashtag: Session.get("hashtag"),
+			hashtag: Router.current().params.quizName,
 			privateKey: localData.getPrivateKey()
 		}, (err, res) => {
 			if (err) {
@@ -93,7 +93,7 @@ Template.liveResults.events({
 				var time = new Date();
 				var timestring = time.getDate() + "_" + (time.getMonth() + 1) + "_" + time.getFullYear();
 				a.href = 'data:' + exportData;
-				a.download = Session.get("hashtag") + "-" + timestring + ".json";
+				a.download = Router.current().params.quizName + "-" + timestring + ".json";
 				a.innerHTML = '';
 				event.target.appendChild(a);
 				if (Session.get("exportReady")) {
@@ -108,9 +108,9 @@ Template.liveResults.events({
 	'click #backButton': (event)=> {
 		event.stopPropagation();
 		$('.sound-button').show();
-		Meteor.call('ResponsesCollection.clearAll', localData.getPrivateKey(), Session.get("hashtag"));
-		Meteor.call("MemberListCollection.clearReadConfirmed", localData.getPrivateKey(), Session.get("hashtag"));
-		Meteor.call("EventManagerCollection.setSessionStatus", localData.getPrivateKey(), Session.get("hashtag"), 2);
+		Meteor.call('ResponsesCollection.clearAll', localData.getPrivateKey(), Router.current().params.quizName);
+		Meteor.call("MemberListCollection.clearReadConfirmed", localData.getPrivateKey(), Router.current().params.quizName);
+		Meteor.call("EventManagerCollection.setSessionStatus", localData.getPrivateKey(), Router.current().params.quizName, 2);
 	},
 	'click #startNextQuestion': (event)=> {
 		event.stopPropagation();
@@ -122,7 +122,7 @@ Template.liveResults.events({
 
 		Meteor.call('Question.startTimer', {
 			privateKey: localData.getPrivateKey(),
-			hashtag: Session.get("hashtag"),
+			hashtag: Router.current().params.quizName,
 			questionIndex: EventManagerCollection.findOne().questionIndex + 1
 		}, (err) => {
 			if (err) {
@@ -140,11 +140,11 @@ Template.liveResults.events({
 		event.stopPropagation();
 		Session.set("showLeaderBoardId", undefined);
 		Session.set("showGlobalRanking", true);
-		Router.go("/statistics");
+		Router.go("/" + Router.current().params.quizName + "/statistics");
 	},
 	'click #showNextQuestionDialog': (event)=> {
 		event.stopPropagation();
-		Meteor.call("EventManagerCollection.showReadConfirmedForIndex", localData.getPrivateKey(), Session.get("hashtag"), EventManagerCollection.findOne().questionIndex + 1);
+		Meteor.call("EventManagerCollection.showReadConfirmedForIndex", localData.getPrivateKey(), Router.current().params.quizName, EventManagerCollection.findOne().questionIndex + 1);
 	},
 	"click .btn-more-learners": function () {
 		Session.set("LearnerCount", MemberListCollection.find().count());
diff --git a/arsnova.click/client/layout/view_live_results/scripts/helpers.js b/arsnova.click/client/layout/view_live_results/scripts/helpers.js
index cc210819a..aa87bd8e4 100644
--- a/arsnova.click/client/layout/view_live_results/scripts/helpers.js
+++ b/arsnova.click/client/layout/view_live_results/scripts/helpers.js
@@ -15,7 +15,6 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {TAPi18n} from 'meteor/tap:i18n';
 import {EventManagerCollection} from '/lib/eventmanager/collection.js';
@@ -23,6 +22,7 @@ import {AnswerOptionCollection} from '/lib/answeroptions/collection.js';
 import {MemberListCollection} from '/lib/member_list/collection.js';
 import {ResponsesCollection} from '/lib/responses/collection.js';
 import {QuestionGroupCollection} from '/lib/questions/collection.js';
+import * as localData from '/client/lib/local_storage.js';
 import {countdown, getPercentRead, getCurrentRead, hslColPerc, checkIfIsCorrect} from './lib.js';
 
 Template.liveResults.helpers({
@@ -30,7 +30,7 @@ Template.liveResults.helpers({
 		return Session.get("sessionClosed") ? "view.liveResults.game_over" : "view.liveResults.countdown";
 	},
 	isOwner: function () {
-		return Session.get("isOwner");
+		return localData.containsHashtag(Router.current().params.quizName);
 	},
 	getCountdown: function () {
 		if (Session.get("countdownInitialized")) {
@@ -40,7 +40,7 @@ Template.liveResults.helpers({
 		return 0;
 	},
 	isCountdownZero: function (index) {
-		if (Session.get("isOwner")) {
+		if (localData.containsHashtag(Router.current().params.quizName)) {
 			if (Session.get("sessionClosed") || !Session.get("countdownInitialized") || EventManagerCollection.findOne().questionIndex !== index) {
 				return true;
 			} else {
@@ -237,11 +237,11 @@ Template.liveResults.helpers({
 	readingConfirmationListForQuestion: (index)=> {
 		let result = [];
 		let sortParamObj = Session.get('LearnerCountOverride') ? {lowerCaseNick: 1} : {insertDate: -1};
-		let ownNick = MemberListCollection.findOne({nick: Session.get("nick")}, {limit: 1});
+		let ownNick = MemberListCollection.findOne({nick: localStorage.getItem(Router.current().params.quizName + "nick")}, {limit: 1});
 		if (ownNick && ownNick.readConfirmed[index]) {
 			result.push(ownNick);
 		}
-		MemberListCollection.find({nick: {$ne: Session.get("nick")}}, {
+		MemberListCollection.find({nick: {$ne: localStorage.getItem(Router.current().params.quizName + "nick")}}, {
 			sort: sortParamObj
 		}).forEach(function (doc) {
 			if (result.length < Session.get("LearnerCount") && doc.readConfirmed[index]) {
@@ -251,7 +251,7 @@ Template.liveResults.helpers({
 		return result;
 	},
 	isOwnNick: (nick)=> {
-		return nick === Session.get("nick");
+		return nick === localStorage.getItem(Router.current().params.quizName + "nick");
 	},
 	showMoreButton: function (index) {
 		var result = [];
@@ -276,6 +276,6 @@ Template.liveResults.helpers({
 
 Template.readingConfirmedLearner.helpers({
 	isOwnNick: function (nickname) {
-		return nickname === Session.get("nick");
+		return nickname === localStorage.getItem(Router.current().params.quizName + "nick");
 	}
 });
diff --git a/arsnova.click/client/layout/view_live_results/scripts/lib.js b/arsnova.click/client/layout/view_live_results/scripts/lib.js
index 9f5caa755..ff4d7ac0b 100644
--- a/arsnova.click/client/layout/view_live_results/scripts/lib.js
+++ b/arsnova.click/client/layout/view_live_results/scripts/lib.js
@@ -66,7 +66,7 @@ export function checkIfIsCorrect(isCorrect) {
 }
 
 export function startCountdown(index) {
-	Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Session.get("hashtag"), index);
+	Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Router.current().params.quizName, index);
 	var questionDoc = QuestionGroupCollection.findOne().questionList[index];
 	Session.set("sessionCountDown", questionDoc.timer);
 	$("#countdowndiv").appendTo($("body"));
@@ -137,7 +137,7 @@ export function startCountdown(index) {
 			if (Session.get("isOwner") && AnswerOptionCollection.find({isCorrect: 1}).count() > 0) {
 				routeToLeaderboardTimer = setTimeout(() => {
 					Session.set("showGlobalRanking", true);
-					Router.go("/statistics");
+					Router.go("/" + Router.current().params.quizName + "/statistics");
 				}, 7000);
 			}
 		}
diff --git a/arsnova.click/client/layout/view_live_results/scripts/onRendered.js b/arsnova.click/client/layout/view_live_results/scripts/onRendered.js
index ba1d24d75..b1bca3a23 100644
--- a/arsnova.click/client/layout/view_live_results/scripts/onRendered.js
+++ b/arsnova.click/client/layout/view_live_results/scripts/onRendered.js
@@ -23,8 +23,8 @@ import * as localData from '/client/lib/local_storage.js';
 import {calculateButtonCount} from './lib.js';
 
 Template.liveResults.onRendered(()=> {
-	if (Session.get("isOwner") && EventManagerCollection.findOne() && EventManagerCollection.findOne().readingConfirmationIndex === -1) {
-		Meteor.call("EventManagerCollection.showReadConfirmedForIndex", localData.getPrivateKey(), Session.get("hashtag"), 0);
+	if (localData.containsHashtag(Router.current().params.quizName) && EventManagerCollection.findOne() && EventManagerCollection.findOne().readingConfirmationIndex === -1) {
+		Meteor.call("EventManagerCollection.showReadConfirmedForIndex", localData.getPrivateKey(), Router.current().params.quizName, 0);
 	}
 	Session.set("LearnerCountOverride", false);
 	calculateButtonCount();
diff --git a/arsnova.click/client/layout/view_lobby/scripts/events.js b/arsnova.click/client/layout/view_lobby/scripts/events.js
index 2e87bc8b1..d0c9db5b8 100644
--- a/arsnova.click/client/layout/view_lobby/scripts/events.js
+++ b/arsnova.click/client/layout/view_lobby/scripts/events.js
@@ -26,16 +26,16 @@ import {calculateButtonCount} from './lib.js';
 
 Template.memberlist.events({
 	"click .btn-more-learners": function () {
-		Session.set("LearnerCount", MemberListCollection.find().count());
-		Session.set("LearnerCountOverride", true);
+		Session.set("learnerCount", MemberListCollection.find().count());
+		Session.set("learnerCountOverride", true);
 	},
 	'click .btn-less-learners': function () {
-		Session.set("LearnerCountOverride", false);
+		Session.set("learnerCountOverride", false);
 		calculateButtonCount();
 	},
 	'click .btn-learner': function (event) {
 		event.preventDefault();
-		if (!Session.get("isOwner")) {
+		if (!localData.containsHashtag(Router.current().params.quizName)) {
 			return;
 		}
 		new Splashscreen({
@@ -45,7 +45,7 @@ Template.memberlist.events({
 			onRendered: function (instance) {
 				instance.templateSelector.find('#nickName').text($(event.currentTarget).text().replace(/(?:\r\n|\r| |\n)/g, ''));
 				instance.templateSelector.find('#kickMemberButton').on('click', function () {
-					Meteor.call('MemberListCollection.removeLearner', localData.getPrivateKey(), Session.get("hashtag"), $(event.currentTarget).attr("id"), function (err) {
+					Meteor.call('MemberListCollection.removeLearner', localData.getPrivateKey(), Router.current().params.quizName, $(event.currentTarget).attr("id"), function (err) {
 						if (err) {
 							new ErrorSplashscreen({
 								autostart: true,
@@ -59,8 +59,8 @@ Template.memberlist.events({
 	},
 	'click #startPolling': function () {
 		$('.sound-button').hide();
-		Session.set("sessionClosed", false);
-		Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Session.get("hashtag"), -1);
-		Meteor.call('EventManagerCollection.setSessionStatus', localData.getPrivateKey(), Session.get("hashtag"), 3);
+		localStorage.setItem(Router.current().params.quizName + "sessionClosed", false);
+		Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Router.current().params.quizName, -1);
+		Meteor.call('EventManagerCollection.setSessionStatus', localData.getPrivateKey(), Router.current().params.quizName, 3);
 	}
 });
diff --git a/arsnova.click/client/layout/view_lobby/scripts/helpers.js b/arsnova.click/client/layout/view_lobby/scripts/helpers.js
index 7cec5ecc2..8de661e49 100644
--- a/arsnova.click/client/layout/view_lobby/scripts/helpers.js
+++ b/arsnova.click/client/layout/view_lobby/scripts/helpers.js
@@ -17,32 +17,33 @@
 
 import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
+import * as localData from '/client/lib/local_storage.js';
 import {MemberListCollection} from '/lib/member_list/collection.js';
 
 Template.memberlist.helpers({
 	hashtag: function () {
-		return Session.get("hashtag");
+		return Router.current().params.quizName;
 	},
 	isOwner: function () {
-		return Session.get("isOwner");
+		return localData.containsHashtag(Router.current().params.quizName);
 	},
 	learners: function () {
-		var sortParamObj = Session.get('LearnerCountOverride') ? {lowerCaseNick: 1} : {insertDate: -1};
+		var sortParamObj = Session.get("learnerCountOverride") ? {lowerCaseNick: 1} : {insertDate: -1};
 		return [
-			MemberListCollection.find({nick: Session.get("nick")}, {
+			MemberListCollection.find({nick: localStorage.getItem(Router.current().params.quizName + "nick")}, {
 				limit: 1
 			}),
-			MemberListCollection.find({nick: {$ne: Session.get("nick")}}, {
-				limit: (Session.get("LearnerCount") - 1),
+			MemberListCollection.find({nick: {$ne: localStorage.getItem(Router.current().params.quizName + "nick")}}, {
+				limit: (Session.get("learnerCount") - 1),
 				sort: sortParamObj
 			})
 		];
 	},
 	showMoreButton: function () {
-		return ((MemberListCollection.find().count() - Session.get("LearnerCount")) > 1);
+		return ((MemberListCollection.find().count() - Session.get("learnerCount")) > 1);
 	},
 	invisibleLearnerCount: function () {
-		return MemberListCollection.find().count() - Session.get("LearnerCount");
+		return MemberListCollection.find().count() - Session.get("learnerCount");
 	},
 	memberlistCount: function () {
 		return MemberListCollection.find().count();
@@ -51,6 +52,6 @@ Template.memberlist.helpers({
 
 Template.learner.helpers({
 	isOwnNick: function (nickname) {
-		return nickname === Session.get("nick");
+		return nickname === localStorage.getItem(Router.current().params.quizName + "nick");
 	}
 });
diff --git a/arsnova.click/client/layout/view_lobby/scripts/lib.js b/arsnova.click/client/layout/view_lobby/scripts/lib.js
index 0ab228aa8..159852528 100644
--- a/arsnova.click/client/layout/view_lobby/scripts/lib.js
+++ b/arsnova.click/client/layout/view_lobby/scripts/lib.js
@@ -25,7 +25,7 @@ export function calculateButtonCount() {
 	 This session variable determines if the user has clicked on the show-more-button. The button count must not
 	 be calculated then. It is set in the event handler of the button and is reset if the user reenters the page
 	 */
-	if (Session.get("LearnerCountOverride")) {
+	if (Session.get("learnerCountOverride")) {
 		return;
 	}
 
@@ -72,7 +72,7 @@ export function calculateButtonCount() {
 	 This session variable holds the amount of shown buttons and is used in the scripts function
 	 Template.memberlist.scripts.learners which gets the attendees from the mongo db
 	 */
-	Session.set("LearnerCount", queryLimiter);
+	Session.set("learnerCount", queryLimiter);
 }
 
 export function setMemberlistObserver(options) {
diff --git a/arsnova.click/client/layout/view_lobby/scripts/onCreated.js b/arsnova.click/client/layout/view_lobby/scripts/onCreated.js
index c2433bda5..e9d5a1cdd 100644
--- a/arsnova.click/client/layout/view_lobby/scripts/onCreated.js
+++ b/arsnova.click/client/layout/view_lobby/scripts/onCreated.js
@@ -25,35 +25,30 @@ import {calculateButtonCount, setMemberlistObserver} from './lib.js';
 Template.memberlist.onCreated(function () {
 	var oldStartTimeValues = {};
 
-	this.subscribe('EventManagerCollection.join', Session.get("hashtag"));
-	this.subscribe('MemberListCollection.members', Session.get("hashtag"), function () {
-		$(window).resize(function () {
-			var finalHeight = $(window).height() - $(".navbar-fixed-top").outerHeight() - $(".navbar-fixed-bottom").outerHeight() - $(".fixed-bottom").outerHeight();
-			$(".container").css("height", finalHeight + "px");
-			Session.set("LearnerCountOverride", false);
-			calculateButtonCount();
-		});
-
-		setMemberlistObserver({
-			added: function () {
-				calculateButtonCount();
-			}
-		});
+	$(window).resize(function () {
+		var finalHeight = $(window).height() - $(".navbar-fixed-top").outerHeight() - $(".navbar-fixed-bottom").outerHeight() - $(".fixed-bottom").outerHeight();
+		$(".container").css("height", finalHeight + "px");
+		Session.set("learnerCountOverride", false);
+		calculateButtonCount();
 	});
-	this.subscribe('QuestionGroupCollection.memberlist', Session.get("hashtag"), function () {
-		var doc = QuestionGroupCollection.findOne();
-		for (var i = 0; i < doc.questionList.length; i++) {
-			oldStartTimeValues[i] = doc.questionList[i].startTime;
-		}
-	});
-	this.subscribe('ResponsesCollection.session', Session.get("hashtag"), function () {
-		if (Session.get("isOwner")) {
-			Meteor.call('ResponsesCollection.clearAll', localData.getPrivateKey(), Session.get("hashtag"));
+
+	setMemberlistObserver({
+		added: function () {
+			calculateButtonCount();
 		}
 	});
 
-	if (Session.get("isOwner")) {
-		Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Session.get("hashtag"), 0);
-		Meteor.call("EventManagerCollection.showReadConfirmedForIndex", localData.getPrivateKey(), Session.get("hashtag"), -1);
+	var doc = QuestionGroupCollection.findOne();
+	for (var i = 0; i < doc.questionList.length; i++) {
+		oldStartTimeValues[i] = doc.questionList[i].startTime;
+	}
+
+	if (localData.containsHashtag(Router.current().params.quizName)) {
+		Meteor.call('ResponsesCollection.clearAll', localData.getPrivateKey(), Router.current().params.quizName);
+	}
+
+	if (localData.containsHashtag(Router.current().params.quizName)) {
+		Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Router.current().params.quizName, 0);
+		Meteor.call("EventManagerCollection.showReadConfirmedForIndex", localData.getPrivateKey(), Router.current().params.quizName, -1);
 	}
 });
diff --git a/arsnova.click/client/layout/view_lobby/scripts/onRendered.js b/arsnova.click/client/layout/view_lobby/scripts/onRendered.js
index 39bde0ab5..1f5489792 100644
--- a/arsnova.click/client/layout/view_lobby/scripts/onRendered.js
+++ b/arsnova.click/client/layout/view_lobby/scripts/onRendered.js
@@ -22,11 +22,11 @@ import {calculateButtonCount} from './lib.js';
 Template.memberlist.onRendered(function () {
 	var finalHeight = $(window).height() - $(".navbar-fixed-top").outerHeight() - $(".navbar-fixed-bottom").outerHeight() - $(".fixed-bottom").outerHeight();
 	$(".container").css("height", finalHeight + "px");
-	Session.set("LearnerCountOverride", false);
+	Session.set("learnerCountOverride", false);
 	calculateButtonCount();
 
 	var calculateFontSize = function () {
-		var hashtagLength = Session.get("hashtag").length;
+		var hashtagLength = Router.current().params.quizName.length;
 		//take the hastag in the middle of the logo
 		var titelMarginTop = $(".arsnova-logo").height();
 
diff --git a/arsnova.click/client/layout/view_questions/scripts/events.js b/arsnova.click/client/layout/view_questions/scripts/events.js
index 2125e996f..dc8cf76d6 100644
--- a/arsnova.click/client/layout/view_questions/scripts/events.js
+++ b/arsnova.click/client/layout/view_questions/scripts/events.js
@@ -35,7 +35,7 @@ Template.createQuestionView.events({
 			return;
 		}
 		lib.addQuestion(EventManagerCollection.findOne().questionIndex);
-		Router.go("/answeroptions");
+		Router.go("/" + Router.current().params.quizName + "/answeroptions");
 	},
 	"click #backButton": function () {
 		Router.go("/");
diff --git a/arsnova.click/client/layout/view_questions/scripts/lib.js b/arsnova.click/client/layout/view_questions/scripts/lib.js
index ab44327f5..832ac7cb8 100644
--- a/arsnova.click/client/layout/view_questions/scripts/lib.js
+++ b/arsnova.click/client/layout/view_questions/scripts/lib.js
@@ -16,7 +16,6 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import {TAPi18n} from 'meteor/tap:i18n';
 import {QuestionGroupCollection} from '/lib/questions/collection.js';
 import {EventManagerCollection} from '/lib/eventmanager/collection.js';
@@ -64,7 +63,7 @@ export function addQuestion(index) {
 	var questionText = $('#questionText').val();
 	Meteor.call("QuestionGroupCollection.addQuestion", {
 		privateKey: localData.getPrivateKey(),
-		hashtag: Session.get("hashtag"),
+		hashtag: Router.current().params.quizName,
 		questionIndex: index,
 		questionText: questionText
 	}, (err) => {
@@ -74,13 +73,13 @@ export function addQuestion(index) {
 				errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages." + err.reason)
 			});
 		} else {
-			localData.addQuestion(Session.get("hashtag"), index, questionText);
+			localData.addQuestion(Router.current().params.quizName, index, questionText);
 		}
 	});
 }
 
 export function calculateWindow() {
-	var hashtagLength = Session.get("hashtag").length;
+	var hashtagLength = Router.current().params.quizName.length;
 	var headerTitel = $(".header-titel");
 	var fontSize = "";
 	var marginTopModifier = 0;
diff --git a/arsnova.click/client/layout/view_questions/scripts/onCreated.js b/arsnova.click/client/layout/view_questions/scripts/onCreated.js
index 0b0ec9f16..f45effbca 100644
--- a/arsnova.click/client/layout/view_questions/scripts/onCreated.js
+++ b/arsnova.click/client/layout/view_questions/scripts/onCreated.js
@@ -15,11 +15,7 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
-import * as localData from '/client/lib/local_storage.js';
 
 Template.createQuestionView.onCreated(function () {
-	this.subscribe('QuestionGroupCollection.authorizeAsOwner', localData.getPrivateKey(), Session.get("hashtag"));
-	this.subscribe("EventManagerCollection.join", Session.get("hashtag"));
 });
diff --git a/arsnova.click/client/layout/view_questions/scripts/onRendered.js b/arsnova.click/client/layout/view_questions/scripts/onRendered.js
index 70be6a6d1..8d3dfacfd 100644
--- a/arsnova.click/client/layout/view_questions/scripts/onRendered.js
+++ b/arsnova.click/client/layout/view_questions/scripts/onRendered.js
@@ -15,7 +15,6 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {Tracker} from 'meteor/tracker';
 import {EventManagerCollection} from '/lib/eventmanager/collection.js';
@@ -23,7 +22,7 @@ import {QuestionGroupCollection} from '/lib/questions/collection.js';
 import * as lib from './lib.js';
 
 Template.createQuestionView.onRendered(function () {
-	Session.set("markdownAlreadyChecked", false);
+	localStorage.setItem(Router.current().params.quizName + "markdownAlreadyChecked", false);
 	lib.calculateWindow();
 	$(window).resize(lib.calculateWindow());
 
@@ -31,9 +30,9 @@ Template.createQuestionView.onRendered(function () {
 	lib.subscriptionHandler = Tracker.autorun(()=> {
 		if (this.subscriptionsReady() && EventManagerCollection.findOne()) {
 			index = EventManagerCollection.findOne().questionIndex;
-			if (!Session.get("markdownAlreadyChecked")) {
+			if (!localStorage.getItem(Router.current().params.quizName + "markdownAlreadyChecked")) {
 				lib.checkForMarkdown();
-				Session.set("markdownAlreadyChecked", true);
+				localStorage.setItem(Router.current().params.quizName + "markdownAlreadyChecked", true);
 			}
 		}
 	});
diff --git a/arsnova.click/client/layout/view_timer/scripts/events.js b/arsnova.click/client/layout/view_timer/scripts/events.js
index 21e7ea202..b42c116d0 100644
--- a/arsnova.click/client/layout/view_timer/scripts/events.js
+++ b/arsnova.click/client/layout/view_timer/scripts/events.js
@@ -16,7 +16,6 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {TAPi18n} from 'meteor/tap:i18n';
 import {EventManagerCollection} from '/lib/eventmanager/collection.js';
@@ -49,29 +48,29 @@ Template.createTimerView.events({
 						}).count() > 0) {
 						Meteor.call('AnswerOptionCollection.deleteOption', {
 							privateKey: localData.getPrivateKey(),
-							hashtag: Session.get("hashtag"),
+							hashtag: Router.current().params.quizName,
 							questionIndex: cursor.questionIndex,
 							answerOptionNumber: cursor.answerOptionNumber
 						});
-						localData.deleteAnswerOption(Session.get("hashtag"), cursor.questionIndex, cursor.answerOptionNumber);
+						localData.deleteAnswerOption(Router.current().params.quizName, cursor.questionIndex, cursor.answerOptionNumber);
 					} else {
 						if (cursor.answerOptionNumber > 0) {
 							Meteor.call('AnswerOptionCollection.deleteOption', {
 								privateKey: localData.getPrivateKey(),
-								hashtag: Session.get("hashtag"),
+								hashtag: Router.current().params.quizName,
 								questionIndex: cursor.questionIndex,
 								answerOptionNumber: cursor.answerOptionNumber
 							});
-							localData.deleteAnswerOption(Session.get("hashtag"), cursor.questionIndex, cursor.answerOptionNumber);
+							localData.deleteAnswerOption(Router.current().params.quizName, cursor.questionIndex, cursor.answerOptionNumber);
 						}
 					}
 				});
-				Meteor.call("MemberListCollection.removeFromSession", localData.getPrivateKey(), Session.get("hashtag"));
-				Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Session.get("hashtag"), 0);
-				Meteor.call("EventManagerCollection.setSessionStatus", localData.getPrivateKey(), Session.get("hashtag"), 2);
-				Router.go("/memberlist");
+				Meteor.call("MemberListCollection.removeFromSession", localData.getPrivateKey(), Router.current().params.quizName);
+				Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Router.current().params.quizName, 0);
+				Meteor.call("EventManagerCollection.setSessionStatus", localData.getPrivateKey(), Router.current().params.quizName, 2);
+				Router.go("/" + Router.current().params.quizName + "/memberlist");
 			} else {
-				Router.go("/answeroptions");
+				Router.go("/" + Router.current().params.quizName + "/answeroptions");
 			}
 		}
 	}
diff --git a/arsnova.click/client/layout/view_timer/scripts/lib.js b/arsnova.click/client/layout/view_timer/scripts/lib.js
index d0d978e68..ffb28987e 100644
--- a/arsnova.click/client/layout/view_timer/scripts/lib.js
+++ b/arsnova.click/client/layout/view_timer/scripts/lib.js
@@ -22,7 +22,6 @@ import {QuestionGroupCollection} from '/lib/questions/collection.js';
 import * as localData from '/client/lib/local_storage.js';
 
 export let validationTrackerHandle = null;
-export let subscriptionHandler = null;
 
 export function setTimer(index) {
 	var hasError = false;
@@ -31,14 +30,14 @@ export function setTimer(index) {
 	if (!isNaN(timer)) {
 		Meteor.call("Question.setTimer", {
 			privateKey: localData.getPrivateKey(),
-			hashtag: Session.get("hashtag"),
+			hashtag: Router.current().params.quizName,
 			questionIndex: index,
 			timer: timer
 		}, (err) => {
 			if (err) {
 				hasError = err;
 			} else {
-				localData.addTimer(Session.get("hashtag"), index, timer);
+				localData.addTimer(Router.current().params.quizName, index, timer);
 			}
 		});
 	} else {
@@ -50,27 +49,27 @@ export function setTimer(index) {
 }
 
 export function createSlider(index) {
-	if (Session.get("slider") === undefined) {
+	if (typeof QuestionGroupCollection.findOne() === "undefined") {
 		setTimeout(createSlider, 50);
 		return;
 	}
-	if (Session.get("slider") === 0) {
+	if (QuestionGroupCollection.findOne().questionList[index].timer === 0) {
 		Session.set("slider", AnswerOptionCollection.find({questionIndex: index}).count() * 10);
 	}
 	$("#slider").noUiSlider({
-		start: Session.get("slider"),
+		start: QuestionGroupCollection.findOne().questionList[index].timer / 1000,
 		range: {
 			'min': 6,
 			'max': 260
 		}
 	}).on('slide', function (ev, val) {
-		Session.set('slider', Math.round(val));
+		Session.set("slider", Math.round(val));
 	}).on('change', function (ev, val) {
-		Session.set('slider', Math.round(val));
+		Session.set("slider", Math.round(val));
 	});
 }
 
 export function setSlider(index) {
-	Session.set('slider', (QuestionGroupCollection.findOne().questionList[index].timer / 1000));
+	Session.set("slider", (QuestionGroupCollection.findOne().questionList[index].timer / 1000));
 	$("#slider").val((QuestionGroupCollection.findOne().questionList[index].timer / 1000));
 }
diff --git a/arsnova.click/client/layout/view_timer/scripts/onCreated.js b/arsnova.click/client/layout/view_timer/scripts/onCreated.js
index 5ad8a9ae9..b1cdb2898 100644
--- a/arsnova.click/client/layout/view_timer/scripts/onCreated.js
+++ b/arsnova.click/client/layout/view_timer/scripts/onCreated.js
@@ -15,14 +15,8 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
-import * as localData from '/client/lib/local_storage.js';
 
 Template.createTimerView.onCreated(function () {
-	Session.set("slider", 0);
-
-	this.subscribe('AnswerOptionCollection.instructor', localData.getPrivateKey(), Session.get("hashtag"));
-	this.subscribe('QuestionGroupCollection.authorizeAsOwner', localData.getPrivateKey(), Session.get("hashtag"));
-	this.subscribe("EventManagerCollection.join", Session.get("hashtag"));
+	localStorage.setItem(Router.current().params.quizName + "slider", 0);
 });
diff --git a/arsnova.click/client/layout/view_timer/scripts/onDestroyed.js b/arsnova.click/client/layout/view_timer/scripts/onDestroyed.js
index 99d9e6e2f..45f8e794d 100644
--- a/arsnova.click/client/layout/view_timer/scripts/onDestroyed.js
+++ b/arsnova.click/client/layout/view_timer/scripts/onDestroyed.js
@@ -15,13 +15,14 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
+import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
-import {validationTrackerHandle, subscriptionHandler} from './lib.js';
+import {validationTrackerHandle} from './lib.js';
 
 Template.createTimerView.onDestroyed(function () {
 	var body = $('body');
 	body.off('click', '.questionIcon:not(.active)');
 	body.off('click', '.removeQuestion');
 	validationTrackerHandle.stop();
-	subscriptionHandler.stop();
+	Session.set("slider", undefined);
 });
diff --git a/arsnova.click/client/layout/view_timer/scripts/onRendered.js b/arsnova.click/client/layout/view_timer/scripts/onRendered.js
index 246f2c21b..6323454e9 100644
--- a/arsnova.click/client/layout/view_timer/scripts/onRendered.js
+++ b/arsnova.click/client/layout/view_timer/scripts/onRendered.js
@@ -15,7 +15,6 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {Tracker} from 'meteor/tracker';
 import {EventManagerCollection} from '/lib/eventmanager/collection.js';
@@ -23,16 +22,9 @@ import {QuestionGroupCollection} from '/lib/questions/collection.js';
 import * as lib from './lib.js';
 
 Template.createTimerView.onRendered(function () {
-	lib.createSlider();
-
-	let index;
-	lib.subscriptionHandler = Tracker.autorun(()=> {
-		if (this.subscriptionsReady()) {
-			index = EventManagerCollection.findOne().questionIndex;
-			lib.subscriptionHandler.stop();
-			lib.setSlider(index);
-		}
-	});
+	let index = EventManagerCollection.findOne().questionIndex;
+	lib.createSlider(index);
+	lib.setSlider(index);
 	var body = $('body');
 	body.on('click', '.questionIcon:not(.active)', function () {
 		var currentSession = QuestionGroupCollection.findOne();
@@ -41,14 +33,14 @@ Template.createTimerView.onRendered(function () {
 		}
 
 		lib.setTimer(index);
-		Router.go("/question");
+		Router.go("/" + Router.current().params.quizName + "/question");
 	});
 	body.on('click', '.removeQuestion', function () {
 		index = EventManagerCollection.findOne().questionIndex;
 	});
 
 	lib.validationTrackerHandle = Tracker.autorun(()=> {
-		var validQuestions = Session.get("validQuestions");
+		var validQuestions = localStorage.getItem(Router.current().params.quizName + "validQuestions");
 		var forwardButton = $('#forwardButton');
 		forwardButton.removeAttr("disabled");
 		for (var i = 0; i < validQuestions.length; i++) {
diff --git a/arsnova.click/client/layout/view_timer/templates/createTimerView.html b/arsnova.click/client/layout/view_timer/templates/createTimerView.html
index f604a18ca..e029d4deb 100644
--- a/arsnova.click/client/layout/view_timer/templates/createTimerView.html
+++ b/arsnova.click/client/layout/view_timer/templates/createTimerView.html
@@ -29,7 +29,7 @@
 						<input type="text"
 							   title="slider_value"
 							   class="input-field round-corners slider-timer-input"
-							   aria-describedby="basic-addon1" disabled value="{{slider}} {{_ "view.timer.second" count=slider}}">
+							   aria-describedby="basic-addon1" disabled value="{{slider}} {{_ "view.timer.second" count=slider}}"/>
 					</div>
 				</div>
 			</div>
diff --git a/arsnova.click/client/layout/view_voting/scripts/events.js b/arsnova.click/client/layout/view_voting/scripts/events.js
index 0604aa8b3..28cebaa6a 100644
--- a/arsnova.click/client/layout/view_voting/scripts/events.js
+++ b/arsnova.click/client/layout/view_voting/scripts/events.js
@@ -81,14 +81,14 @@ Template.votingview.events({
 		if (EventManagerCollection.findOne().questionIndex + 1 >= QuestionGroupCollection.findOne().questionList.length) {
 			Session.set("sessionClosed", true);
 		}
-		Router.go("/results");
+		Router.go("/" + Router.current().params.quizName + "/results");
 	},
 	"click .sendResponse": function (event) {
 		event.stopPropagation();
 
 		if (Session.get("questionSC") || (AnswerOptionCollection.find({questionIndex: EventManagerCollection.findOne().questionIndex}).count() === 1)) {
 			makeAndSendResponse(event.currentTarget.id);
-			Router.go("/results");
+			Router.go("/" + Router.current().params.quizName + "/results");
 		} else {
 			var responseArr = JSON.parse(Session.get("responses"));
 			var currentId = event.currentTarget.id;
diff --git a/arsnova.click/client/layout/view_voting/scripts/lib.js b/arsnova.click/client/layout/view_voting/scripts/lib.js
index d3cdb8f33..64c9edc52 100644
--- a/arsnova.click/client/layout/view_voting/scripts/lib.js
+++ b/arsnova.click/client/layout/view_voting/scripts/lib.js
@@ -37,7 +37,7 @@ export function startCountdown(index) {
 	countdownRunning = true;
 
 	Meteor.call('Question.isSC', {
-		hashtag: Session.get("hashtag"),
+		hashtag: Router.current().params.quizName,
 		questionIndex: EventManagerCollection.findOne().questionIndex
 	}, (err, res) => {
 		if (!err && res) {
@@ -83,7 +83,7 @@ export function startCountdown(index) {
 			Session.set("sessionClosed", true);
 		}
 		Session.set("countdownInitialized", false);
-		Router.go("/results");
+		Router.go("/" + Router.current().params.quizName + "/results");
 		countdownRunning = false;
 	});
 	Session.set("countdownInitialized", true);
@@ -91,10 +91,10 @@ export function startCountdown(index) {
 
 export function makeAndSendResponse(answerOptionNumber) {
 	Meteor.call('ResponsesCollection.addResponse', {
-		hashtag: Session.get("hashtag"),
+		hashtag: Router.current().params.quizName,
 		questionIndex: EventManagerCollection.findOne().questionIndex,
 		answerOptionNumber: Number(answerOptionNumber),
-		userNick: Session.get("nick")
+		userNick: localStorage.getItem(Router.current().params.quizName + "nick")
 	}, (err) => {
 		if (err) {
 			new ErrorSplashscreen({
diff --git a/arsnova.click/client/layout/view_voting/scripts/onCreated.js b/arsnova.click/client/layout/view_voting/scripts/onCreated.js
index 972e9dfce..493241070 100644
--- a/arsnova.click/client/layout/view_voting/scripts/onCreated.js
+++ b/arsnova.click/client/layout/view_voting/scripts/onCreated.js
@@ -22,23 +22,14 @@ import {AnswerOptionCollection} from '/lib/answeroptions/collection.js';
 import {startCountdown, deleteCountdown} from './lib.js';
 
 Template.votingview.onCreated(function () {
-	Session.set("sessionClosed", undefined);
+	Session.set("sessionClosed",undefined);
 	deleteCountdown();
 
-	this.subscribe("EventManagerCollection.join", Session.get("hashtag"));
-	this.subscribe('QuestionGroupCollection.questionList', Session.get("hashtag"), function () {
-		Session.set("questionGroupSubscriptionReady", true);
-		if (!Session.get("sessionClosed")) {
-			startCountdown(EventManagerCollection.findOne().questionIndex);
-		}
-	});
-
-	this.subscribe('AnswerOptionCollection.public', Session.get("hashtag"), function () {
-		var answerOptionCount = AnswerOptionCollection.find({questionIndex: EventManagerCollection.findOne().questionIndex}).count();
-		var responseArr = [];
-		for (var i = 0; i < answerOptionCount; i++) {
-			responseArr[i] = false;
-		}
-		Session.set("responses", JSON.stringify(responseArr));
-	});
+	startCountdown(EventManagerCollection.findOne().questionIndex);
+	var answerOptionCount = AnswerOptionCollection.find({questionIndex: EventManagerCollection.findOne().questionIndex}).count();
+	var responseArr = [];
+	for (var i = 0; i < answerOptionCount; i++) {
+		responseArr[i] = false;
+	}
+	Session.set("responses",JSON.stringify(responseArr));
 });
diff --git a/arsnova.click/client/lib/connection.js b/arsnova.click/client/lib/connection.js
index aa8f1fe32..6f26e9d28 100644
--- a/arsnova.click/client/lib/connection.js
+++ b/arsnova.click/client/lib/connection.js
@@ -16,11 +16,10 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import * as localData from './local_storage.js';
 
 Meteor.setInterval(function () {
-	if (Session.get("isOwner")) {
-		Meteor.call('keepalive', localData.getPrivateKey(), Session.get("hashtag"));
+	if (localData.containsHashtag(Router.current().params.quizName)) {
+		Meteor.call('keepalive', localData.getPrivateKey(), Router.current().params.quizName);
 	}
 }, 5000);
diff --git a/arsnova.click/client/lib/local_storage.js b/arsnova.click/client/lib/local_storage.js
index ab878e4ed..a5a7d6057 100644
--- a/arsnova.click/client/lib/local_storage.js
+++ b/arsnova.click/client/lib/local_storage.js
@@ -36,7 +36,7 @@ export function containsHashtag(hashtag) {
 		return;
 	}
 	const hashtagString = localStorage.getItem("hashtags");
-	return hashtagString ? $.inArray(hashtag, JSON.parse(hashtagString)) : false;
+	return hashtagString ? $.inArray(hashtag, JSON.parse(hashtagString)) > -1 : false;
 }
 
 export function deleteHashtag(hashtag) {
diff --git a/arsnova.click/client/plugins/event_stack_observer/scripts/lib.js b/arsnova.click/client/plugins/event_stack_observer/scripts/lib.js
index 5598c2177..b4c3bf822 100644
--- a/arsnova.click/client/plugins/event_stack_observer/scripts/lib.js
+++ b/arsnova.click/client/plugins/event_stack_observer/scripts/lib.js
@@ -37,7 +37,7 @@ export class EventStackObserver {
 			changed: function (id, changedFields) {
 				if (changedFields.eventStack) {
 					let index = changedFields.eventStack.length - 1;
-					let currentPath = Router.current().route.path();
+					let currentPath = Router.current().route.getName();
 					if (observerInstance.onChangeCallbacks[currentPath] && observerInstance.onChangeCallbacks[currentPath].length > 0) {
 						let item = changedFields.eventStack[index];
 						if ($.inArray(item.key, observerInstance.ignoreChanges) > -1) {
@@ -92,7 +92,8 @@ export class EventStackObserver {
 		if (typeof callback !== 'function') {
 			throw new Error("invalid callback!");
 		}
-		let currentPath = Router.current().route.path();
+		let currentPath = Router.current().route.getName();
+		console.log("attemting to push callback to array", Router.current().route.getName(), limiter, callback);
 		if (!(this.onChangeCallbacks[currentPath] instanceof Array)) {
 			this.onChangeCallbacks[currentPath] = [];
 		}
@@ -103,6 +104,7 @@ export class EventStackObserver {
 			}
 		});
 		if (!hasCallback) {
+			console.log("adding callback to array",limiter, callback);
 			this.onChangeCallbacks[currentPath].push({
 				limiter,
 				callback
@@ -117,5 +119,5 @@ export function setGlobalEventStackObserver() {
 	if (globalEventStackObserver) {
 		globalEventStackObserver.stop();
 	}
-	globalEventStackObserver = new EventStackObserver({verbose: false});
+	globalEventStackObserver = new EventStackObserver({verbose: true});
 }
diff --git a/arsnova.click/client/plugins/sound/scripts/helpers.js b/arsnova.click/client/plugins/sound/scripts/helpers.js
index 15b86e7c2..af24a31ae 100644
--- a/arsnova.click/client/plugins/sound/scripts/helpers.js
+++ b/arsnova.click/client/plugins/sound/scripts/helpers.js
@@ -15,14 +15,13 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 
 Template.soundConfig.helpers({
 	slider2: function () {
-		return Session.get("slider2");
+		return localStorage.getItem(Router.current().params.quizName + "slider2");
 	},
 	isSoundPlaying: function () {
-		return Session.get("soundIsPlaying");
+		return localStorage.getItem(Router.current().params.quizName + "soundIsPlaying");
 	}
 });
diff --git a/arsnova.click/client/plugins/sound/scripts/lib.js b/arsnova.click/client/plugins/sound/scripts/lib.js
index ac1f858e4..33d5e3673 100644
--- a/arsnova.click/client/plugins/sound/scripts/lib.js
+++ b/arsnova.click/client/plugins/sound/scripts/lib.js
@@ -15,13 +15,11 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
-
 export let buzzsound1 = null;
 
 export function setBuzzsound1(fileName) {
 	buzzsound1 = new buzz.sound('/sounds/' + fileName, {
 		loop: true
 	});
-	buzzsound1.setVolume(Session.get("globalVolume"));
+	buzzsound1.setVolume(localStorage.getItem(Router.current().params.quizName + "globalVolume"));
 }
diff --git a/arsnova.click/client/plugins/sound/scripts/onRendered.js b/arsnova.click/client/plugins/sound/scripts/onRendered.js
index 281a48b53..d26e63774 100644
--- a/arsnova.click/client/plugins/sound/scripts/onRendered.js
+++ b/arsnova.click/client/plugins/sound/scripts/onRendered.js
@@ -15,27 +15,26 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
-import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {buzzsound1, setBuzzsound1} from './lib.js';
 
 Template.soundConfig.onRendered(function () {
 	setBuzzsound1('bensound-thelounge.mp3');
-	Session.set("globalVolume", 80);
+	localStorage.setItem(Router.current().params.quizName + "globalVolume", 80);
 
 	this.$("#slider2").noUiSlider({
-		start: Session.get("slider2"),
+		start: localStorage.getItem(Router.current().params.quizName + "slider2"),
 		range: {
 			'min': 0,
 			'max': 100
 		}
 	}).on('slide', function (ev, val) {
-		Session.set('slider2', Math.round(val));
-		Session.set("globalVolume", Math.round(val));
-		buzzsound1.setVolume(Session.get("globalVolume"));
+		localStorage.setItem(Router.current().params.quizName + 'slider2', Math.round(val));
+		localStorage.setItem(Router.current().params.quizName + "globalVolume", Math.round(val));
+		buzzsound1.setVolume(localStorage.getItem(Router.current().params.quizName + "globalVolume"));
 	}).on('change', function (ev, val) {
-		Session.set('slider2', Math.round(val));
-		Session.set("globalVolume", Math.round(val));
-		buzzsound1.setVolume(Session.get("globalVolume"));
+		localStorage.setItem(Router.current().params.quizName + 'slider2', Math.round(val));
+		localStorage.setItem(Router.current().params.quizName + "globalVolume", Math.round(val));
+		buzzsound1.setVolume(localStorage.getItem(Router.current().params.quizName + "globalVolume"));
 	});
 });
diff --git a/arsnova.click/client/routes.js b/arsnova.click/client/routes.js
index 6ab5f0153..b788ec683 100644
--- a/arsnova.click/client/routes.js
+++ b/arsnova.click/client/routes.js
@@ -16,7 +16,6 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
-import {Session} from 'meteor/session';
 import {TAPi18n} from 'meteor/tap:i18n';
 import {EventManagerCollection} from '/lib/eventmanager/collection.js';
 import {AnswerOptionCollection} from '/lib/answeroptions/collection.js';
@@ -27,42 +26,41 @@ import {Splashscreen, ErrorSplashscreen} from '/client/plugins/splashscreen/scri
 import {globalEventStackObserver, setGlobalEventStackObserver} from '/client/plugins/event_stack_observer/scripts/lib.js';
 
 Router.configure({
-	layoutTemplate: 'layout'
+	layoutTemplate: "layout",
+	loadingTemplate: "loading"
 });
 
 Router.onBeforeAction(function () {
 	if (Router.current().route.path() !== "/") {
-		if (!globalEventStackObserver || !globalEventStackObserver.isRunning()) {
-			Meteor.subscribe('EventManagerCollection.join', Session.get("hashtag"), ()=> {
-				if (!EventManagerCollection.findOne(Session.get("hashtag"))) {
-					Meteor.call('EventManagerCollection.add', localData.getPrivateKey(), Session.get("hashtag"), function () {
-						globalEventStackObserver.start(Session.get("hashtag"));
-					});
+		Meteor.subscribe('EventManagerCollection.join', Router.current().params.quizName, ()=> {
+			if (!globalEventStackObserver) {
+				setGlobalEventStackObserver();
+				if (!globalEventStackObserver.isRunning()) {
+					if (!EventManagerCollection.findOne(Router.current().params.quizName)) {
+						Meteor.call('EventManagerCollection.add', localData.getPrivateKey(), Router.current().params.quizName, function () {
+							globalEventStackObserver.start(Router.current().params.quizName);
+						});
+					}
 				}
-			});
-		} else {
+			}
+			console.log("adding callback for " + Router.current().route.path(), Router.current().params.quizName, Router.current().route.getName());
 			globalEventStackObserver.onChange([
 				"EventManagerCollection.setSessionStatus",
 				"EventManagerCollection.reset"
 			], function (key, value) {
 				if (!isNaN(value.sessionStatus)) {
 					if (value.sessionStatus < 2) {
-						Session.set("hashtag", undefined);
-						Session.set("slider", undefined);
-						if (!Session.get("isOwner")) {
+						if (!localData.containsHashtag(Router.current().params.quizName)) {
 							new ErrorSplashscreen({
 								autostart: true,
 								errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages.session_closed")
 							});
-							Router.go("/resetToHome");
-						} else {
-							Session.set("isOwner", undefined);
-							Router.go("/");
 						}
+						Router.go("/" + Router.current().params.quizName + "/resetToHome");
 					}
 				}
 			});
-		}
+		});
 	}
 	this.next();
 });
@@ -70,224 +68,281 @@ Router.onBeforeAction(function () {
 Router.onAfterAction(function () {
 });
 
-Router.route('/', function () {
-	try {
-		localData.initializePrivateKey();
-		Session.set("localStorageAvailable", true);
-	} catch (err) {
-		Session.set("localStorageAvailable", false);
+Router.route('/', {
+	waitOn: function () {
+		return [
+			Meteor.subscribe('HashtagsCollection.public')
+		];
+	},
+	action: function () {
+		try {
+			localData.initializePrivateKey();
+			localStorage.setItem("localStorageAvailable", true);
+		} catch (err) {
+			localStorage.setItem("localStorageAvailable", false);
+		}
+		localStorage.setItem("slider", undefined);
+		this.render('home');
 	}
-	setGlobalEventStackObserver();
-	Session.set("hashtag", undefined);
-	Session.set("slider", undefined);
-	Session.set("isOwner", undefined);
-	this.render('home');
 });
 
-Router.route('/resetToHome', function () {
+Router.route('/:quizName/resetToHome', function () {
+	delete localStorage[Router.current().params.quizName + "nick"];
 	Router.go("/");
 });
 
-Router.route('/nick', function () {
-	if (!Session.get("hashtag")) {
-		Router.go("/");
-	}
+Router.route('/:quizName/nick', function () {
 	this.render('nick');
 });
 
-Router.route('/question', function () {
-	if (Session.get("isOwner")) {
-		Meteor.subscribe('EventManagerCollection.join', Session.get("hashtag"), ()=> {
-			if (!EventManagerCollection.findOne(Session.get("hashtag"))) {
-				Meteor.call('EventManagerCollection.setActiveQuestion', localData.getPrivateKey(), Session.get("hashtag"), 0);
+Router.route('/:quizName/question', {
+
+	waitOn: function () {
+		return [
+			Meteor.subscribe('QuestionGroupCollection.authorizeAsOwner', localData.getPrivateKey(), Router.current().params.quizName),
+			Meteor.subscribe("EventManagerCollection.join", Router.current().params.quizName)
+		];
+	},
+
+	action: function () {
+		if (localData.containsHashtag(Router.current().params.quizName)) {
+			if (!EventManagerCollection.findOne(Router.current().params.quizName)) {
+				Meteor.call('EventManagerCollection.setActiveQuestion', localData.getPrivateKey(), Router.current().params.quizName, 0);
 			}
-		});
-		this.render('createQuestionView');
-	} else {
-		Router.go("/");
+			this.render('createQuestionView');
+		} else {
+			Router.go("/");
+		}
 	}
 });
 
-Router.route('/answeroptions', function () {
-	this.render('createAnswerOptions');
+Router.route('/:quizName/answeroptions', {
+	waitOn: function () {
+		return [
+			Meteor.subscribe('AnswerOptionCollection.instructor', localData.getPrivateKey(), Router.current().params.quizName),
+			Meteor.subscribe('EventManagerCollection.join', Router.current().params.quizName)
+		];
+	},
+	action: function () {
+		if (localData.containsHashtag(Router.current().params.quizName)) {
+			this.render('createAnswerOptions');
+		} else {
+			Router.go("/");
+		}
+	}
 });
 
-Router.route('/settimer', function () {
-	if (Session.get("isOwner")) {
-		this.render('createTimerView');
-	} else {
-		Router.go('/');
+Router.route('/:quizName/settimer', {
+	waitOn: function () {
+		return [
+			Meteor.subscribe('AnswerOptionCollection.instructor', localData.getPrivateKey(), Router.current().params.quizName),
+			Meteor.subscribe('QuestionGroupCollection.authorizeAsOwner', localData.getPrivateKey(), Router.current().params.quizName),
+			Meteor.subscribe("EventManagerCollection.join", Router.current().params.quizName)
+		];
+	},
+	action: function () {
+		if (localData.containsHashtag(Router.current().params.quizName)) {
+			this.render('createTimerView');
+		} else {
+			Router.go("/");
+		}
 	}
 });
 
-Router.route('/memberlist', function () {
-	globalEventStackObserver.onChange([
-		"EventManagerCollection.setSessionStatus"
-	], function (key, value) {
-		if (!isNaN(value.sessionStatus)) {
-			if (value.sessionStatus === 3) {
-				Router.go("/results");
-			}
-		}
-	});
-
-	globalEventStackObserver.onChange([
-		"MemberListCollection.removeLearner"
-	], function (key, value) {
-		if (value.user) {
-			if (value.user === Session.get("nick")) {
-				new ErrorSplashscreen({
-					autostart: true,
-					errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages.kicked_from_quiz")
-				});
-				Router.go("/resetToHome");
-			}
+Router.route('/:quizName/memberlist', {
+	action: function () {
+		this.wait(Meteor.subscribe('MemberListCollection.members', Router.current().params.quizName));
+		this.wait(Meteor.subscribe('QuestionGroupCollection.memberlist', Router.current().params.quizName));
+		this.wait(Meteor.subscribe('ResponsesCollection.session', Router.current().params.quizName));
+
+		if (!this.ready()) {
+			this.render("loading");
+		} else {
+			globalEventStackObserver.onChange([
+				"EventManagerCollection.setSessionStatus"
+			], function (key, value) {
+				if (!isNaN(value.sessionStatus)) {
+					if (value.sessionStatus === 3) {
+						Router.go("/" + Router.current().params.quizName + "/results");
+					}
+				}
+			});
+
+			globalEventStackObserver.onChange([
+				"MemberListCollection.removeLearner"
+			], function (key, value) {
+				if (value.user) {
+					if (value.user === localStorage.getItem(Router.current().params.quizName + "nick")) {
+						new ErrorSplashscreen({
+							autostart: true,
+							errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages.kicked_from_quiz")
+						});
+						Router.go("/" + Router.current().params.quizName + "/resetToHome");
+					}
+				}
+			});
+			this.render('memberlist');
 		}
-	});
-	this.render('memberlist');
+	}
 });
 
-Router.route('/votingview', function () {
-	this.render('votingview');
+Router.route('/:quizName/votingview', {
+	waitOn: function () {
+		return [
+			Meteor.subscribe("EventManagerCollection.join", Router.current().params.quizName),
+			Meteor.subscribe('QuestionGroupCollection.questionList', Router.current().params.quizName),
+			Meteor.subscribe('AnswerOptionCollection.public', Router.current().params.quizName)
+		];
+	},
+	action: function () {
+		this.render('votingview');
+	}
 });
 
 
-Router.route('/onpolling', {
+Router.route('/:quizName/onpolling', {
 	waitOn: function () {
 		return [
-			Meteor.subscribe('QuestionGroupCollection.questionList', Session.get("hashtag")),
-			Meteor.subscribe('EventManagerCollection.join', Session.get("hashtag")),
-			Meteor.subscribe('ResponsesCollection.session', Session.get("hashtag")),
-			Meteor.subscribe('AnswerOptionCollection.options', Session.get("hashtag")),
-			Meteor.subscribe('MemberListCollection.members', Session.get("hashtag")),
+			Meteor.subscribe('QuestionGroupCollection.questionList', Router.current().params.quizName),
+			Meteor.subscribe('ResponsesCollection.session', Router.current().params.quizName),
+			Meteor.subscribe('AnswerOptionCollection.options', Router.current().params.quizName),
+			Meteor.subscribe('MemberListCollection.members', Router.current().params.quizName),
 			Meteor.subscribe('HashtagsCollection.public')
 		];
 	},
 
 	action: function () {
-		if (Session.get("isOwner")) {
+		if (localData.containsHashtag(Router.current().params.quizName)) {
 			this.render('live_results');
 		} else {
 			this.render('votingview');
 		}
 	}
 });
-Router.route('/results', {
-	waitOn: function () {
-		return [
-			Meteor.subscribe('QuestionGroupCollection.questionList', Session.get("hashtag")),
-			Meteor.subscribe('EventManagerCollection.join', Session.get("hashtag")),
-			Meteor.subscribe('ResponsesCollection.session', Session.get("hashtag")),
-			Meteor.subscribe('AnswerOptionCollection.options', Session.get("hashtag")),
-			Meteor.subscribe('MemberListCollection.members', Session.get("hashtag")),
-			Meteor.subscribe('HashtagsCollection.public')
-		];
-	},
-
+Router.route('/:quizName/results', {
 	action: function () {
-		globalEventStackObserver.onChange([
-			"EventManagerCollection.setSessionStatus"
-		], function (key, value) {
-			if (!isNaN(value.sessionStatus)) {
-				if (value.sessionStatus === 2) {
-					$('.modal-backdrop').remove();
-					Router.go("/memberlist");
+		this.wait(Meteor.subscribe('QuestionGroupCollection.questionList', Router.current().params.quizName));
+		this.wait(Meteor.subscribe('ResponsesCollection.session', Router.current().params.quizName));
+		this.wait(Meteor.subscribe('AnswerOptionCollection.options', Router.current().params.quizName));
+		this.wait(Meteor.subscribe('MemberListCollection.members', Router.current().params.quizName));
+		this.wait(Meteor.subscribe('HashtagsCollection.public'));
+
+		if (!this.ready()) {
+			this.render("loading");
+		} else {
+			globalEventStackObserver.onChange([
+				"EventManagerCollection.setSessionStatus"
+			], function (key, value) {
+				if (!isNaN(value.sessionStatus)) {
+					if (value.sessionStatus === 2) {
+						$('.modal-backdrop').remove();
+						Router.go("/" + Router.current().params.quizName + "/memberlist");
+					}
 				}
-			}
-		});
+			});
+
+			globalEventStackObserver.onChange([
+				"EventManagerCollection.setActiveQuestion"
+			], function (key, value) {
+				if (!isNaN(value.questionIndex) && value.questionIndex !== -1) {
+					if (localData.containsHashtag(Router.current().params.quizName)) {
+						new Splashscreen({
+							autostart: true,
+							instanceId: "answers_" + EventManagerCollection.findOne().questionIndex,
+							templateName: 'questionAndAnswerSplashscreen',
+							closeOnButton: '#js-btn-hideQuestionModal',
+							onRendered: function (instance) {
+								var content = "";
+								mathjaxMarkdown.initializeMarkdownAndLatex();
+								AnswerOptionCollection.find({questionIndex: EventManagerCollection.findOne().questionIndex}, {sort: {answerOptionNumber: 1}}).forEach(function (answerOption) {
+									if (!answerOption.answerText) {
+										answerOption.answerText = "";
+									}
 
-		globalEventStackObserver.onChange(["EventManagerCollection.setActiveQuestion"], function (key, value) {
-			if (!isNaN(value.questionIndex) && value.questionIndex !== -1) {
-				if (Session.get("isOwner")) {
+									content += String.fromCharCode((answerOption.answerOptionNumber + 65)) + "<br/>";
+									content += mathjaxMarkdown.getContent(answerOption.answerText) + "<br/>";
+								});
+
+								instance.templateSelector.find('#answerContent').html(content);
+								setTimeout(function () {
+									instance.close();
+								}, 10000);
+							}
+						});
+					} else {
+						Router.go("/" + Router.current().params.quizName + "/onpolling");
+					}
+				}
+			});
+
+			globalEventStackObserver.onChange([
+				"EventManagerCollection.showReadConfirmedForIndex"
+			], function (key, value) {
+				if (!isNaN(value.readingConfirmationIndex) && value.readingConfirmationIndex > -1) {
+					var questionDoc = QuestionGroupCollection.findOne();
 					new Splashscreen({
 						autostart: true,
-						instanceId: "answers_" + EventManagerCollection.findOne().questionIndex,
-						templateName: 'questionAndAnswerSplashscreen',
-						closeOnButton: '#js-btn-hideQuestionModal',
+						templateName: 'readingConfirmedSplashscreen',
+						closeOnButton: '#setReadConfirmed',
 						onRendered: function (instance) {
 							var content = "";
-							mathjaxMarkdown.initializeMarkdownAndLatex();
-							AnswerOptionCollection.find({questionIndex: EventManagerCollection.findOne().questionIndex}, {sort: {answerOptionNumber: 1}}).forEach(function (answerOption) {
-								if (!answerOption.answerText) {
-									answerOption.answerText = "";
-								}
-
-								content += String.fromCharCode((answerOption.answerOptionNumber + 65)) + "<br/>";
-								content += mathjaxMarkdown.getContent(answerOption.answerText) + "<br/>";
-							});
+							if (questionDoc) {
+								mathjaxMarkdown.initializeMarkdownAndLatex();
+								var questionText = questionDoc.questionList[EventManagerCollection.findOne().readingConfirmationIndex].questionText;
+								content = mathjaxMarkdown.getContent(questionText);
+							}
+							instance.templateSelector.find('#questionContent').html(content);
 
-							instance.templateSelector.find('#answerContent').html(content);
-							setTimeout(function () {
-								instance.close();
-							}, 10000);
+							if (localData.containsHashtag(Router.current().params.quizName)) {
+								instance.templateSelector.find('#setReadConfirmed').text(TAPi18n.__("global.close_window"));
+							} else {
+								instance.templateSelector.find('#setReadConfirmed').parent().on('click', '#setReadConfirmed', function () {
+									Meteor.call("MemberListCollection.setReadConfirmed", {
+										hashtag: Router.current().params.quizName,
+										questionIndex: EventManagerCollection.findOne().readingConfirmationIndex,
+										nick: localStorage.getItem(Router.current().params.quizName + "nick")
+									}, (err)=> {
+										if (err) {
+											new ErrorSplashscreen({
+												autostart: true,
+												errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages." + err.reason)
+											});
+										}
+									});
+								});
+							}
 						}
 					});
-				} else {
-					Router.go("/onpolling");
 				}
-			}
-		});
-
-		globalEventStackObserver.onChange(["EventManagerCollection.showReadConfirmedForIndex"], function (key, value) {
-			if (!isNaN(value.readingConfirmationIndex) && value.readingConfirmationIndex > -1) {
-				var questionDoc = QuestionGroupCollection.findOne();
-				new Splashscreen({
-					autostart: true,
-					templateName: 'readingConfirmedSplashscreen',
-					closeOnButton: '#setReadConfirmed',
-					onRendered: function (instance) {
-						var content = "";
-						if (questionDoc) {
-							mathjaxMarkdown.initializeMarkdownAndLatex();
-							var questionText = questionDoc.questionList[EventManagerCollection.findOne().readingConfirmationIndex].questionText;
-							content = mathjaxMarkdown.getContent(questionText);
-						}
-						instance.templateSelector.find('#questionContent').html(content);
-
-						if (Session.get("isOwner")) {
-							instance.templateSelector.find('#setReadConfirmed').text(TAPi18n.__("global.close_window"));
-						} else {
-							instance.templateSelector.find('#setReadConfirmed').parent().on('click', '#setReadConfirmed', function () {
-								Meteor.call("MemberListCollection.setReadConfirmed", {
-									hashtag: Session.get("hashtag"),
-									questionIndex: EventManagerCollection.findOne().readingConfirmationIndex,
-									nick: Session.get("nick")
-								}, (err)=> {
-									if (err) {
-										new ErrorSplashscreen({
-											autostart: true,
-											errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages." + err.reason)
-										});
-									}
-								});
-							});
-						}
-					}
-				});
-			}
-		});
-		this.render('live_results');
+			});
+			this.render('live_results');
+		}
 	}
 });
-/*
-Router.route('/onpolling', function () {
-	if (Session.get("isOwner")) {
-		this.render('liveResults');
-	} else {
-		$('.modal-backdrop').hide();
-		this.render('votingview');
+
+Router.route('/:quizName/statistics', {
+	waitOn: function () {
+		Meteor.subscribe('ResponsesCollection.session', Router.current().params.quizName);
+		Meteor.subscribe('AnswerOptionCollection.options', Router.current().params.quizName);
+		Meteor.subscribe('MemberListCollection.members', Router.current().params.quizName);
+		Meteor.subscribe('QuestionGroupCollection.questionList', Router.current().params.quizName);
+		Meteor.subscribe("EventManagerCollection.join", Router.current().params.quizName);
+	},
+	action: function () {
+		this.render('leaderBoard');
 	}
 });
-Router.route('/results', function () {
-	this.render('liveResults');
-});
-*/
-Router.route('/statistics', function () {
-	this.render('leaderBoard');
-});
 
-Router.route('/hashtagmanagement', function () {
-	this.render('hashtagManagement');
+Router.route('/hashtagmanagement', {
+	waitOn: function () {
+		return [
+			Meteor.subscribe('HashtagsCollection.public'),
+			Meteor.subscribe("EventManagerCollection.join", Router.current().params.quizName)
+		];
+	},
+	action: function () {
+		this.render('hashtagManagement');
+	}
 });
 
 // Routes for Footer-Links
@@ -316,8 +371,8 @@ Router.onStop(function () {
 	var lastRoute = Router.current().route.getName();
 	if (lastRoute === undefined) {
 		//homeView
-		Session.set("lastPage", "/");
+		localStorage.setItem(Router.current().params.quizName + "lastPage", "/");
 	} else if (lastRoute !== "agb" && lastRoute !== "datenschutz" && lastRoute !== "impressum") {
-		Session.set("lastPage", lastRoute);
+		localStorage.setItem(Router.current().params.quizName + "lastPage", lastRoute);
 	}
 });
-- 
GitLab


From c4297676f71ceb3bf21a6f454beafb6bf2f9a333 Mon Sep 17 00:00:00 2001
From: Christopher Fullarton <christopher@fullarton.eu>
Date: Fri, 6 May 2016 07:40:52 +0200
Subject: [PATCH 02/10] Add Session usage to the sound template again, fix
 routing bugs

---
 .../client/layout/region_header/header.js     |  19 +-
 .../event_stack_observer/scripts/lib.js       |   6 +-
 .../client/plugins/sound/scripts/helpers.js   |   5 +-
 .../client/plugins/sound/scripts/lib.js       |   4 +-
 .../plugins/sound/scripts/onRendered.js       |  17 +-
 arsnova.click/client/routes.js                | 284 +++++++++---------
 .../server/publications/eventmanager.js       |  19 +-
 7 files changed, 178 insertions(+), 176 deletions(-)

diff --git a/arsnova.click/client/layout/region_header/header.js b/arsnova.click/client/layout/region_header/header.js
index a60b1b5a8..514549497 100644
--- a/arsnova.click/client/layout/region_header/header.js
+++ b/arsnova.click/client/layout/region_header/header.js
@@ -16,6 +16,7 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
+import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {TAPi18n} from 'meteor/tap:i18n';
 import * as localData from '/client/lib/local_storage.js';
@@ -24,8 +25,8 @@ import {Splashscreen} from "/client/plugins/splashscreen/scripts/lib";
 import {ErrorSplashscreen} from '/client/plugins/splashscreen/scripts/lib.js';
 
 Template.header.onCreated(function () {
-	localStorage.setItem("slider2", 80);
-	localStorage.setItem("globalVolume", 80);
+	Session.setDefault("slider2", 80);
+	Session.setDefault("globalVolume", 80);
 
 	setBuzzsound1('waity.mp3');
 });
@@ -95,7 +96,7 @@ Template.header.events({
 			onRendered: function (instance) {
 				instance.templateSelector.find('#soundSelect').on('change', function (event) {
 					buzzsound1.stop();
-					localStorage.setItem(Router.current().params.quizName + "soundIsPlaying", false);
+					Session.set("soundIsPlaying", false);
 					switch ($(event.target).val()) {
 						case "Song1":
 							setBuzzsound1("bensound-thelounge.mp3");
@@ -110,28 +111,28 @@ Template.header.events({
 				});
 
 				instance.templateSelector.find("#js-btn-playStopMusic").on('click', function () {
-					if (localStorage.getItem(Router.current().params.quizName + "soundIsPlaying")) {
+					if (Session.get("soundIsPlaying")) {
 						buzzsound1.stop();
-						localStorage.setItem(Router.current().params.quizName + "soundIsPlaying", false);
+						Session.set("soundIsPlaying", false);
 					} else {
 						buzzsound1.play();
-						localStorage.setItem(Router.current().params.quizName + "soundIsPlaying", true);
+						Session.set("soundIsPlaying", true);
 					}
 				});
 
 				instance.templateSelector.find("#js-btn-hideSoundModal").on('click', function () {
 					buzzsound1.stop();
-					localStorage.setItem(Router.current().params.quizName + "soundIsPlaying", false);
+					Session.set("soundIsPlaying", false);
 				});
 
 				instance.templateSelector.find('#isSoundOnButton').on('click', function () {
 					var btn = $('#isSoundOnButton');
 					btn.toggleClass("down");
 					if (btn.hasClass("down")) {
-						localStorage.setItem(Router.current().params.quizName + "togglemusic", true);
+						Session.set("togglemusic", true);
 						btn.html(TAPi18n.__("plugins.sound.active"));
 					} else {
-						localStorage.setItem(Router.current().params.quizName + "togglemusic", false);
+						Session.set("togglemusic", false);
 						btn.html(TAPi18n.__("plugins.sound.inactive"));
 					}
 				});
diff --git a/arsnova.click/client/plugins/event_stack_observer/scripts/lib.js b/arsnova.click/client/plugins/event_stack_observer/scripts/lib.js
index b4c3bf822..105627957 100644
--- a/arsnova.click/client/plugins/event_stack_observer/scripts/lib.js
+++ b/arsnova.click/client/plugins/event_stack_observer/scripts/lib.js
@@ -26,7 +26,7 @@ export class EventStackObserver {
 		this.running = false;
 	}
 
-	start (hashtag) {
+	startObserving (hashtag) {
 		this.stop();
 		if (!EventManagerCollection.findOne({hashtag})) {
 			throw new Error("EventManager collection is not ready!");
@@ -93,7 +93,6 @@ export class EventStackObserver {
 			throw new Error("invalid callback!");
 		}
 		let currentPath = Router.current().route.getName();
-		console.log("attemting to push callback to array", Router.current().route.getName(), limiter, callback);
 		if (!(this.onChangeCallbacks[currentPath] instanceof Array)) {
 			this.onChangeCallbacks[currentPath] = [];
 		}
@@ -104,7 +103,6 @@ export class EventStackObserver {
 			}
 		});
 		if (!hasCallback) {
-			console.log("adding callback to array",limiter, callback);
 			this.onChangeCallbacks[currentPath].push({
 				limiter,
 				callback
@@ -119,5 +117,5 @@ export function setGlobalEventStackObserver() {
 	if (globalEventStackObserver) {
 		globalEventStackObserver.stop();
 	}
-	globalEventStackObserver = new EventStackObserver({verbose: true});
+	globalEventStackObserver = new EventStackObserver({verbose: false});
 }
diff --git a/arsnova.click/client/plugins/sound/scripts/helpers.js b/arsnova.click/client/plugins/sound/scripts/helpers.js
index af24a31ae..15b86e7c2 100644
--- a/arsnova.click/client/plugins/sound/scripts/helpers.js
+++ b/arsnova.click/client/plugins/sound/scripts/helpers.js
@@ -15,13 +15,14 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
+import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 
 Template.soundConfig.helpers({
 	slider2: function () {
-		return localStorage.getItem(Router.current().params.quizName + "slider2");
+		return Session.get("slider2");
 	},
 	isSoundPlaying: function () {
-		return localStorage.getItem(Router.current().params.quizName + "soundIsPlaying");
+		return Session.get("soundIsPlaying");
 	}
 });
diff --git a/arsnova.click/client/plugins/sound/scripts/lib.js b/arsnova.click/client/plugins/sound/scripts/lib.js
index 33d5e3673..ac1f858e4 100644
--- a/arsnova.click/client/plugins/sound/scripts/lib.js
+++ b/arsnova.click/client/plugins/sound/scripts/lib.js
@@ -15,11 +15,13 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
+import {Session} from 'meteor/session';
+
 export let buzzsound1 = null;
 
 export function setBuzzsound1(fileName) {
 	buzzsound1 = new buzz.sound('/sounds/' + fileName, {
 		loop: true
 	});
-	buzzsound1.setVolume(localStorage.getItem(Router.current().params.quizName + "globalVolume"));
+	buzzsound1.setVolume(Session.get("globalVolume"));
 }
diff --git a/arsnova.click/client/plugins/sound/scripts/onRendered.js b/arsnova.click/client/plugins/sound/scripts/onRendered.js
index d26e63774..281a48b53 100644
--- a/arsnova.click/client/plugins/sound/scripts/onRendered.js
+++ b/arsnova.click/client/plugins/sound/scripts/onRendered.js
@@ -15,26 +15,27 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
+import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {buzzsound1, setBuzzsound1} from './lib.js';
 
 Template.soundConfig.onRendered(function () {
 	setBuzzsound1('bensound-thelounge.mp3');
-	localStorage.setItem(Router.current().params.quizName + "globalVolume", 80);
+	Session.set("globalVolume", 80);
 
 	this.$("#slider2").noUiSlider({
-		start: localStorage.getItem(Router.current().params.quizName + "slider2"),
+		start: Session.get("slider2"),
 		range: {
 			'min': 0,
 			'max': 100
 		}
 	}).on('slide', function (ev, val) {
-		localStorage.setItem(Router.current().params.quizName + 'slider2', Math.round(val));
-		localStorage.setItem(Router.current().params.quizName + "globalVolume", Math.round(val));
-		buzzsound1.setVolume(localStorage.getItem(Router.current().params.quizName + "globalVolume"));
+		Session.set('slider2', Math.round(val));
+		Session.set("globalVolume", Math.round(val));
+		buzzsound1.setVolume(Session.get("globalVolume"));
 	}).on('change', function (ev, val) {
-		localStorage.setItem(Router.current().params.quizName + 'slider2', Math.round(val));
-		localStorage.setItem(Router.current().params.quizName + "globalVolume", Math.round(val));
-		buzzsound1.setVolume(localStorage.getItem(Router.current().params.quizName + "globalVolume"));
+		Session.set('slider2', Math.round(val));
+		Session.set("globalVolume", Math.round(val));
+		buzzsound1.setVolume(Session.get("globalVolume"));
 	});
 });
diff --git a/arsnova.click/client/routes.js b/arsnova.click/client/routes.js
index b788ec683..77322bd32 100644
--- a/arsnova.click/client/routes.js
+++ b/arsnova.click/client/routes.js
@@ -27,39 +27,43 @@ import {globalEventStackObserver, setGlobalEventStackObserver} from '/client/plu
 
 Router.configure({
 	layoutTemplate: "layout",
-	loadingTemplate: "loading"
+	loadingTemplate: "loading",
+	waitOn: function () {
+		const subscriptions = [];
+		if (typeof Router.current().params.quizName !== "undefined") {
+			subscriptions.push(Meteor.subscribe('EventManagerCollection.join', Router.current().params.quizName));
+		}
+		return subscriptions;
+	}
 });
 
 Router.onBeforeAction(function () {
 	if (Router.current().route.path() !== "/") {
-		Meteor.subscribe('EventManagerCollection.join', Router.current().params.quizName, ()=> {
-			if (!globalEventStackObserver) {
-				setGlobalEventStackObserver();
-				if (!globalEventStackObserver.isRunning()) {
-					if (!EventManagerCollection.findOne(Router.current().params.quizName)) {
-						Meteor.call('EventManagerCollection.add', localData.getPrivateKey(), Router.current().params.quizName, function () {
-							globalEventStackObserver.start(Router.current().params.quizName);
-						});
-					}
+		if (!globalEventStackObserver) {
+			setGlobalEventStackObserver();
+			if (!globalEventStackObserver.isRunning()) {
+				if (!EventManagerCollection.findOne(Router.current().params.quizName)) {
+					Meteor.call('EventManagerCollection.add', localData.getPrivateKey(), Router.current().params.quizName, function () {
+						globalEventStackObserver.startObserving(Router.current().params.quizName);
+					});
 				}
 			}
-			console.log("adding callback for " + Router.current().route.path(), Router.current().params.quizName, Router.current().route.getName());
-			globalEventStackObserver.onChange([
-				"EventManagerCollection.setSessionStatus",
-				"EventManagerCollection.reset"
-			], function (key, value) {
-				if (!isNaN(value.sessionStatus)) {
-					if (value.sessionStatus < 2) {
-						if (!localData.containsHashtag(Router.current().params.quizName)) {
-							new ErrorSplashscreen({
-								autostart: true,
-								errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages.session_closed")
-							});
-						}
-						Router.go("/" + Router.current().params.quizName + "/resetToHome");
+		}
+		globalEventStackObserver.onChange([
+			"EventManagerCollection.setSessionStatus",
+			"EventManagerCollection.reset"
+		], function (key, value) {
+			if (!isNaN(value.sessionStatus)) {
+				if (value.sessionStatus < 2) {
+					if (!localData.containsHashtag(Router.current().params.quizName)) {
+						new ErrorSplashscreen({
+							autostart: true,
+							errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages.session_closed")
+						});
 					}
+					Router.go("/" + Router.current().params.quizName + "/resetToHome");
 				}
-			});
+			}
 		});
 	}
 	this.next();
@@ -150,39 +154,38 @@ Router.route('/:quizName/settimer', {
 });
 
 Router.route('/:quizName/memberlist', {
+	waitOn: function () {
+		return [
+			Meteor.subscribe('QuestionGroupCollection.questionList', Router.current().params.quizName),
+			Meteor.subscribe('ResponsesCollection.session', Router.current().params.quizName),
+			Meteor.subscribe('MemberListCollection.members', Router.current().params.quizName)
+		];
+	},
 	action: function () {
-		this.wait(Meteor.subscribe('MemberListCollection.members', Router.current().params.quizName));
-		this.wait(Meteor.subscribe('QuestionGroupCollection.memberlist', Router.current().params.quizName));
-		this.wait(Meteor.subscribe('ResponsesCollection.session', Router.current().params.quizName));
-
-		if (!this.ready()) {
-			this.render("loading");
-		} else {
-			globalEventStackObserver.onChange([
-				"EventManagerCollection.setSessionStatus"
-			], function (key, value) {
-				if (!isNaN(value.sessionStatus)) {
-					if (value.sessionStatus === 3) {
-						Router.go("/" + Router.current().params.quizName + "/results");
-					}
+		globalEventStackObserver.onChange([
+			"EventManagerCollection.setSessionStatus"
+		], function (key, value) {
+			if (!isNaN(value.sessionStatus)) {
+				if (value.sessionStatus === 3) {
+					Router.go("/" + Router.current().params.quizName + "/results");
 				}
-			});
+			}
+		});
 
-			globalEventStackObserver.onChange([
-				"MemberListCollection.removeLearner"
-			], function (key, value) {
-				if (value.user) {
-					if (value.user === localStorage.getItem(Router.current().params.quizName + "nick")) {
-						new ErrorSplashscreen({
-							autostart: true,
-							errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages.kicked_from_quiz")
-						});
-						Router.go("/" + Router.current().params.quizName + "/resetToHome");
-					}
+		globalEventStackObserver.onChange([
+			"MemberListCollection.removeLearner"
+		], function (key, value) {
+			if (value.user) {
+				if (value.user === localStorage.getItem(Router.current().params.quizName + "nick")) {
+					new ErrorSplashscreen({
+						autostart: true,
+						errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages.kicked_from_quiz")
+					});
+					Router.go("/" + Router.current().params.quizName + "/resetToHome");
 				}
-			});
-			this.render('memberlist');
-		}
+			}
+		});
+		this.render('memberlist');
 	}
 });
 
@@ -220,103 +223,102 @@ Router.route('/:quizName/onpolling', {
 	}
 });
 Router.route('/:quizName/results', {
+	waitOn: function () {
+		return [
+			Meteor.subscribe('QuestionGroupCollection.questionList', Router.current().params.quizName),
+			Meteor.subscribe('ResponsesCollection.session', Router.current().params.quizName),
+			Meteor.subscribe('AnswerOptionCollection.options', Router.current().params.quizName),
+			Meteor.subscribe('MemberListCollection.members', Router.current().params.quizName),
+			Meteor.subscribe('HashtagsCollection.public')
+		];
+	},
 	action: function () {
-		this.wait(Meteor.subscribe('QuestionGroupCollection.questionList', Router.current().params.quizName));
-		this.wait(Meteor.subscribe('ResponsesCollection.session', Router.current().params.quizName));
-		this.wait(Meteor.subscribe('AnswerOptionCollection.options', Router.current().params.quizName));
-		this.wait(Meteor.subscribe('MemberListCollection.members', Router.current().params.quizName));
-		this.wait(Meteor.subscribe('HashtagsCollection.public'));
-
-		if (!this.ready()) {
-			this.render("loading");
-		} else {
-			globalEventStackObserver.onChange([
-				"EventManagerCollection.setSessionStatus"
-			], function (key, value) {
-				if (!isNaN(value.sessionStatus)) {
-					if (value.sessionStatus === 2) {
-						$('.modal-backdrop').remove();
-						Router.go("/" + Router.current().params.quizName + "/memberlist");
-					}
+		globalEventStackObserver.onChange([
+			"EventManagerCollection.setSessionStatus"
+		], function (key, value) {
+			if (!isNaN(value.sessionStatus)) {
+				if (value.sessionStatus === 2) {
+					$('.modal-backdrop').remove();
+					Router.go("/" + Router.current().params.quizName + "/memberlist");
 				}
-			});
-
-			globalEventStackObserver.onChange([
-				"EventManagerCollection.setActiveQuestion"
-			], function (key, value) {
-				if (!isNaN(value.questionIndex) && value.questionIndex !== -1) {
-					if (localData.containsHashtag(Router.current().params.quizName)) {
-						new Splashscreen({
-							autostart: true,
-							instanceId: "answers_" + EventManagerCollection.findOne().questionIndex,
-							templateName: 'questionAndAnswerSplashscreen',
-							closeOnButton: '#js-btn-hideQuestionModal',
-							onRendered: function (instance) {
-								var content = "";
-								mathjaxMarkdown.initializeMarkdownAndLatex();
-								AnswerOptionCollection.find({questionIndex: EventManagerCollection.findOne().questionIndex}, {sort: {answerOptionNumber: 1}}).forEach(function (answerOption) {
-									if (!answerOption.answerText) {
-										answerOption.answerText = "";
-									}
-
-									content += String.fromCharCode((answerOption.answerOptionNumber + 65)) + "<br/>";
-									content += mathjaxMarkdown.getContent(answerOption.answerText) + "<br/>";
-								});
-
-								instance.templateSelector.find('#answerContent').html(content);
-								setTimeout(function () {
-									instance.close();
-								}, 10000);
-							}
-						});
-					} else {
-						Router.go("/" + Router.current().params.quizName + "/onpolling");
-					}
-				}
-			});
+			}
+		});
 
-			globalEventStackObserver.onChange([
-				"EventManagerCollection.showReadConfirmedForIndex"
-			], function (key, value) {
-				if (!isNaN(value.readingConfirmationIndex) && value.readingConfirmationIndex > -1) {
-					var questionDoc = QuestionGroupCollection.findOne();
+		globalEventStackObserver.onChange([
+			"EventManagerCollection.setActiveQuestion"
+		], function (key, value) {
+			if (!isNaN(value.questionIndex) && value.questionIndex !== -1) {
+				if (localData.containsHashtag(Router.current().params.quizName)) {
 					new Splashscreen({
 						autostart: true,
-						templateName: 'readingConfirmedSplashscreen',
-						closeOnButton: '#setReadConfirmed',
+						instanceId: "answers_" + EventManagerCollection.findOne().questionIndex,
+						templateName: 'questionAndAnswerSplashscreen',
+						closeOnButton: '#js-btn-hideQuestionModal',
 						onRendered: function (instance) {
 							var content = "";
-							if (questionDoc) {
-								mathjaxMarkdown.initializeMarkdownAndLatex();
-								var questionText = questionDoc.questionList[EventManagerCollection.findOne().readingConfirmationIndex].questionText;
-								content = mathjaxMarkdown.getContent(questionText);
-							}
-							instance.templateSelector.find('#questionContent').html(content);
-
-							if (localData.containsHashtag(Router.current().params.quizName)) {
-								instance.templateSelector.find('#setReadConfirmed').text(TAPi18n.__("global.close_window"));
-							} else {
-								instance.templateSelector.find('#setReadConfirmed').parent().on('click', '#setReadConfirmed', function () {
-									Meteor.call("MemberListCollection.setReadConfirmed", {
-										hashtag: Router.current().params.quizName,
-										questionIndex: EventManagerCollection.findOne().readingConfirmationIndex,
-										nick: localStorage.getItem(Router.current().params.quizName + "nick")
-									}, (err)=> {
-										if (err) {
-											new ErrorSplashscreen({
-												autostart: true,
-												errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages." + err.reason)
-											});
-										}
-									});
-								});
-							}
+							mathjaxMarkdown.initializeMarkdownAndLatex();
+							AnswerOptionCollection.find({questionIndex: EventManagerCollection.findOne().questionIndex}, {sort: {answerOptionNumber: 1}}).forEach(function (answerOption) {
+								if (!answerOption.answerText) {
+									answerOption.answerText = "";
+								}
+
+								content += String.fromCharCode((answerOption.answerOptionNumber + 65)) + "<br/>";
+								content += mathjaxMarkdown.getContent(answerOption.answerText) + "<br/>";
+							});
+
+							instance.templateSelector.find('#answerContent').html(content);
+							setTimeout(function () {
+								instance.close();
+							}, 10000);
 						}
 					});
+				} else {
+					Router.go("/" + Router.current().params.quizName + "/onpolling");
 				}
-			});
-			this.render('live_results');
-		}
+			}
+		});
+
+		globalEventStackObserver.onChange([
+			"EventManagerCollection.showReadConfirmedForIndex"
+		], function (key, value) {
+			if (!isNaN(value.readingConfirmationIndex) && value.readingConfirmationIndex > -1) {
+				var questionDoc = QuestionGroupCollection.findOne();
+				new Splashscreen({
+					autostart: true,
+					templateName: 'readingConfirmedSplashscreen',
+					closeOnButton: '#setReadConfirmed',
+					onRendered: function (instance) {
+						var content = "";
+						if (questionDoc) {
+							mathjaxMarkdown.initializeMarkdownAndLatex();
+							var questionText = questionDoc.questionList[EventManagerCollection.findOne().readingConfirmationIndex].questionText;
+							content = mathjaxMarkdown.getContent(questionText);
+						}
+						instance.templateSelector.find('#questionContent').html(content);
+
+						if (localData.containsHashtag(Router.current().params.quizName)) {
+							instance.templateSelector.find('#setReadConfirmed').text(TAPi18n.__("global.close_window"));
+						} else {
+							instance.templateSelector.find('#setReadConfirmed').parent().on('click', '#setReadConfirmed', function () {
+								Meteor.call("MemberListCollection.setReadConfirmed", {
+									hashtag: Router.current().params.quizName,
+									questionIndex: EventManagerCollection.findOne().readingConfirmationIndex,
+									nick: localStorage.getItem(Router.current().params.quizName + "nick")
+								}, (err)=> {
+									if (err) {
+										new ErrorSplashscreen({
+											autostart: true,
+											errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages." + err.reason)
+										});
+									}
+								});
+							});
+						}
+					}
+				});
+			}
+		});
+		this.render('live_results');
 	}
 });
 
diff --git a/arsnova.click/server/publications/eventmanager.js b/arsnova.click/server/publications/eventmanager.js
index fa13725e7..4f5e750e3 100644
--- a/arsnova.click/server/publications/eventmanager.js
+++ b/arsnova.click/server/publications/eventmanager.js
@@ -23,15 +23,12 @@ Meteor.publish('EventManagerCollection.join', (hashtag)=> {
 	if (typeof hashtag === "undefined") {
 		return false;
 	}
-	if (Meteor.isServer) {
-		new SimpleSchema({
-			hashtag: {
-				type: String,
-				min: 1,
-				max: 25
-			}
-		}).validate({hashtag: hashtag});
-		return EventManagerCollection.find({hashtag: hashtag});
-	}
-	return false;
+	new SimpleSchema({
+		hashtag: {
+			type: String,
+			min: 1,
+			max: 25
+		}
+	}).validate({hashtag: hashtag});
+	return EventManagerCollection.find({hashtag: hashtag});
 });
-- 
GitLab


From ebe7de47d0e088618fba5cdbc22c67dba4d38cfe Mon Sep 17 00:00:00 2001
From: Christopher Fullarton <christopher@fullarton.eu>
Date: Fri, 6 May 2016 08:04:47 +0200
Subject: [PATCH 03/10] Add a styled loading screen

---
 .../client/layout/global/styles/loading.scss  | 142 ++++++++++++++++++
 .../layout/global/templates/loading.html      |  13 +-
 arsnova.click/client/routes.js                |   9 +-
 3 files changed, 162 insertions(+), 2 deletions(-)
 create mode 100644 arsnova.click/client/layout/global/styles/loading.scss

diff --git a/arsnova.click/client/layout/global/styles/loading.scss b/arsnova.click/client/layout/global/styles/loading.scss
new file mode 100644
index 000000000..62484cbb4
--- /dev/null
+++ b/arsnova.click/client/layout/global/styles/loading.scss
@@ -0,0 +1,142 @@
+
+/*
+Credits: https://ihatetomatoes.net/create-custom-preloading-screen/
+*/
+#loader-wrapper {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1000;
+
+  .loader-section {
+	position: fixed;
+	top: 0;
+	width: 51%;
+	height: 100%;
+	z-index: 1000;
+	-webkit-transform: translateX(0);  /* Chrome, Opera 15+, Safari 3.1+ */
+	-ms-transform: translateX(0);  /* IE 9 */
+	transform: translateX(0);  /* Firefox 16+, IE 10+, Opera */
+
+	&.section-left {
+	  left: 0;
+	}
+
+	&.section-right {
+	  right: 0;
+	}
+  }
+
+  #loader {
+	display: block;
+	position: relative;
+	left: 50%;
+	top: 50%;
+	width: 150px;
+	height: 150px;
+	margin: -75px 0 0 -75px;
+	border-radius: 50%;
+	border: 3px solid transparent;
+	border-top-color: #3498db;
+
+	-webkit-animation: spin 2s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
+	animation: spin 2s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
+
+	z-index: 1001;
+
+	&:before {
+	  content: "";
+	  position: absolute;
+	  top: 5px;
+	  left: 5px;
+	  right: 5px;
+	  bottom: 5px;
+	  border-radius: 50%;
+	  border: 3px solid transparent;
+	  border-top-color: #e74c3c;
+
+	  -webkit-animation: spin 3s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
+	  animation: spin 3s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
+	}
+
+	&:after {
+	  content: "";
+	  position: absolute;
+	  top: 15px;
+	  left: 15px;
+	  right: 15px;
+	  bottom: 15px;
+	  border-radius: 50%;
+	  border: 3px solid transparent;
+	  border-top-color: #f9c922;
+
+	  -webkit-animation: spin 1.5s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
+	  animation: spin 1.5s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
+	}
+  }
+
+  /* Loaded */
+  &.loaded {
+	-webkit-transform: translateY(-100%);
+	-ms-transform: translateY(-100%);
+	transform: translateY(-100%);
+
+	-webkit-transition: all 0.3s 0.6s ease-out;
+	transition: all 0.3s 0.6s ease-out;
+
+	.loader-section.section-left {
+	  -webkit-transform: translateX(-100%);  /* Chrome, Opera 15+, Safari 3.1+ */
+	  -ms-transform: translateX(-100%);  /* IE 9 */
+	  transform: translateX(-100%);  /* Firefox 16+, IE 10+, Opera */
+
+	  -webkit-transition: all 0.3s 0.3s ease-out;
+	  transition: all 0.3s 0.3s ease-out;
+	}
+
+	.loader-section.section-right {
+	  -webkit-transform: translateX(100%);  /* Chrome, Opera 15+, Safari 3.1+ */
+	  -ms-transform: translateX(100%);  /* IE 9 */
+	  transform: translateX(100%);  /* Firefox 16+, IE 10+, Opera */
+
+	  -webkit-transition: all 0.3s 0.3s ease-out;
+	  transition: all 0.3s 0.3s ease-out;
+	}
+
+	#loader {
+	  opacity: 0;
+	  -webkit-transition: all 0.3s ease-out;
+	  transition: all 0.3s ease-out;
+	}
+
+	#loader-wrapper {
+	  visibility: hidden;
+	}
+  }
+
+  @-webkit-keyframes spin {
+	0%   {
+	  -webkit-transform: rotate(0deg);  /* Chrome, Opera 15+, Safari 3.1+ */
+	  -ms-transform: rotate(0deg);  /* IE 9 */
+	  transform: rotate(0deg);  /* Firefox 16+, IE 10+, Opera */
+	}
+	100% {
+	  -webkit-transform: rotate(360deg);  /* Chrome, Opera 15+, Safari 3.1+ */
+	  -ms-transform: rotate(360deg);  /* IE 9 */
+	  transform: rotate(360deg);  /* Firefox 16+, IE 10+, Opera */
+	}
+  }
+  @keyframes spin {
+	0%   {
+	  -webkit-transform: rotate(0deg);  /* Chrome, Opera 15+, Safari 3.1+ */
+	  -ms-transform: rotate(0deg);  /* IE 9 */
+	  transform: rotate(0deg);  /* Firefox 16+, IE 10+, Opera */
+	}
+	100% {
+	  -webkit-transform: rotate(360deg);  /* Chrome, Opera 15+, Safari 3.1+ */
+	  -ms-transform: rotate(360deg);  /* IE 9 */
+	  transform: rotate(360deg);  /* Firefox 16+, IE 10+, Opera */
+	}
+  }
+}
\ No newline at end of file
diff --git a/arsnova.click/client/layout/global/templates/loading.html b/arsnova.click/client/layout/global/templates/loading.html
index 868859757..5d179910d 100644
--- a/arsnova.click/client/layout/global/templates/loading.html
+++ b/arsnova.click/client/layout/global/templates/loading.html
@@ -1,3 +1,14 @@
 <template name="loading">
-    <p>Loading</p>
+    {{> titel}}
+    <div class="row">
+        <div class="col-md-12">
+            <div id="loader-wrapper">
+                <div id="loader"></div>
+
+                <div class="loader-section section-left"></div>
+                <div class="loader-section section-right"></div>
+
+            </div>
+        </div>
+    </div>
 </template>
\ No newline at end of file
diff --git a/arsnova.click/client/routes.js b/arsnova.click/client/routes.js
index 77322bd32..8e3d89ad9 100644
--- a/arsnova.click/client/routes.js
+++ b/arsnova.click/client/routes.js
@@ -38,7 +38,7 @@ Router.configure({
 });
 
 Router.onBeforeAction(function () {
-	if (Router.current().route.path() !== "/") {
+	if (typeof Router.current().params.quizName !== "undefined") {
 		if (!globalEventStackObserver) {
 			setGlobalEventStackObserver();
 			if (!globalEventStackObserver.isRunning()) {
@@ -70,6 +70,13 @@ Router.onBeforeAction(function () {
 });
 
 Router.onAfterAction(function () {
+	$('#loader-wrapper').toggleClass('loaded');
+});
+
+Router.route("/loading", {
+	action: function () {
+		this.render("loading");
+	}
 });
 
 Router.route('/', {
-- 
GitLab


From 3c6785e986cba48696c3dddac6506db6c601844d Mon Sep 17 00:00:00 2001
From: Christopher Fullarton <christopher@fullarton.eu>
Date: Fri, 6 May 2016 08:06:37 +0200
Subject: [PATCH 04/10] Fix gulp errors

---
 arsnova.click/client/layout/global/scripts/onRendered.js         | 1 -
 arsnova.click/client/layout/view_live_results/scripts/helpers.js | 1 +
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/arsnova.click/client/layout/global/scripts/onRendered.js b/arsnova.click/client/layout/global/scripts/onRendered.js
index 3895bb7ab..96a2f570a 100644
--- a/arsnova.click/client/layout/global/scripts/onRendered.js
+++ b/arsnova.click/client/layout/global/scripts/onRendered.js
@@ -20,7 +20,6 @@ import  * as localData from '/client/lib/local_storage.js';
 import {Splashscreen} from '/client/plugins/splashscreen/scripts/lib.js';
 import * as hashtagLib from '/client/layout/view_hashtag_management/scripts/lib.js';
 import {HashtagsCollection} from '/lib/hashtags/collection.js';
-import {EventManagerCollection} from '/lib/eventmanager/collection.js';
 
 Template.home.onRendered(function () {
 	if (localStorage.getItem("localStorageAvailable") && localData.getAllHashtags().length > 0) {
diff --git a/arsnova.click/client/layout/view_live_results/scripts/helpers.js b/arsnova.click/client/layout/view_live_results/scripts/helpers.js
index aa87bd8e4..a52f6f83e 100644
--- a/arsnova.click/client/layout/view_live_results/scripts/helpers.js
+++ b/arsnova.click/client/layout/view_live_results/scripts/helpers.js
@@ -15,6 +15,7 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
+import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {TAPi18n} from 'meteor/tap:i18n';
 import {EventManagerCollection} from '/lib/eventmanager/collection.js';
-- 
GitLab


From e75042ddc7bc9f2052a2fa9334adc414abf6b46b Mon Sep 17 00:00:00 2001
From: Christopher Fullarton <christopher@fullarton.eu>
Date: Fri, 6 May 2016 08:22:09 +0200
Subject: [PATCH 05/10] Add route when providing only a quiz name

---
 arsnova.click/client/routes.js | 127 +++++++++++++++++++--------------
 1 file changed, 74 insertions(+), 53 deletions(-)

diff --git a/arsnova.click/client/routes.js b/arsnova.click/client/routes.js
index 8e3d89ad9..ff835a8e9 100644
--- a/arsnova.click/client/routes.js
+++ b/arsnova.click/client/routes.js
@@ -37,6 +37,17 @@ Router.configure({
 	}
 });
 
+Router.onStop(function () {
+	var lastRoute = Router.current().route.getName();
+	if (lastRoute === undefined) {
+		//homeView
+		localStorage.setItem(Router.current().params.quizName + "lastPage", "/");
+	} else if (lastRoute !== "agb" && lastRoute !== "datenschutz" && lastRoute !== "impressum") {
+		localStorage.setItem(Router.current().params.quizName + "lastPage", lastRoute);
+	}
+});
+
+
 Router.onBeforeAction(function () {
 	if (typeof Router.current().params.quizName !== "undefined") {
 		if (!globalEventStackObserver) {
@@ -73,12 +84,6 @@ Router.onAfterAction(function () {
 	$('#loader-wrapper').toggleClass('loaded');
 });
 
-Router.route("/loading", {
-	action: function () {
-		this.render("loading");
-	}
-});
-
 Router.route('/', {
 	waitOn: function () {
 		return [
@@ -92,18 +97,78 @@ Router.route('/', {
 		} catch (err) {
 			localStorage.setItem("localStorageAvailable", false);
 		}
-		localStorage.setItem("slider", undefined);
 		this.render('home');
 	}
 });
 
+Router.route('/hashtagmanagement', {
+	waitOn: function () {
+		return [
+			Meteor.subscribe('HashtagsCollection.public'),
+			Meteor.subscribe("EventManagerCollection.join", Router.current().params.quizName)
+		];
+	},
+	action: function () {
+		this.render('hashtagManagement');
+	}
+});
+
+// Routes for Footer-Links
+
+Router.route('/ueber', function () {
+	this.render('ueber');
+});
+
+Router.route('/agb', function () {
+	this.render('agb');
+});
+
+Router.route('/datenschutz', function () {
+	this.render('datenschutz');
+});
+
+Router.route('/impressum', function () {
+	this.render('impressum');
+});
+
+Router.route('/translate', function () {
+	this.render('translate');
+});
+
+Router.route("/:quizName", {
+	action: function () {
+		console.log(EventManagerCollection.findOne().sessionStatus);
+		if (!EventManagerCollection.findOne() || EventManagerCollection.findOne().sessionStatus !== 2) {
+			try {
+				localData.initializePrivateKey();
+				localStorage.setItem("localStorageAvailable", true);
+				if (localData.containsHashtag(Router.current().params.quizName)) {
+					Router.go("/" + Router.current().params.quizName + "/question");
+				}
+			} catch (err) {
+				localStorage.setItem("localStorageAvailable", false);
+				Router.go("/");
+			}
+		} else {
+			Router.go("/" + Router.current().params.quizName + "/nick");
+		}
+	}
+});
+
 Router.route('/:quizName/resetToHome', function () {
 	delete localStorage[Router.current().params.quizName + "nick"];
+	delete localStorage.slider;
 	Router.go("/");
 });
 
-Router.route('/:quizName/nick', function () {
-	this.render('nick');
+Router.route('/:quizName/nick', {
+	action: function () {
+		if (!EventManagerCollection.findOne() || EventManagerCollection.findOne().sessionStatus !== 2) {
+			Router.go("/");
+		} else {
+			this.render('nick');
+		}
+	}
 });
 
 Router.route('/:quizName/question', {
@@ -341,47 +406,3 @@ Router.route('/:quizName/statistics', {
 		this.render('leaderBoard');
 	}
 });
-
-Router.route('/hashtagmanagement', {
-	waitOn: function () {
-		return [
-			Meteor.subscribe('HashtagsCollection.public'),
-			Meteor.subscribe("EventManagerCollection.join", Router.current().params.quizName)
-		];
-	},
-	action: function () {
-		this.render('hashtagManagement');
-	}
-});
-
-// Routes for Footer-Links
-
-Router.route('/ueber', function () {
-	this.render('ueber');
-});
-
-Router.route('/agb', function () {
-	this.render('agb');
-});
-
-Router.route('/datenschutz', function () {
-	this.render('datenschutz');
-});
-
-Router.route('/impressum', function () {
-	this.render('impressum');
-});
-
-Router.route('/translate', function () {
-	this.render('translate');
-});
-
-Router.onStop(function () {
-	var lastRoute = Router.current().route.getName();
-	if (lastRoute === undefined) {
-		//homeView
-		localStorage.setItem(Router.current().params.quizName + "lastPage", "/");
-	} else if (lastRoute !== "agb" && lastRoute !== "datenschutz" && lastRoute !== "impressum") {
-		localStorage.setItem(Router.current().params.quizName + "lastPage", lastRoute);
-	}
-});
-- 
GitLab


From 8f0ad11a7968a44196050b780251cf9e12062a2a Mon Sep 17 00:00:00 2001
From: Christopher Fullarton <christopher@fullarton.eu>
Date: Fri, 6 May 2016 08:40:05 +0200
Subject: [PATCH 06/10] Set path for images to absolute path

---
 .../layout/global/templates/layout.html       | 34 +++++++++----------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/arsnova.click/client/layout/global/templates/layout.html b/arsnova.click/client/layout/global/templates/layout.html
index 2ea6f77a5..83bb92b7b 100644
--- a/arsnova.click/client/layout/global/templates/layout.html
+++ b/arsnova.click/client/layout/global/templates/layout.html
@@ -37,44 +37,44 @@
 	<meta name="apple-mobile-web-app-capable" content="yes">
 	<meta name="apple-mobile-web-app-status-bar-style" content="white">
 
-	<link rel="icon" sizes="16x16 32x32" href="images/icons/favicon.ico?v=2">
+	<link rel="icon" sizes="16x16 32x32" href="/images/icons/favicon.ico?v=2">
 	<!-- special tag for apple "set to homescreen" favicon  -->
-	<link rel="apple-touch-icon" href="images/icons/favicon.ico?v=2">
-	<link rel="apple-touch-icon-precomposed" href="images/icons/favicon.ico?v=2">
+	<link rel="apple-touch-icon" href="/images/icons/favicon.ico?v=2">
+	<link rel="apple-touch-icon-precomposed" href="/images/icons/favicon.ico?v=2">
 
 	<!-- iPad and iPad mini (with @2× display) iOS ≥ 8 -->
-	<link rel="apple-touch-icon-precomposed" sizes="180x180" href="images/icons/arsNovaClick-180.png">
+	<link rel="apple-touch-icon-precomposed" sizes="180x180" href="/images/icons/arsNovaClick-180.png">
 	<!-- iPad 3+ (with @2× display) iOS ≥ 7 -->
-	<link rel="apple-touch-icon-precomposed" sizes="152x152" href="images/icons/arsNovaClick-152.png">
+	<link rel="apple-touch-icon-precomposed" sizes="152x152" href="/images/icons/arsNovaClick-152.png">
 	<!-- iPad (with @2× display) iOS ≤ 6 -->
-	<link rel="apple-touch-icon-precomposed" sizes="144x144" href="images/icons/arsNovaClick-144.png">
+	<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/images/icons/arsNovaClick-144.png">
 	<!-- iPhone (with @2× and @3 display) iOS ≥ 7 -->
-	<link rel="apple-touch-icon-precomposed" sizes="120x120" href="images/icons/arsNovaClick-120.png">
+	<link rel="apple-touch-icon-precomposed" sizes="120x120" href="/images/icons/arsNovaClick-120.png">
 	<!-- iPhone (with @2× display) iOS ≤ 6 -->
-	<link rel="apple-touch-icon-precomposed" sizes="114x114" href="images/icons/arsNovaClick-114.png">
+	<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/images/icons/arsNovaClick-114.png">
 	<!-- iPad mini and the first- and second-generation iPad (@1× display) on iOS ≥ 7 -->
-	<link rel="apple-touch-icon-precomposed" sizes="76x76" href="images/icons/arsNovaClick-76.png">
+	<link rel="apple-touch-icon-precomposed" sizes="76x76" href="/images/icons/arsNovaClick-76.png">
 	<!-- iPad mini and the first- and second-generation iPad (@1× display) on iOS ≤ 6 -->
-	<link rel="apple-touch-icon-precomposed" sizes="72x72" href="images/icons/arsNovaClick-72.png">
+	<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/images/icons/arsNovaClick-72.png">
 	<!-- Android Stock Browser and non-Retina iPhone and iPod Touch -->
-	<link rel="apple-touch-icon-precomposed" href="images/icons/arsNovaClick-57.png">
+	<link rel="apple-touch-icon-precomposed" href="/images/icons/arsNovaClick-57.png">
 	<!-- Fallback for everything else -->
-	<link rel="shortcut icon" href="images/icons/arsNovaClick-180.png">
+	<link rel="shortcut icon" href="/images/icons/arsNovaClick-180.png">
 
 	<!--
 			Chrome 31+ has home screen icon 192×192 (the recommended size for multiple resolutions).
 			If it’s not defined on that size it will take 128×128.
 		-->
-	<link rel="icon" sizes="192x192" href="images/icons/arsNovaClick-192.png">
-	<link rel="icon" sizes="128x128" href="images/icons/arsNovaClick-128.png">
+	<link rel="icon" sizes="192x192" href="/images/icons/arsNovaClick-192.png">
+	<link rel="icon" sizes="128x128" href="/images/icons/arsNovaClick-128.png">
 
 	<!-- Tile icon for Win8 (144x144 + tile color) -->
-	<meta name="msapplication-TileImage" content="images/icons/arsNovaClick-144.png">
+	<meta name="msapplication-TileImage" content="/images/icons/arsNovaClick-144.png">
 	<meta name="msapplication-TileColor" content="#222222">
 
 	<!-- wp icon -->
-	<meta name="msapplication-square70x70logo" content="images/icons/arsNovaClick-72.png">
-	<meta name="msapplication-square150x150logo" content="images/icons/arsNovaClick-152.png">
+	<meta name="msapplication-square70x70logo" content="/images/icons/arsNovaClick-72.png">
+	<meta name="msapplication-square150x150logo" content="/images/icons/arsNovaClick-152.png">
 
 	<!-- piwik -->
 	<script src="//arsnova.thm.de/stats/piwik.js" async defer></script>
-- 
GitLab


From c5edbc40d23ffbc04f14cfa38f7fcc631f95caec Mon Sep 17 00:00:00 2001
From: Christopher Fullarton <christopher@fullarton.eu>
Date: Fri, 6 May 2016 11:33:25 +0200
Subject: [PATCH 07/10] Clean local storage when leaving the session

---
 .../client/layout/region_footer/footer_helper.js         | 4 ++--
 arsnova.click/client/layout/region_header/header.js      | 2 +-
 arsnova.click/client/routes.js                           | 9 +++++++--
 3 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/arsnova.click/client/layout/region_footer/footer_helper.js b/arsnova.click/client/layout/region_footer/footer_helper.js
index 8bf09ce6d..11a2385bc 100644
--- a/arsnova.click/client/layout/region_footer/footer_helper.js
+++ b/arsnova.click/client/layout/region_footer/footer_helper.js
@@ -71,13 +71,13 @@ Template.footer.helpers({
 		return (showHomeSl.indexOf(Router.current().route.path()) !== -1) && (localStorage.getItem(Router.current().params.quizName + "lastPage") !== undefined) && (showHome.indexOf(localStorage.getItem(Router.current().params.quizName + "lastPage")) === -1) && (Router.current().route.path() !== '/');
 	},
 	getLastPage: function () {
-		return localStorage.getItem(Router.current().params.quizName + "lastPage");
+		return localStorage.getItem("lastPage");
 	}
 });
 
 Template.footer.events({
 	"click #toPrevPage": function () {
-		localStorage.setItem(Router.current().params.quizName + "lastPage", undefined);
+		delete localStorage.lastPage;
 	},
 	"click #hideShowFooterBar": function () {
 		if ($("#footerBar").hasClass("hide")) {
diff --git a/arsnova.click/client/layout/region_header/header.js b/arsnova.click/client/layout/region_header/header.js
index 514549497..bff4007a7 100644
--- a/arsnova.click/client/layout/region_header/header.js
+++ b/arsnova.click/client/layout/region_header/header.js
@@ -80,7 +80,7 @@ Template.header.events({
 								});
 							}
 						});
-						Router.go("/");
+						Router.go("/" + Router.current().params.quizName + "/resetToHome");
 					});
 				}
 			});
diff --git a/arsnova.click/client/routes.js b/arsnova.click/client/routes.js
index ff835a8e9..c0bc4c9d5 100644
--- a/arsnova.click/client/routes.js
+++ b/arsnova.click/client/routes.js
@@ -41,9 +41,9 @@ Router.onStop(function () {
 	var lastRoute = Router.current().route.getName();
 	if (lastRoute === undefined) {
 		//homeView
-		localStorage.setItem(Router.current().params.quizName + "lastPage", "/");
+		localStorage.setItem("lastPage", "/");
 	} else if (lastRoute !== "agb" && lastRoute !== "datenschutz" && lastRoute !== "impressum") {
-		localStorage.setItem(Router.current().params.quizName + "lastPage", lastRoute);
+		localStorage.setItem("lastPage", lastRoute);
 	}
 });
 
@@ -157,7 +157,12 @@ Router.route("/:quizName", {
 
 Router.route('/:quizName/resetToHome', function () {
 	delete localStorage[Router.current().params.quizName + "nick"];
+	delete localStorage[Router.current().params.quizName + "validQuestions"];
+	delete localStorage[Router.current().params.quizName + "markdownAlreadyChecked"];
+	delete localStorage[Router.current().params.quizName + "slider"];
+	delete localStorage[Router.current().params.quizName + "sessionClosed"];
 	delete localStorage.slider;
+	delete localStorage.lastPage;
 	Router.go("/");
 });
 
-- 
GitLab


From 0e303816c0d2e71eebd1b8e3479a19ca751bf75f Mon Sep 17 00:00:00 2001
From: Christopher Fullarton <christopher@fullarton.eu>
Date: Fri, 6 May 2016 12:04:48 +0200
Subject: [PATCH 08/10] Fix routing bugs for valid questions

---
 .../layout/region_header/question_list.js     | 33 ++++++++++---------
 .../view_hashtag_management/scripts/events.js |  4 +--
 .../scripts/onCreated.js                      |  1 -
 .../view_questions/scripts/onDestroyed.js     |  6 +++-
 .../view_questions/scripts/onRendered.js      |  8 ++---
 .../layout/view_timer/scripts/onCreated.js    |  1 -
 .../layout/view_timer/scripts/onDestroyed.js  |  7 ++--
 .../layout/view_timer/scripts/onRendered.js   |  2 +-
 arsnova.click/client/routes.js                |  4 +--
 9 files changed, 34 insertions(+), 32 deletions(-)

diff --git a/arsnova.click/client/layout/region_header/question_list.js b/arsnova.click/client/layout/region_header/question_list.js
index 3a06950c6..7259b6a2b 100644
--- a/arsnova.click/client/layout/region_header/question_list.js
+++ b/arsnova.click/client/layout/region_header/question_list.js
@@ -16,6 +16,7 @@
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
 import {Meteor} from 'meteor/meteor';
+import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {Tracker} from 'meteor/tracker';
 import {TAPi18n} from 'meteor/tap:i18n';
@@ -29,9 +30,7 @@ import * as lib from './lib.js';
 var redirectTracker = null;
 
 Template.questionList.onCreated(function () {
-	localStorage.setItem(Router.current().params.quizName + "validQuestions", []);
-
-	this.subscribe("EventManagerCollection.join", Router.current().params.quizName);
+	Session.set("validQuestions", []);
 	this.subscribe('QuestionGroupCollection.questionList', Router.current().params.quizName);
 	this.subscribe('AnswerOptionCollection.instructor', localData.getPrivateKey(), Router.current().params.quizName);
 
@@ -42,14 +41,14 @@ Template.questionList.onCreated(function () {
 			}
 
 			var questionList = QuestionGroupCollection.findOne().questionList;
-			var validQuestions = localStorage.getItem(Router.current().params.quizName + "validQuestions");
+			var validQuestions = Session.get("validQuestions");
 			if (questionList.length >= validQuestions.length) {
 				return;
 			}
 
 			validQuestions.splice(questionList.length - 1, validQuestions.length - questionList.length);
 
-			localStorage.setItem(Router.current().params.quizName + "validQuestions", validQuestions);
+			Session.set("validQuestions", validQuestions);
 		}
 	});
 });
@@ -61,7 +60,7 @@ Template.questionList.onDestroyed(function () {
 Template.questionList.onRendered(function () {
 	let handleRedirect = true;
 	redirectTracker = Tracker.autorun(function () {
-		let validQuestions = localStorage.getItem(Router.current().params.quizName + "validQuestions");
+		let validQuestions = Session.get("validQuestions");
 		if (!validQuestions || validQuestions.length === 0) {
 			return;
 		}
@@ -73,16 +72,18 @@ Template.questionList.onRendered(function () {
 				break;
 			}
 		}
-		if (!localStorage.getItem(Router.current().params.quizName + "overrideValidQuestionRedirect") && allValid && handleRedirect) {
-			localStorage.setItem(Router.current().params.quizName + "overrideValidQuestionRedirect", undefined);
-			Meteor.call("MemberListCollection.removeFromSession", localData.getPrivateKey(), Router.current().params.quizName);
-			Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Router.current().params.quizName, 0);
-			Meteor.call("EventManagerCollection.setSessionStatus", localData.getPrivateKey(), Router.current().params.quizName, 2);
-			Router.go("/" + Router.current().params.quizName + "/memberlist");
-		} else {
-			localStorage.setItem(Router.current().params.quizName + "overrideValidQuestionRedirect", undefined);
+		if (!Session.get("overrideValidQuestionRedirect")) {
+			delete Session.keys.overrideValidQuestionRedirect;
 			handleRedirect = false;
 			redirectTracker.stop();
+		} else {
+			if (allValid && handleRedirect) {
+				delete Session.keys.overrideValidQuestionRedirect;
+				Meteor.call("MemberListCollection.removeFromSession", localData.getPrivateKey(), Router.current().params.quizName);
+				Meteor.call("EventManagerCollection.setActiveQuestion", localData.getPrivateKey(), Router.current().params.quizName, 0);
+				Meteor.call("EventManagerCollection.setSessionStatus", localData.getPrivateKey(), Router.current().params.quizName, 2);
+				Router.go("/" + Router.current().params.quizName + "/memberlist");
+			}
 		}
 	});
 });
@@ -102,9 +103,9 @@ Template.questionList.helpers({
 		return index === EventManagerCollection.findOne().questionIndex;
 	},
 	hasCompleteContent: function (index) {
-		var validQuestions = localStorage.getItem(Router.current().params.quizName + "validQuestions");
+		var validQuestions = Session.get("validQuestions");
 		validQuestions[index] = lib.checkForValidQuestions(index);
-		localStorage.setItem(Router.current().params.quizName + "validQuestions", validQuestions);
+		Session.set("validQuestions", validQuestions);
 		return validQuestions[index];
 	}
 });
diff --git a/arsnova.click/client/layout/view_hashtag_management/scripts/events.js b/arsnova.click/client/layout/view_hashtag_management/scripts/events.js
index 9240c4204..fb1283db6 100644
--- a/arsnova.click/client/layout/view_hashtag_management/scripts/events.js
+++ b/arsnova.click/client/layout/view_hashtag_management/scripts/events.js
@@ -78,7 +78,7 @@ Template.hashtagView.events({
 		}));
 	},
 	"click #addNewHashtag": function () {
-		if (!localStorage.getItem(Router.current().params.quizName + "localStorageAvailable")) {
+		if (!localStorage.getItem("localStorageAvailable")) {
 			new ErrorSplashscreen({
 				autostart: true,
 				errorMessage: TAPi18n.__("plugins.splashscreen.error.error_messages.private_browsing")
@@ -204,7 +204,6 @@ Template.hashtagManagement.events({
 		var hashtag = $(event.currentTarget).parent().parent()[0].id;
 		localData.reenterSession(hashtag);
 		Meteor.call('EventManagerCollection.add', localData.getPrivateKey(), hashtag, function () {
-			localStorage.setItem(Router.current().params.quizName + "overrideValidQuestionRedirect", true);
 			Router.go("/" + hashtag + "/question");
 		});
 	},
@@ -279,6 +278,7 @@ Template.showHashtagsSplashscreen.events({
 		var hashtag = $(event.currentTarget).text();
 		localData.reenterSession(hashtag);
 		lib.hashtagSplashscreen.destroy();
+		Session.set("overrideValidQuestionRedirect", true);
 		Router.go("/" + hashtag + "/question");
 	},
 	"click #js-btn-showHashtagManagement": function () {
diff --git a/arsnova.click/client/layout/view_hashtag_management/scripts/onCreated.js b/arsnova.click/client/layout/view_hashtag_management/scripts/onCreated.js
index a975ea89a..630a21079 100644
--- a/arsnova.click/client/layout/view_hashtag_management/scripts/onCreated.js
+++ b/arsnova.click/client/layout/view_hashtag_management/scripts/onCreated.js
@@ -18,5 +18,4 @@
 import {Template} from 'meteor/templating';
 
 Template.hashtagManagement.onCreated(function () {
-	this.subscribe('HashtagsCollection.public');
 });
diff --git a/arsnova.click/client/layout/view_questions/scripts/onDestroyed.js b/arsnova.click/client/layout/view_questions/scripts/onDestroyed.js
index 8404bf384..4d5232ca0 100644
--- a/arsnova.click/client/layout/view_questions/scripts/onDestroyed.js
+++ b/arsnova.click/client/layout/view_questions/scripts/onDestroyed.js
@@ -15,6 +15,7 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
+import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import * as lib from './lib.js';
 
@@ -22,5 +23,8 @@ Template.createQuestionView.onDestroyed(function () {
 	var body = $('body');
 	body.off('click', '.questionIcon:not(.active)');
 	body.off('click', '.removeQuestion');
-	lib.subscriptionHandler.stop();
+	if (lib.subscriptionHandler) {
+		lib.subscriptionHandler.stop();
+	}
+	delete Session.keys.overrideValidQuestionRedirect;
 });
diff --git a/arsnova.click/client/layout/view_questions/scripts/onRendered.js b/arsnova.click/client/layout/view_questions/scripts/onRendered.js
index 8d3dfacfd..f9e5b0e01 100644
--- a/arsnova.click/client/layout/view_questions/scripts/onRendered.js
+++ b/arsnova.click/client/layout/view_questions/scripts/onRendered.js
@@ -22,18 +22,14 @@ import {QuestionGroupCollection} from '/lib/questions/collection.js';
 import * as lib from './lib.js';
 
 Template.createQuestionView.onRendered(function () {
-	localStorage.setItem(Router.current().params.quizName + "markdownAlreadyChecked", false);
 	lib.calculateWindow();
 	$(window).resize(lib.calculateWindow());
 
 	let index;
 	lib.subscriptionHandler = Tracker.autorun(()=> {
-		if (this.subscriptionsReady() && EventManagerCollection.findOne()) {
+		if (this.subscriptionsReady() && typeof localStorage.getItem(Router.current().params.quizName) !== "undefined") {
 			index = EventManagerCollection.findOne().questionIndex;
-			if (!localStorage.getItem(Router.current().params.quizName + "markdownAlreadyChecked")) {
-				lib.checkForMarkdown();
-				localStorage.setItem(Router.current().params.quizName + "markdownAlreadyChecked", true);
-			}
+			lib.checkForMarkdown();
 		}
 	});
 	var body = $('body');
diff --git a/arsnova.click/client/layout/view_timer/scripts/onCreated.js b/arsnova.click/client/layout/view_timer/scripts/onCreated.js
index b1cdb2898..b42537ef9 100644
--- a/arsnova.click/client/layout/view_timer/scripts/onCreated.js
+++ b/arsnova.click/client/layout/view_timer/scripts/onCreated.js
@@ -18,5 +18,4 @@
 import {Template} from 'meteor/templating';
 
 Template.createTimerView.onCreated(function () {
-	localStorage.setItem(Router.current().params.quizName + "slider", 0);
 });
diff --git a/arsnova.click/client/layout/view_timer/scripts/onDestroyed.js b/arsnova.click/client/layout/view_timer/scripts/onDestroyed.js
index 45f8e794d..7aad0144d 100644
--- a/arsnova.click/client/layout/view_timer/scripts/onDestroyed.js
+++ b/arsnova.click/client/layout/view_timer/scripts/onDestroyed.js
@@ -23,6 +23,9 @@ Template.createTimerView.onDestroyed(function () {
 	var body = $('body');
 	body.off('click', '.questionIcon:not(.active)');
 	body.off('click', '.removeQuestion');
-	validationTrackerHandle.stop();
-	Session.set("slider", undefined);
+	if (validationTrackerHandle) {
+		validationTrackerHandle.stop();
+	}
+	delete Session.keys.validQuestions;
+	delete Session.keys.slider;
 });
diff --git a/arsnova.click/client/layout/view_timer/scripts/onRendered.js b/arsnova.click/client/layout/view_timer/scripts/onRendered.js
index 6323454e9..7d7098220 100644
--- a/arsnova.click/client/layout/view_timer/scripts/onRendered.js
+++ b/arsnova.click/client/layout/view_timer/scripts/onRendered.js
@@ -40,7 +40,7 @@ Template.createTimerView.onRendered(function () {
 	});
 
 	lib.validationTrackerHandle = Tracker.autorun(()=> {
-		var validQuestions = localStorage.getItem(Router.current().params.quizName + "validQuestions");
+		var validQuestions = Session.get("validQuestions");
 		var forwardButton = $('#forwardButton');
 		forwardButton.removeAttr("disabled");
 		for (var i = 0; i < validQuestions.length; i++) {
diff --git a/arsnova.click/client/routes.js b/arsnova.click/client/routes.js
index c0bc4c9d5..871325084 100644
--- a/arsnova.click/client/routes.js
+++ b/arsnova.click/client/routes.js
@@ -104,8 +104,7 @@ Router.route('/', {
 Router.route('/hashtagmanagement', {
 	waitOn: function () {
 		return [
-			Meteor.subscribe('HashtagsCollection.public'),
-			Meteor.subscribe("EventManagerCollection.join", Router.current().params.quizName)
+			Meteor.subscribe('HashtagsCollection.public')
 		];
 	},
 	action: function () {
@@ -161,6 +160,7 @@ Router.route('/:quizName/resetToHome', function () {
 	delete localStorage[Router.current().params.quizName + "markdownAlreadyChecked"];
 	delete localStorage[Router.current().params.quizName + "slider"];
 	delete localStorage[Router.current().params.quizName + "sessionClosed"];
+	delete localStorage[Router.current().params.quizName + "overrideValidQuestionRedirect"];
 	delete localStorage.slider;
 	delete localStorage.lastPage;
 	Router.go("/");
-- 
GitLab


From 353cdb4f238e810322303338865fcccb756682dc Mon Sep 17 00:00:00 2001
From: Christopher Fullarton <christopher@fullarton.eu>
Date: Fri, 6 May 2016 12:13:15 +0200
Subject: [PATCH 09/10] Remove console.log

---
 arsnova.click/client/routes.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/arsnova.click/client/routes.js b/arsnova.click/client/routes.js
index 871325084..183063a03 100644
--- a/arsnova.click/client/routes.js
+++ b/arsnova.click/client/routes.js
@@ -136,7 +136,6 @@ Router.route('/translate', function () {
 
 Router.route("/:quizName", {
 	action: function () {
-		console.log(EventManagerCollection.findOne().sessionStatus);
 		if (!EventManagerCollection.findOne() || EventManagerCollection.findOne().sessionStatus !== 2) {
 			try {
 				localData.initializePrivateKey();
-- 
GitLab


From c49ded16c8db46780277b40c4f7786604edc38d3 Mon Sep 17 00:00:00 2001
From: Christopher Fullarton <christopher@fullarton.eu>
Date: Fri, 6 May 2016 12:15:00 +0200
Subject: [PATCH 10/10] Fix gulp errors

---
 .../client/layout/view_hashtag_management/scripts/events.js     | 2 ++
 arsnova.click/client/layout/view_timer/scripts/onRendered.js    | 1 +
 2 files changed, 3 insertions(+)

diff --git a/arsnova.click/client/layout/view_hashtag_management/scripts/events.js b/arsnova.click/client/layout/view_hashtag_management/scripts/events.js
index fb1283db6..37880f9e6 100644
--- a/arsnova.click/client/layout/view_hashtag_management/scripts/events.js
+++ b/arsnova.click/client/layout/view_hashtag_management/scripts/events.js
@@ -14,7 +14,9 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
+
 import {Meteor} from 'meteor/meteor';
+import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {TAPi18n} from 'meteor/tap:i18n';
 import {EventManagerCollection} from '/lib/eventmanager/collection.js';
diff --git a/arsnova.click/client/layout/view_timer/scripts/onRendered.js b/arsnova.click/client/layout/view_timer/scripts/onRendered.js
index 7d7098220..36ede8dda 100644
--- a/arsnova.click/client/layout/view_timer/scripts/onRendered.js
+++ b/arsnova.click/client/layout/view_timer/scripts/onRendered.js
@@ -15,6 +15,7 @@
  * You should have received a copy of the GNU General Public License
  * along with ARSnova Click.  If not, see <http://www.gnu.org/licenses/>.*/
 
+import {Session} from 'meteor/session';
 import {Template} from 'meteor/templating';
 import {Tracker} from 'meteor/tracker';
 import {EventManagerCollection} from '/lib/eventmanager/collection.js';
-- 
GitLab