<commit_before>	// load the master sakai object to access all Sakai OAE API methods
require(["jquery",
         "sakai/sakai.api.core",
         "/devwidgets/lecturelist/javascript/jquery.ui.slider.js"],
        function($, sakai, jqui) {

	console.log({jquery: $});
    /**
     * @name sakai.WIDGET_ID
     *
     * @class WIDGET_ID
     *
     * @description
     * WIDGET DESCRIPTION
     *
     * @version 0.0.1
     * @param {String} tuid Unique id of the widget
     * @param {Boolean} showSettings Show the settings of the widget or not
     */
	sakai_global.lecturelist = function (tuid, showSettings) {

		var ICAL_PROXY_PATH = "/var/proxy/ical.json"

		var LECTURE_ENTRY = $("#lecturelist_templates .entry", $rootel);
		var AGENDA_ROW = $("#lecturelist_templates .agenda-row", $rootel);

        // By default show events from 2 days ago up to 2 weeks in the future
        var DEFAULT_DISPLAY_RANGE = [-2, 14];
        var MIN_SLIDER_DATE = -61;
        var MAX_SLIDER_DATE = 61;

		/////////////////////////////
        // Configuration variables //
        /////////////////////////////

    	// DOM jQuery Objects
        var $rootel = $("#" + tuid);  // unique container for each widget instance
        var $mainContainer = $("#lecturelist_main", $rootel);
        var $settingsContainer = $("#lecturelist_settings", $rootel);
        var settingsForm = $("#lecturelist_settings_form", $rootel);
        var settingsFormTitleField = $("#lecturelist_settings_txtTitle", $rootel);
        var settingsFormUrlField = $("#lecturelist_settings_txtUrl", $rootel);

        // Widget state vars
        var _title = null;
        var _feedUrl = null;
        var _groupedDays = null;

        // Settings state
        var _settingsDateRange = null;

        DAYS = {"0": "Mon",
        		"1": "Tue",
        		"2": "Wed",
        		"3": "Thu",
        		"4": "Fri",
        		"5": "Sat",
        		"6": "Sun"};

        MONTHS = {"0": "Jan",
        		  "1": "Feb",
        		  "2": "Mar",
        		  "3": "Apr",
        		  "4": "May",
        		  "5": "Jun",
        		  "6": "Jul",
        		  "7": "Aug",
        		  "8": "Sep",
        		  "9": "Oct",
        		  "10": "Nov",
        		  "11": "Dec"};

        var TODAY = dateToday();

        /**
         * A class to represent events.
         */
    	function Event(vevent) {

    		function paragraphBreak(text) {
    			// Break the text on blank lines
    			return text.split(/^\s*$/m);
    		}

    		this.vevent = vevent;
    		this.absDate = buildAbsoluteDateString(vevent.DTSTART);
    		this.dayDelta = getDayDelta(TODAY, vevent.DTSTART);
    		this.relDate = buildRelativeDateString(this.dayDelta);
    		this.time = buildTimeString(vevent.DTSTART);
    		this.summary = vevent.SUMMARY || vevent.DESCRIPTION || "";
    		this.description = vevent.DESCRIPTION || vevent.SUMMARY || "";
    		if(this.description == this.summary) {
    			this.description = "";
    		}
    		this.description = paragraphBreak(this.description);

    		// These fields may be undefined
    		this.url = vevent.URL;
    		this.location = vevent.LOCATION;
    		this.contact = vevent.CONTACT;
    	}

        ///////////////////////
        // Utility functions //
        ///////////////////////

        function onStateAvailable(succeeded, state) {
        	if(!succeeded) {
        		alert("Failed to fetch widget state.");
        		return;
        	}

        	_title = state.title;
        	_feedUrl = state.url;
        	fetchCalendarData();
        }

        function fetchCalendarData() {
        	var success = function(data) {
        		if(data.vcalendar && data.vcalendar.vevents) {
        			var events = data.vcalendar.vevents;

        			// Convert event date strings into date objects
        			events = $.map(events, parseEventDates);

        			// Filter the events to just those happening today
        			//events = $.grep(events, notBefore(dateToday()));

        			// Group the events into a list of groups, one for each day
        			_groupedDays = groupByDay(events);

        		}
        		updateCalendar();
        	};

        	$.ajax({
        		url: ICAL_PROXY_PATH,
        		data: {feedurl: _feedUrl},
        		success: success});
        }

        function parseEventDates(event) {
        	event.DTSTART = new Date(event.DTSTART);
        	event.DTEND = new Date(event.DTEND);
        	return event;
        }

        function dateToday() {
        	return stripTime(new Date());
        }
        function dateTomorrow() {
        	var today = dateToday();
        	today.setDate(today.getDate() + 1);
        	return today;
        }

        function notBefore(date) {
        	return function(event) {
        		return event.DTSTART >= date;
        	}
        }

        function between(dateStart, dateEnd) {
        	return function(event) {
        		return event.dtstart >= dateStart && event.dstart < dateEnd;
        	}
        }

        function groupByDay(vevents) {
        	var days = {}
        	for(var i = 0; i < vevents.length; ++i) {
        		var event = vevents[i];
        		// We need a string to key our obj with
        		var dateKey = stripTime(event.DTSTART).toISOString();
        		if(!days[dateKey]) {
        			days[dateKey] = []
        		}
        		days[dateKey].push(new Event(event));
        	}
        	var sortedDays = [];
        	for(key in days) {
        		var events = days[key];
        		events.sort(function(a, b){
        			return a.vevent.DTSTART.milliseconds - b.vevent.DTSTART.milliseconds;
        		});
        		sortedDays.push([key, events]);
        	}
        	sortedDays.sort(function(a, b) {
        		if(a[0] < b[0])
        			return -1;
        		else if(a[0] > b[0])
        			return 1;
        		return 0;
        	});

        	return sortedDays;
        }

        function stripTime(date) {
        	return new Date(date.getFullYear(), date.getMonth(),
        			date.getDate());
        }

        /**
         * Loads widget saved state, calling the callback(success, data)
         * function once the state is loaded.
         */
        function getState(callback) {
        	sakai.api.Widgets.loadWidgetData(tuid, callback);
        }

        /////////////////////////
        // Main View functions //
        /////////////////////////

        /** Called when the calendar data has been updated. */
        function updateCalendar() {

        	var rendered = sakai.api.Util.TemplateRenderer("#agenda_template", {
				title: _title,
				webcalFeedUrl: rewriteHttpUrlToWebcal(_feedUrl),
				days: _groupedDays
			});

        	$(".ajax-content", $rootel).html(rendered);
        	$(".ajax-content .summary.compact", $rootel).toggle(
        			expandCalendarEntry, contractCalendarEntry);
        	$(".loading", $rootel).hide();
        	$(".ajax-content", $rootel).show();

        	$("#title", $rootel).hover(function() {
        		$(this).children().fadeIn();
        	}, function(){
        		$(this).children().fadeOut();
        	})
        }

        function buildTimeString(date) {
        	var hour = date.getHours();
        	var minute = "" + date.getMinutes();
        	if(minute.length === 1)
        		minute = "0" + minute;
        	return hour + ":" + minute;
        }

        // Length of one day in milliseconds
        var DAY_MILLIS = 1000*60*60*24;
        var MAX_DAYS_AGO = 5;

        function getDayDelta(from, to) {
        	// Millisecond time @ start of today
        	var fromms = stripTime(from).getTime();
        	var toms = stripTime(to).getTime();
        	// Calculate number of days between from and to.
        	return Math.floor((toms - fromms) / DAY_MILLIS);
        }

        /**
         * Builds a relative date string from an integer day delta. Day deltas
         * can be calculated by getDayDelta().
         *
         * For example:
         * buildRelativeDateString(0) => "Today"
         * buildRelativeDateString(-1) => "Yesterday"
         * buildRelativeDateString(1) => "Tomorrow"
         * buildRelativeDateString(-10) => "10 days ago"
         * buildRelativeDateString(10) => "In 10 days"
         */
        function buildRelativeDateString(delta) {
        	// make sure we have an integer
        	var days = Math.floor(delta);
        	if(days == 0)
				return "Today";
			else if(days == 1)
				return "Tomorrow";
			else if(days == -1)
				return "Yesterday";
			else if(days < 0)
				return "" + Math.abs(days) + " days ago";
			else
				return "In " + days + " days time";

        }

        /**
         * Builds relative date strings which are even more relative than
         * buildRelativeDateString() in that it doesn't refer to Today/Yesterday
         * etc which could confuse people in the context of choosing a general
         * sliding time window to show events inside.
         */
        function buildVeryRelativeDateString(delta) {
        	var days = Math.floor(delta);
        	if(days == 0)
        		return "the present day";
        	else if(days < 0)
        		return "" + Math.abs(days) + " days in the past";
        	else // days > 0
        		return "" + days + " days in the future";
        }

        function buildAbsoluteDateString(date) {
        	var dayName = DAYS[date.getDay()];
        	var dayNumber = date.getDate();
        	var monthName = MONTHS[date.getMonth()];
        	return dayName + " " + dayNumber + " " + monthName;
        }

        function expandCalendarEntry(jqevent) {
        	var summary = $(this);
        	var expanded = summary.siblings(".full");

        	summary.removeClass("compact expandable").addClass("contractable");
        	expanded.slideDown();
        }

        function contractCalendarEntry(jqevent) {
        	var summary = $(this);
        	var expanded = summary.siblings(".full");

        	summary.addClass("compact expandable").removeClass("contractable");
        	expanded.slideUp();
        }

        /////////////////////////////
        // Settings View functions //
        /////////////////////////////

        /**
         * Watch for value changes to the settings URL field in order to rewrite
         * webcal:// urls to http://.
         */
        settingsFormUrlField.change(function() {
        	var urltext = $(this).val();
        	// Help people inputting webcal:// links by rewriting them to http
        	urltext = rewriteWebcalUrlToHttp(urltext);
        	$(this).val(urltext);
        });

        function rewriteWebcalUrlToHttp(url) {
        	return url.replace(/^webcal:\/\//, "http://");
        }

        function rewriteHttpUrlToWebcal(url) {
        	return url.replace(/^http:\/\//, "webcal://");
        }

        function onWidgetSettingsStateAvailable(success, state) {
        	if(success) {
        		var title = state.title;
        		var url = state.url;
        		if(state.daysFrom && state.daysTo)
        			_settingsDateRange = [state.daysFrom, state.daysTo];
        	}
        	else {
        		alert("Error fetching saved settings");
        	}
        	settingsFormTitleField.val(title || "");
        	settingsFormUrlField.val(url || "");
        	setupRangeSlider($("#daterangeslider", $rootel),
            		settingsHandleRangeSlide);
        	$("#daterangeslider", $rootel).slider("values", _settingsDateRange || DEFAULT_DISPLAY_RANGE)
        }

        /** Add listener to setting form submit */
        function settingsSave() {
        	var state = {
        			title: settingsFormTitleField.val(),
        			url: settingsFormUrlField.val(),
        			daysFrom: _settingsDateRange[0],
        			daysTo: _settingsDateRange[1]
        	};

        	// async save our widget's state
        	sakai.api.Widgets.saveWidgetData(tuid, state,
        			onWidgetSettingsDataSaved);
        }

        function onWidgetSettingsDataSaved(success, data) {
        	if (success) {
        		// Settings finished, switch to Main view
        		sakai.api.Widgets.Container.informFinish(tuid, "lecturelist");
        	} else {
        		sakai.api.Util.notification.show("Couldn't Save Your Settings",
        				"An error prevented your settings from being saved. "
        				+ " Please try again.",
        				sakai.api.Util.notification.type.ERROR);
        	}
        }

        /////////////////////////////
        // Initialisation function //
        /////////////////////////////

        function setupRangeSlider(container, slideFunc) {
        	$("#daterangeslider", $rootel).slider({
        		range: true,
        		min: MIN_SLIDER_DATE,
        		max: MAX_SLIDER_DATE,
        		values: _settingsDateRange || DEFAULT_DISPLAY_RANGE,
        		slide: slideFunc,
        		change: slideFunc
        	});
        }

        function settingsHandleRangeSlide(event, ui) {
        	_settingsDateRange = ui.values;
        	var from = ui.values[0];
        	var to = ui.values[1];

        	var fromString = from <= MIN_SLIDER_DATE ? "any date in the past"
        			: buildVeryRelativeDateString(from);
        	var toString = to >= MAX_SLIDER_DATE ? "any date in the future"
        			: buildVeryRelativeDateString(to);

        	$("#lecturelist_settings_daterangeslider_label .from", $rootel)
        		.text(fromString);
        	$("#lecturelist_settings_daterangeslider_label .to", $rootel)
        		.text(toString);
        }

        /**
         * Initialization function DOCUMENTATION
         */
        var doInit = function () {

            if (showSettings) {
            	// Setup validation/save handler on save button
            	var validateOpts = { submitHandler: settingsSave };
                sakai.api.Util.Forms.validate(settingsForm, validateOpts, true);

                $("#lecturelist_settings_save", $rootel).click(function() {
                	settingsForm.submit();
                });
                // Hook up the cancel button
                $("#lecturelist_settings_cancel", $rootel).click(function(){
            		sakai.api.Widgets.Container.informCancel(
            				tuid, "lecturelist");
            	});

                // Async fetch widget settings to populate form
                getState(onWidgetSettingsStateAvailable);

                // show the Settings view
                $settingsContainer.show();
            } else {
            	// set up Main view

            	// Async fetch widget settings to populate form
                getState(onStateAvailable);

            	$mainContainer.show();
            	$(".loading", $rootel).fadeIn(1000);
            }
        };

        // run the initialization function when the widget object loads
        doInit();
    };

    // inform Sakai OAE that this widget has loaded and is ready to run
    sakai.api.Widgets.widgetLoader.informOnLoad("lecturelist");
});
<commit_msg>Remove some cruft.<commit_after>	// load the master sakai object to access all Sakai OAE API methods
require(["jquery",
         "sakai/sakai.api.core",
         "/devwidgets/lecturelist/javascript/jquery.ui.slider.js"],
        function($, sakai) {

	console.log({jquery: $});
    /**
     * @name sakai.WIDGET_ID
     *
     * @class WIDGET_ID
     *
     * @description
     * WIDGET DESCRIPTION
     *
     * @version 0.0.1
     * @param {String} tuid Unique id of the widget
     * @param {Boolean} showSettings Show the settings of the widget or not
     */
	sakai_global.lecturelist = function (tuid, showSettings) {

		var ICAL_PROXY_PATH = "/var/proxy/ical.json"

		var LECTURE_ENTRY = $("#lecturelist_templates .entry", $rootel);
		var AGENDA_ROW = $("#lecturelist_templates .agenda-row", $rootel);

        // By default show events from 2 days ago up to 2 weeks in the future
        var DEFAULT_DISPLAY_RANGE = [-2, 14];
        var MIN_SLIDER_DATE = -61;
        var MAX_SLIDER_DATE = 61;

		/////////////////////////////
        // Configuration variables //
        /////////////////////////////

    	// DOM jQuery Objects
        var $rootel = $("#" + tuid);  // unique container for each widget instance
        var $mainContainer = $("#lecturelist_main", $rootel);
        var $settingsContainer = $("#lecturelist_settings", $rootel);
        var settingsForm = $("#lecturelist_settings_form", $rootel);
        var settingsFormTitleField = $("#lecturelist_settings_txtTitle", $rootel);
        var settingsFormUrlField = $("#lecturelist_settings_txtUrl", $rootel);

        // Widget state vars
        var _title = null;
        var _feedUrl = null;
        var _groupedDays = null;

        // Settings state
        var _settingsDateRange = null;

        DAYS = {"0": "Mon",
        		"1": "Tue",
        		"2": "Wed",
        		"3": "Thu",
        		"4": "Fri",
        		"5": "Sat",
        		"6": "Sun"};

        MONTHS = {"0": "Jan",
        		  "1": "Feb",
        		  "2": "Mar",
        		  "3": "Apr",
        		  "4": "May",
        		  "5": "Jun",
        		  "6": "Jul",
        		  "7": "Aug",
        		  "8": "Sep",
        		  "9": "Oct",
        		  "10": "Nov",
        		  "11": "Dec"};

        var TODAY = dateToday();

        /**
         * A class to represent events.
         */
    	function Event(vevent) {

    		function paragraphBreak(text) {
    			// Break the text on blank lines
    			return text.split(/^\s*$/m);
    		}

    		this.vevent = vevent;
    		this.absDate = buildAbsoluteDateString(vevent.DTSTART);
    		this.dayDelta = getDayDelta(TODAY, vevent.DTSTART);
    		this.relDate = buildRelativeDateString(this.dayDelta);
    		this.time = buildTimeString(vevent.DTSTART);
    		this.summary = vevent.SUMMARY || vevent.DESCRIPTION || "";
    		this.description = vevent.DESCRIPTION || vevent.SUMMARY || "";
    		if(this.description == this.summary) {
    			this.description = "";
    		}
    		this.description = paragraphBreak(this.description);

    		// These fields may be undefined
    		this.url = vevent.URL;
    		this.location = vevent.LOCATION;
    		this.contact = vevent.CONTACT;
    	}

        ///////////////////////
        // Utility functions //
        ///////////////////////

        function onStateAvailable(succeeded, state) {
        	if(!succeeded) {
        		alert("Failed to fetch widget state.");
        		return;
        	}

        	_title = state.title;
        	_feedUrl = state.url;
        	fetchCalendarData();
        }

        function fetchCalendarData() {
        	var success = function(data) {
        		if(data.vcalendar && data.vcalendar.vevents) {
        			var events = data.vcalendar.vevents;

        			// Convert event date strings into date objects
        			events = $.map(events, parseEventDates);

        			// Filter the events to just those happening today
        			//events = $.grep(events, notBefore(dateToday()));

        			// Group the events into a list of groups, one for each day
        			_groupedDays = groupByDay(events);

        		}
        		updateCalendar();
        	};

        	$.ajax({
        		url: ICAL_PROXY_PATH,
        		data: {feedurl: _feedUrl},
        		success: success});
        }

        function parseEventDates(event) {
        	event.DTSTART = new Date(event.DTSTART);
        	event.DTEND = new Date(event.DTEND);
        	return event;
        }

        function dateToday() {
        	return stripTime(new Date());
        }
        function dateTomorrow() {
        	var today = dateToday();
        	today.setDate(today.getDate() + 1);
        	return today;
        }

        function notBefore(date) {
        	return function(event) {
        		return event.DTSTART >= date;
        	}
        }

        function between(dateStart, dateEnd) {
        	return function(event) {
        		return event.dtstart >= dateStart && event.dstart < dateEnd;
        	}
        }

        function groupByDay(vevents) {
        	var days = {}
        	for(var i = 0; i < vevents.length; ++i) {
        		var event = vevents[i];
        		// We need a string to key our obj with
        		var dateKey = stripTime(event.DTSTART).toISOString();
        		if(!days[dateKey]) {
        			days[dateKey] = []
        		}
        		days[dateKey].push(new Event(event));
        	}
        	var sortedDays = [];
        	for(key in days) {
        		var events = days[key];
        		events.sort(function(a, b){
        			return a.vevent.DTSTART.milliseconds - b.vevent.DTSTART.milliseconds;
        		});
        		sortedDays.push([key, events]);
        	}
        	sortedDays.sort(function(a, b) {
        		if(a[0] < b[0])
        			return -1;
        		else if(a[0] > b[0])
        			return 1;
        		return 0;
        	});

        	return sortedDays;
        }

        function stripTime(date) {
        	return new Date(date.getFullYear(), date.getMonth(),
        			date.getDate());
        }

        /**
         * Loads widget saved state, calling the callback(success, data)
         * function once the state is loaded.
         */
        function getState(callback) {
        	sakai.api.Widgets.loadWidgetData(tuid, callback);
        }

        /////////////////////////
        // Main View functions //
        /////////////////////////

        /** Called when the calendar data has been updated. */
        function updateCalendar() {

        	var rendered = sakai.api.Util.TemplateRenderer("#agenda_template", {
				title: _title,
				webcalFeedUrl: rewriteHttpUrlToWebcal(_feedUrl),
				days: _groupedDays
			});

        	$(".ajax-content", $rootel).html(rendered);
        	$(".ajax-content .summary.compact", $rootel).toggle(
        			expandCalendarEntry, contractCalendarEntry);
        	$(".loading", $rootel).hide();
        	$(".ajax-content", $rootel).show();

        	$("#title", $rootel).hover(function() {
        		$(this).children().fadeIn();
        	}, function(){
        		$(this).children().fadeOut();
        	})
        }

        function buildTimeString(date) {
        	var hour = date.getHours();
        	var minute = "" + date.getMinutes();
        	if(minute.length === 1)
        		minute = "0" + minute;
        	return hour + ":" + minute;
        }

        // Length of one day in milliseconds
        var DAY_MILLIS = 1000*60*60*24;
        var MAX_DAYS_AGO = 5;

        function getDayDelta(from, to) {
        	// Millisecond time @ start of today
        	var fromms = stripTime(from).getTime();
        	var toms = stripTime(to).getTime();
        	// Calculate number of days between from and to.
        	return Math.floor((toms - fromms) / DAY_MILLIS);
        }

        /**
         * Builds a relative date string from an integer day delta. Day deltas
         * can be calculated by getDayDelta().
         *
         * For example:
         * buildRelativeDateString(0) => "Today"
         * buildRelativeDateString(-1) => "Yesterday"
         * buildRelativeDateString(1) => "Tomorrow"
         * buildRelativeDateString(-10) => "10 days ago"
         * buildRelativeDateString(10) => "In 10 days"
         */
        function buildRelativeDateString(delta) {
        	// make sure we have an integer
        	var days = Math.floor(delta);
        	if(days == 0)
				return "Today";
			else if(days == 1)
				return "Tomorrow";
			else if(days == -1)
				return "Yesterday";
			else if(days < 0)
				return "" + Math.abs(days) + " days ago";
			else
				return "In " + days + " days time";

        }

        /**
         * Builds relative date strings which are even more relative than
         * buildRelativeDateString() in that it doesn't refer to Today/Yesterday
         * etc which could confuse people in the context of choosing a general
         * sliding time window to show events inside.
         */
        function buildVeryRelativeDateString(delta) {
        	var days = Math.floor(delta);
        	if(days == 0)
        		return "the present day";
        	else if(days < 0)
        		return "" + Math.abs(days) + " days in the past";
        	else // days > 0
        		return "" + days + " days in the future";
        }

        function buildAbsoluteDateString(date) {
        	var dayName = DAYS[date.getDay()];
        	var dayNumber = date.getDate();
        	var monthName = MONTHS[date.getMonth()];
        	return dayName + " " + dayNumber + " " + monthName;
        }

        function expandCalendarEntry(jqevent) {
        	var summary = $(this);
        	var expanded = summary.siblings(".full");

        	summary.removeClass("compact expandable").addClass("contractable");
        	expanded.slideDown();
        }

        function contractCalendarEntry(jqevent) {
        	var summary = $(this);
        	var expanded = summary.siblings(".full");

        	summary.addClass("compact expandable").removeClass("contractable");
        	expanded.slideUp();
        }

        /////////////////////////////
        // Settings View functions //
        /////////////////////////////

        /**
         * Watch for value changes to the settings URL field in order to rewrite
         * webcal:// urls to http://.
         */
        settingsFormUrlField.change(function() {
        	var urltext = $(this).val();
        	// Help people inputting webcal:// links by rewriting them to http
        	urltext = rewriteWebcalUrlToHttp(urltext);
        	$(this).val(urltext);
        });

        function rewriteWebcalUrlToHttp(url) {
        	return url.replace(/^webcal:\/\//, "http://");
        }

        function rewriteHttpUrlToWebcal(url) {
        	return url.replace(/^http:\/\//, "webcal://");
        }

        function onWidgetSettingsStateAvailable(success, state) {
        	if(success) {
        		var title = state.title;
        		var url = state.url;
        		if(state.daysFrom && state.daysTo)
        			_settingsDateRange = [state.daysFrom, state.daysTo];
        	}
        	else {
        		alert("Error fetching saved settings");
        	}
        	settingsFormTitleField.val(title || "");
        	settingsFormUrlField.val(url || "");
        	setupRangeSlider($("#daterangeslider", $rootel),
            		settingsHandleRangeSlide);
        	$("#daterangeslider", $rootel).slider("values", _settingsDateRange || DEFAULT_DISPLAY_RANGE)
        }

        /** Add listener to setting form submit */
        function settingsSave() {
        	var state = {
        			title: settingsFormTitleField.val(),
        			url: settingsFormUrlField.val(),
        			daysFrom: _settingsDateRange[0],
        			daysTo: _settingsDateRange[1]
        	};

        	// async save our widget's state
        	sakai.api.Widgets.saveWidgetData(tuid, state,
        			onWidgetSettingsDataSaved);
        }

        function onWidgetSettingsDataSaved(success, data) {
        	if (success) {
        		// Settings finished, switch to Main view
        		sakai.api.Widgets.Container.informFinish(tuid, "lecturelist");
        	} else {
        		sakai.api.Util.notification.show("Couldn't Save Your Settings",
        				"An error prevented your settings from being saved. "
        				+ " Please try again.",
        				sakai.api.Util.notification.type.ERROR);
        	}
        }

        /////////////////////////////
        // Initialisation function //
        /////////////////////////////

        function setupRangeSlider(container, slideFunc) {
        	$("#daterangeslider", $rootel).slider({
        		range: true,
        		min: MIN_SLIDER_DATE,
        		max: MAX_SLIDER_DATE,
        		values: _settingsDateRange || DEFAULT_DISPLAY_RANGE,
        		slide: slideFunc,
        		change: slideFunc
        	});
        }

        function settingsHandleRangeSlide(event, ui) {
        	_settingsDateRange = ui.values;
        	var from = ui.values[0];
        	var to = ui.values[1];

        	var fromString = from <= MIN_SLIDER_DATE ? "any date in the past"
        			: buildVeryRelativeDateString(from);
        	var toString = to >= MAX_SLIDER_DATE ? "any date in the future"
        			: buildVeryRelativeDateString(to);

        	$("#lecturelist_settings_daterangeslider_label .from", $rootel)
        		.text(fromString);
        	$("#lecturelist_settings_daterangeslider_label .to", $rootel)
        		.text(toString);
        }

        /**
         * Initialization function DOCUMENTATION
         */
        var doInit = function () {

            if (showSettings) {
            	// Setup validation/save handler on save button
            	var validateOpts = { submitHandler: settingsSave };
                sakai.api.Util.Forms.validate(settingsForm, validateOpts, true);

                $("#lecturelist_settings_save", $rootel).click(function() {
                	settingsForm.submit();
                });
                // Hook up the cancel button
                $("#lecturelist_settings_cancel", $rootel).click(function(){
            		sakai.api.Widgets.Container.informCancel(
            				tuid, "lecturelist");
            	});

                // Async fetch widget settings to populate form
                getState(onWidgetSettingsStateAvailable);

                // show the Settings view
                $settingsContainer.show();
            } else {
            	// set up Main view

            	// Async fetch widget settings to populate form
                getState(onStateAvailable);

            	$mainContainer.show();
            	$(".loading", $rootel).fadeIn(1000);
            }
        };

        // run the initialization function when the widget object loads
        doInit();
    };

    // inform Sakai OAE that this widget has loaded and is ready to run
    sakai.api.Widgets.widgetLoader.informOnLoad("lecturelist");
});
<|endoftext|>


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

/**
 * UserMetricsController - Controller to handle user metric data
 *
 */
var UserMetricsController = (function(dashboardId) {

  var observable = new Event('UserMetricsController');
  var service = Service.get(Service.types.userMetrics, dashboardId);
  var _config;

  function getUserMetrics(response) {
    var model;

    if(response) {
      response = JSON.parse(response);
      model = {};
      if(response.length && response.length > 0) {
        model.metrics = {
          rtActiveUsers: 0,
          lastVersionActiveUsers: 0,
          sevenDayUsers: 0
        };

        var last_versions = {};

        response.forEach(function(metric) {
          if(metric.name === 'activeUsers') {
            model.metrics.rtActiveUsers += parseInt(metric.value);
            if(!metric.appVersion || !metric.appVersion.match(_lastVersion)) {
              return;
            } else if (metric.appVersion === last_versions[metric.viewId + (metric.platform || '')]) {
              model.metrics.lastVersionActiveUsers += parseInt(metric.value);
            } else if(!last_versions[metric.viewId + (metric.platform || '')] || Utils.compareVersions(metric.appVersion, last_versions[metric.viewId + (metric.platform || '')], _lastVersion) > 0) {
              last_versions[metric.viewId + (metric.platform || '')] = metric.appVersion;
              model.metrics.lastVersionActiveUsers = parseInt(metric.value);
            }
          }
          if(metric.name === '7dayUsers') {
            model.metrics.sevenDayUsers += parseInt(metric.value);
          }
        }, this);

        model.metrics.oldVerisonsActiveUsersRate = parseFloat((100 * (model.metrics.rtActiveUsers - model.metrics.lastVersionActiveUsers) / model.metrics.rtActiveUsers).toFixed(2));
      }
    }

    observable.notify(model);
  }

  this.observable = observable;
  this.dispose = function() {
    this.observable.reset();
    service.removeListener(getUserMetrics);
  };
  this.init = function(config) {
    if(!config.analyticViews || !config.analyticViews.length) {
      return Promise.reject();
    }
    _lastVersion = new RegExp(config.lastVersion);
    service.addListener(getUserMetrics);
  };

});<commit_msg>refactor(user-metrics): only display old versions active users rate if app has versions<commit_after>/*
 * Copyright 2017 Banco Bilbao Vizcaya Argentaria, S.A.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * UserMetricsController - Controller to handle user metric data
 *
 */
var UserMetricsController = (function(dashboardId) {

  var observable = new Event('UserMetricsController');
  var service = Service.get(Service.types.userMetrics, dashboardId);
  var _config;

  function getUserMetrics(response) {
    var model;

    if(response) {
      response = JSON.parse(response);
      model = {};
      if(response.length && response.length > 0) {
        model.metrics = {
          rtActiveUsers: 0,
          sevenDayUsers: 0
        };

        var last_versions = {};
        var lastVersionActiveUsers;

        response.forEach(function(metric) {
          if(metric.name === 'activeUsers') {
            model.metrics.rtActiveUsers += parseInt(metric.value);
            if(!metric.appVersion || !metric.appVersion.match(_lastVersion)) {
              return;
            } else if (metric.appVersion === last_versions[metric.viewId + (metric.platform || '')]) {
              lastVersionActiveUsers += parseInt(metric.value);
            } else if(!last_versions[metric.viewId + (metric.platform || '')] || Utils.compareVersions(metric.appVersion, last_versions[metric.viewId + (metric.platform || '')], _lastVersion) > 0) {
              last_versions[metric.viewId + (metric.platform || '')] = metric.appVersion;
              lastVersionActiveUsers = parseInt(metric.value);
            }
          }
          if(metric.name === '7dayUsers') {
            model.metrics.sevenDayUsers += parseInt(metric.value);
          }
        }, this);

        model.metrics.oldVerisonsActiveUsersRate = lastVersionActiveUsers !== undefined  ? parseFloat((100 * (model.metrics.rtActiveUsers - lastVersionActiveUsers) / model.metrics.rtActiveUsers).toFixed(2)) : undefined;
      }
    }

    observable.notify(model);
  }

  this.observable = observable;
  this.dispose = function() {
    this.observable.reset();
    service.removeListener(getUserMetrics);
  };
  this.init = function(config) {
    if(!config.analyticViews || !config.analyticViews.length) {
      return Promise.reject();
    }
    _lastVersion = new RegExp(config.lastVersion);
    service.addListener(getUserMetrics);
  };

});<|endoftext|>


<commit_before>// Copyright 2014 Mike Tsao <mike@sowbug.com>

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

'use strict';

var walletAppController = function($scope,
                                   $http,
                                   settings,
                                   credentials,
                                   wallet,
                                   electrum) {
  $scope.settings = settings;
  $scope.credentials = credentials;
  $scope.wallet = wallet;
  $scope.electrum = electrum;

  // For some crazy reason, angularjs won't reflect view changes in
  // the model's scope-level objects, so I have to create another
  // object and hang stuff off it. I picked w for wallet.
  $scope.w = {};
  $scope.w.showPrivateKey = false;
  $scope.w.selectedAddress = undefined;

  if (chrome && chrome.runtime) {
    var manifest = chrome.runtime.getManifest();
    $scope.app_name = manifest.name;
    $scope.app_version = "v" + manifest.version;
  } else {
    $scope.app_name = "[name]";
    $scope.app_version = "v0.0.0.0";
  }

  $scope.addPostLoadWatchers = function() {
    // It's important not to add these watchers before initial load.
    $scope.$watch("settings", function(newVal, oldVal) {
      if (newVal != oldVal) {
        $scope.settings.save();
      }
    }, true);

    $scope.$watch("credentials", function(newVal, oldVal) {
      if (newVal != oldVal) {
        $scope.credentials.save();
      }
    }, true);

    $scope.$watch("wallet", function(newVal, oldVal) {
      if (newVal != oldVal) {
        // A small hack: if the wallet ever changes, it's a good
        // time to hide this checkbox.
        $scope.w.showPrivateKey = false;
        $scope.wallet.save();
      }
    }, true);

    $scope.$watchCollection('getAccounts()', function(newItems, oldItems) {
      if (newItems != oldItems) {
        $scope.wallet.save();
      }
    });
  };

  $scope.startLoading = function() {
    $scope.settings.load().then(function() {
      return $scope.credentials.load();
    }).then(function() {
      return $scope.wallet.load();
    }).then(function() {
      $scope.$apply();
      $scope.addPostLoadWatchers();
    }).catch(function(err) {
      console.log("error", err);
    });
  };

  $scope.$watch('getAccountCount()', function(newVal, oldVal) {
    if (newVal > 0 && oldVal == 0) {
      $scope.selectFirstAccount();
    }
  });

  $scope.$watch(
    'getWalletKeyFingerprint()',
    function(newVal, oldVal) {
      if (newVal != oldVal) {
        if (newVal) {
          var xhr = new XMLHttpRequest();
          xhr.open('GET', 'http://robohash.org/' + newVal +
                   '.png?set=set3&bgset=any&size=64x64', true);
          xhr.responseType = 'blob';
          xhr.onload = function(e) {
            $("#master-key-fingerprint-img").attr(
              "src",
              window.webkitURL.createObjectURL(this.response));
          };
          xhr.send();
        } else {
          $("#master-key-fingerprint-img").attr("src", "");
        }
      }
    }
  );

  $scope.$watch(
    'getCurrentAccountFingerprint()',
    function(newVal, oldVal) {
      if (newVal != oldVal) {
        if (newVal) {
          var xhr = new XMLHttpRequest();
          xhr.open('GET', 'http://robohash.org/' + newVal +
                   '.png?set=set3&bgset=any&size=64x64', true);
          xhr.responseType = 'blob';
          xhr.onload = function(e) {
            $("#account-fingerprint-img").attr(
              "src",
              window.webkitURL.createObjectURL(this.response));
          };
          xhr.send();
        } else {
          $("#account-fingerprint-img").attr("src", "");
        }
      }
    });

  $scope.newMasterKey = function() {
    $scope.wallet.addRandomMasterKey()
      .then(function() {
        $scope.$apply();
      });
  };

  $scope.importMasterKey = function() {
    $scope.wallet.importMasterKey($scope.w.importMasterKey)
      .then(function() {
        $scope.w.importMasterKey = null;
        $("#import-master-key-modal").modal('hide');
        $scope.$apply();
      }.bind(this),
            function() { console.log("nope"); }.bind(this));
  };

  $scope.removeMasterKey = function() {
    // TODO(miket): ideally we'll track whether this key was backed
    // up, and make this button available only if yes. Then we'll
    // confirm up the wazoo before actually deleting.
    //
    // Less of a big deal if the master key is public.
    $scope.wallet.removeMasterKey();
  };

  $scope.nextAccountName = function() {
    return "Account " + $scope.wallet.getNextAccountNumber();
  };

  $scope.deriveNextAccount = function() {
    $scope.wallet.deriveNextAccount(false, function(succeeded) {
      if (succeeded) {
        $scope.$apply();
      }
    });
  };

  $scope.watchNextAccount = function() {
    $scope.wallet.deriveNextAccount(true, function(succeeded) {
      if (succeeded) {
        $scope.$apply();
      }
    });
  };

  $scope.unlockWallet = function() {
    var unlockSuccessful = function() {
      $("#unlock-wallet-modal").modal('hide');
      $scope.w.passphraseNew = null;
      $scope.$apply();
    };
    var unlockFailed = function() {
      $scope.w.passphraseNew = null;
      $scope.$apply();
    };
    var relockCallback = function() {
      //$scope.w.showPrivateKey = false;
      $scope.$apply();
    };

    $scope.credentials.unlock($scope.w.passphraseNew,
                              relockCallback.bind(this))
      .then(unlockSuccessful, unlockFailed);
  };

  $scope.lockWallet = function() {
    $scope.credentials.lock(function() {
      $scope.$apply();
    });
  };

  $scope.setPassphrase = function() {
    // TODO: angularjs can probably do this check for us
    if (!$scope.w.passphraseNew || $scope.w.passphraseNew.length == 0) {
      console.log("missing new passphrase");
      return;
    }
    if ($scope.w.passphraseNew != $scope.w.passphraseConfirm) {
      console.log("new didn't match confirm:" + $scope.w.passphraseNew);
      return;
    }

    $scope.credentials.setPassphrase($scope.w.passphraseNew,
                                     function() {
                                       $scope.$apply();
                                     }).then(function() {
                                       $scope.$apply();
                                     });

    // We don't want these items lurking in the DOM.
    $scope.w.passphraseNew = null;
    $scope.w.passphraseConfirm = null;
  };

  $scope.reinitializeEverything = function() {
    $scope.settings.init();
    $scope.credentials.init();
    $scope.wallet.init();
  };

  $scope.clearEverything = function() {
    // TODO(miket): confirmation
    $scope.reinitializeEverything();
    clearAllStorage();
    chrome.runtime.reload();
  };

  $scope.isPassphraseSet = function() {
    return $scope.credentials.isPassphraseSet();
  };

  $scope.isWalletUnlocked = function() {
    return $scope.isPassphraseSet() && !$scope.credentials.isWalletLocked();
  };

  $scope.isWalletKeySet = function() {
    return $scope.wallet.isKeySet();
  };

  $scope.isWalletKeyPrivateSet = function() {
    return $scope.wallet.isExtendedPrivateSet();
  };

  $scope.getWalletKeyPrivate = function() {
    return $scope.wallet.getExtendedPrivateBase58();
  };

  $scope.getWalletKeyPublic = function() {
    return $scope.wallet.getExtendedPublicBase58();
  };

  $scope.getWalletKeyFingerprint = function() {
    return $scope.wallet.getFingerprint();
  };

  $scope.getAccountCount = function() {
    return $scope.wallet.getAccountCount();
  };

  $scope.getAccounts = function() {
    return $scope.wallet.getAccounts();
  };

  $scope.getPublicAddresses = function() {
    return $scope.wallet.publicAddresses;
  }

  $scope.getChangeAddresses = function() {
    return $scope.wallet.changeAddresses;
  }

  $scope.getAddressBalance = function(addr_b58) {
    return $scope.wallet.watchedAddresses[addr_b58].value;
  }

  $scope.getCurrentAccount = function() {
    return $scope.w.currentAccount;
  };

  $scope.getCurrentAccountFingerprint = function() {
    if (!$scope.getCurrentAccount()) {
      return null;
    }
    return $scope.getCurrentAccount().fingerprint;
  };

  $scope.areRequestsPending = function() {
    return $scope.electrum.areRequestsPending();
  };

  $scope.setCurrentAccountByIndex = function(index) {
    if ($scope.w.currentAccount == $scope.getAccounts()[index]) {
      return;
    }
    $scope.w.currentAccount = $scope.getAccounts()[index];
   // $scope.refreshAccount();
  };

  $scope.selectFirstAccount = function() {
    if ($scope.getAccountCount() > 0) {
      $scope.setCurrentAccountByIndex(0);
    }
  };

  $scope.exportMasterKey = function() {
    chrome.fileSystem.chooseEntry(
      {'type': 'saveFile',
       'suggestedName': 'Happynine-Export-' +
       $scope.getWalletKeyFingerprint() + '.txt',
      },
      function(entry) {
        var errorHandler = function(e) {
          console.log(e);
        };
        entry.createWriter(function(writer) {
          writer.onerror = errorHandler;
          writer.onwriteend = function(e) {
            console.log('write complete');
          };
          var message =
            "Happynine: BIP 0038 Master Key Export\r\n" +
            $scope.getWalletKeyFingerprint() + ": " +
            $scope.getWalletKeyPublic() + "\r\n" +
            "Private Key [NOT YET IMPLEMENTED]\r\n\r\n" +
            "THIS IS NOT A BACKUP OF YOUR KEY! THAT FEATURE IS COMING.\r\n";
          message += "Meanwhile, here is a proof of concept text " +
            "QR code (NOT YOURS). \r\n\r\n";
          var qr = $('

');
          qr.qrcode({'text': $scope.getWalletKeyPublic(), 'render': 'text'});
          message += qr.text();
          var blob = new Blob([message], {type: 'text/plain; charset=utf-8'});
          writer.write(blob);
        }, errorHandler);
      });
  };

  $scope.send = function() {
    var sendTo = $scope.w.sendTo;
    var sendValue = $scope.unitToSatoshi($scope.w.sendValue);
    var sendFee = $scope.unitToSatoshi($scope.w.sendFee);

    if (sendValue > 0 && sendFee > 0 && sendTo.length > 25) {
      console.log("send", sendTo, sendValue, $scope.unitLabel(),
                  "for", sendFee);
      $scope.wallet.sendFunds(sendTo, sendValue, sendFee)
        .then(function() {
          $scope.$apply();
        });
    }
  };

  $scope.setAddress = function(a) {
    console.log("called", a);
    $scope.w.selectedAddress = a;
    $('#qrcode').qrcode({
      'width': 128,
      'height': 128,
      'text': a});
  };

  $scope.satoshiToUnit = function(satoshis) {
    return $scope.settings.satoshiToUnit(satoshis);
  };

  $scope.unitToSatoshi = function(units) {
    return $scope.settings.unitToSatoshi(units);
  };

  $scope.unitLabel = function() {
    return $scope.settings.unitLabel();
  };

  // TODO(miket): this might be a race with moduleDidLoad.
  var listenerDiv = document.getElementById('listener');
  listenerDiv.addEventListener('load', function() {
    $scope.startLoading();
  }, true);
};
<commit_msg>Multiple QR codes<commit_after>// Copyright 2014 Mike Tsao <mike@sowbug.com>

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

'use strict';

var walletAppController = function($scope,
                                   $http,
                                   settings,
                                   credentials,
                                   wallet,
                                   electrum) {
  $scope.settings = settings;
  $scope.credentials = credentials;
  $scope.wallet = wallet;
  $scope.electrum = electrum;

  // For some crazy reason, angularjs won't reflect view changes in
  // the model's scope-level objects, so I have to create another
  // object and hang stuff off it. I picked w for wallet.
  $scope.w = {};
  $scope.w.showPrivateKey = false;
  $scope.w.selectedAddress = undefined;

  if (chrome && chrome.runtime) {
    var manifest = chrome.runtime.getManifest();
    $scope.app_name = manifest.name;
    $scope.app_version = "v" + manifest.version;
  } else {
    $scope.app_name = "[name]";
    $scope.app_version = "v0.0.0.0";
  }

  $scope.addPostLoadWatchers = function() {
    // It's important not to add these watchers before initial load.
    $scope.$watch("settings", function(newVal, oldVal) {
      if (newVal != oldVal) {
        $scope.settings.save();
      }
    }, true);

    $scope.$watch("credentials", function(newVal, oldVal) {
      if (newVal != oldVal) {
        $scope.credentials.save();
      }
    }, true);

    $scope.$watch("wallet", function(newVal, oldVal) {
      if (newVal != oldVal) {
        // A small hack: if the wallet ever changes, it's a good
        // time to hide this checkbox.
        $scope.w.showPrivateKey = false;
        $scope.wallet.save();
      }
    }, true);

    $scope.$watchCollection('getAccounts()', function(newItems, oldItems) {
      if (newItems != oldItems) {
        $scope.wallet.save();
      }
    });
  };

  $scope.startLoading = function() {
    $scope.settings.load().then(function() {
      return $scope.credentials.load();
    }).then(function() {
      return $scope.wallet.load();
    }).then(function() {
      $scope.$apply();
      $scope.addPostLoadWatchers();
    }).catch(function(err) {
      console.log("error", err);
    });
  };

  $scope.$watch('getAccountCount()', function(newVal, oldVal) {
    if (newVal > 0 && oldVal == 0) {
      $scope.selectFirstAccount();
    }
  });

  $scope.$watch(
    'getWalletKeyFingerprint()',
    function(newVal, oldVal) {
      if (newVal != oldVal) {
        if (newVal) {
          var xhr = new XMLHttpRequest();
          xhr.open('GET', 'http://robohash.org/' + newVal +
                   '.png?set=set3&bgset=any&size=64x64', true);
          xhr.responseType = 'blob';
          xhr.onload = function(e) {
            $("#master-key-fingerprint-img").attr(
              "src",
              window.webkitURL.createObjectURL(this.response));
          };
          xhr.send();
        } else {
          $("#master-key-fingerprint-img").attr("src", "");
        }
      }
    }
  );

  $scope.$watch(
    'getCurrentAccountFingerprint()',
    function(newVal, oldVal) {
      if (newVal != oldVal) {
        if (newVal) {
          var xhr = new XMLHttpRequest();
          xhr.open('GET', 'http://robohash.org/' + newVal +
                   '.png?set=set3&bgset=any&size=64x64', true);
          xhr.responseType = 'blob';
          xhr.onload = function(e) {
            $("#account-fingerprint-img").attr(
              "src",
              window.webkitURL.createObjectURL(this.response));
          };
          xhr.send();
        } else {
          $("#account-fingerprint-img").attr("src", "");
        }
      }
    });

  $scope.newMasterKey = function() {
    $scope.wallet.addRandomMasterKey()
      .then(function() {
        $scope.$apply();
      });
  };

  $scope.importMasterKey = function() {
    $scope.wallet.importMasterKey($scope.w.importMasterKey)
      .then(function() {
        $scope.w.importMasterKey = null;
        $("#import-master-key-modal").modal('hide');
        $scope.$apply();
      }.bind(this),
            function() { console.log("nope"); }.bind(this));
  };

  $scope.removeMasterKey = function() {
    // TODO(miket): ideally we'll track whether this key was backed
    // up, and make this button available only if yes. Then we'll
    // confirm up the wazoo before actually deleting.
    //
    // Less of a big deal if the master key is public.
    $scope.wallet.removeMasterKey();
  };

  $scope.nextAccountName = function() {
    return "Account " + $scope.wallet.getNextAccountNumber();
  };

  $scope.deriveNextAccount = function() {
    $scope.wallet.deriveNextAccount(false, function(succeeded) {
      if (succeeded) {
        $scope.$apply();
      }
    });
  };

  $scope.watchNextAccount = function() {
    $scope.wallet.deriveNextAccount(true, function(succeeded) {
      if (succeeded) {
        $scope.$apply();
      }
    });
  };

  $scope.unlockWallet = function() {
    var unlockSuccessful = function() {
      $("#unlock-wallet-modal").modal('hide');
      $scope.w.passphraseNew = null;
      $scope.$apply();
    };
    var unlockFailed = function() {
      $scope.w.passphraseNew = null;
      $scope.$apply();
    };
    var relockCallback = function() {
      //$scope.w.showPrivateKey = false;
      $scope.$apply();
    };

    $scope.credentials.unlock($scope.w.passphraseNew,
                              relockCallback.bind(this))
      .then(unlockSuccessful, unlockFailed);
  };

  $scope.lockWallet = function() {
    $scope.credentials.lock(function() {
      $scope.$apply();
    });
  };

  $scope.setPassphrase = function() {
    // TODO: angularjs can probably do this check for us
    if (!$scope.w.passphraseNew || $scope.w.passphraseNew.length == 0) {
      console.log("missing new passphrase");
      return;
    }
    if ($scope.w.passphraseNew != $scope.w.passphraseConfirm) {
      console.log("new didn't match confirm:" + $scope.w.passphraseNew);
      return;
    }

    $scope.credentials.setPassphrase($scope.w.passphraseNew,
                                     function() {
                                       $scope.$apply();
                                     }).then(function() {
                                       $scope.$apply();
                                     });

    // We don't want these items lurking in the DOM.
    $scope.w.passphraseNew = null;
    $scope.w.passphraseConfirm = null;
  };

  $scope.reinitializeEverything = function() {
    $scope.settings.init();
    $scope.credentials.init();
    $scope.wallet.init();
  };

  $scope.clearEverything = function() {
    // TODO(miket): confirmation
    $scope.reinitializeEverything();
    clearAllStorage();
    chrome.runtime.reload();
  };

  $scope.isPassphraseSet = function() {
    return $scope.credentials.isPassphraseSet();
  };

  $scope.isWalletUnlocked = function() {
    return $scope.isPassphraseSet() && !$scope.credentials.isWalletLocked();
  };

  $scope.isWalletKeySet = function() {
    return $scope.wallet.isKeySet();
  };

  $scope.isWalletKeyPrivateSet = function() {
    return $scope.wallet.isExtendedPrivateSet();
  };

  $scope.getWalletKeyPrivate = function() {
    return $scope.wallet.getExtendedPrivateBase58();
  };

  $scope.getWalletKeyPublic = function() {
    return $scope.wallet.getExtendedPublicBase58();
  };

  $scope.getWalletKeyFingerprint = function() {
    return $scope.wallet.getFingerprint();
  };

  $scope.getAccountCount = function() {
    return $scope.wallet.getAccountCount();
  };

  $scope.getAccounts = function() {
    return $scope.wallet.getAccounts();
  };

  $scope.getPublicAddresses = function() {
    return $scope.wallet.publicAddresses;
  }

  $scope.getChangeAddresses = function() {
    return $scope.wallet.changeAddresses;
  }

  $scope.getAddressBalance = function(addr_b58) {
    return $scope.wallet.watchedAddresses[addr_b58].value;
  }

  $scope.getCurrentAccount = function() {
    return $scope.w.currentAccount;
  };

  $scope.getCurrentAccountFingerprint = function() {
    if (!$scope.getCurrentAccount()) {
      return null;
    }
    return $scope.getCurrentAccount().fingerprint;
  };

  $scope.areRequestsPending = function() {
    return $scope.electrum.areRequestsPending();
  };

  $scope.setCurrentAccountByIndex = function(index) {
    if ($scope.w.currentAccount == $scope.getAccounts()[index]) {
      return;
    }
    $scope.w.currentAccount = $scope.getAccounts()[index];
   // $scope.refreshAccount();
  };

  $scope.selectFirstAccount = function() {
    if ($scope.getAccountCount() > 0) {
      $scope.setCurrentAccountByIndex(0);
    }
  };

  $scope.exportMasterKey = function() {
    chrome.fileSystem.chooseEntry(
      {'type': 'saveFile',
       'suggestedName': 'Happynine-Export-' +
       $scope.getWalletKeyFingerprint() + '.txt',
      },
      function(entry) {
        var errorHandler = function(e) {
          console.log(e);
        };
        entry.createWriter(function(writer) {
          writer.onerror = errorHandler;
          writer.onwriteend = function(e) {
            console.log('write complete');
          };
          var message =
            "Happynine: BIP 0038 Master Key Export\r\n" +
            $scope.getWalletKeyFingerprint() + ": " +
            $scope.getWalletKeyPublic() + "\r\n" +
            "Private Key [NOT YET IMPLEMENTED]\r\n\r\n" +
            "THIS IS NOT A BACKUP OF YOUR KEY! THAT FEATURE IS COMING.\r\n";
          message += "Meanwhile, here is a proof of concept text " +
            "QR code (NOT YOURS). \r\n\r\n";
          var qr = $('

');
          qr.qrcode({'text': $scope.getWalletKeyPublic(), 'render': 'text'});
          message += qr.text();
          var blob = new Blob([message], {type: 'text/plain; charset=utf-8'});
          writer.write(blob);
        }, errorHandler);
      });
  };

  $scope.send = function() {
    var sendTo = $scope.w.sendTo;
    var sendValue = $scope.unitToSatoshi($scope.w.sendValue);
    var sendFee = $scope.unitToSatoshi($scope.w.sendFee);

    if (sendValue > 0 && sendFee > 0 && sendTo.length > 25) {
      console.log("send", sendTo, sendValue, $scope.unitLabel(),
                  "for", sendFee);
      $scope.wallet.sendFunds(sendTo, sendValue, sendFee)
        .then(function() {
          $scope.$apply();
        });
    }
  };

  $scope.setAddress = function(a) {
    $scope.w.selectedAddress = a;
    $('#qrcode').empty();
    $('#qrcode').qrcode({
      'width': 128,
      'height': 128,
      'text': a});
  };

  $scope.satoshiToUnit = function(satoshis) {
    return $scope.settings.satoshiToUnit(satoshis);
  };

  $scope.unitToSatoshi = function(units) {
    return $scope.settings.unitToSatoshi(units);
  };

  $scope.unitLabel = function() {
    return $scope.settings.unitLabel();
  };

  // TODO(miket): this might be a race with moduleDidLoad.
  var listenerDiv = document.getElementById('listener');
  listenerDiv.addEventListener('load', function() {
    $scope.startLoading();
  }, true);
};
<|endoftext|>


<commit_before>/// <reference path="GameDraw.js" />
/// <reference path="GameObjects.js" />
var GameEngine = ( function () {
    var board,
        players,
        dices,
        firstDiceThrow,
        currentPlayer,
        hasChosen,
    	indexOfChosenField,
    	indexOfTargetField;

    function start() {
        players = [];
        // playerOneName , playerTwoName - variables containing names from start screen
        players.push(Object.create(GameObjects.Player).init('First', 'white'));
        players.push(Object.create(GameObjects.Player).init('Second', 'black'));

        board = GameObjects.Board.init();

        // dices.rollDices() ; dices.usedNumber(number) ; dices.clearNumbers()
        dices = GameObjects.Dices.init();

        firstDiceThrow = true;

        GameDraw.initGame(board, players);
    }

    function clickedToRollDices() {
        if (firstDiceThrow) {
            firstDiceThrow = false;
            throwFirstDiceToDeterminePlayer();
            currentPlayer = players[0].isOnTurn ? players[0] : players[1];
        }
        if (dices.numbers.length === 0) {
            dices.rollDices();
            currentPlayer = setCurrentPlayerOnTurn();
        }
        GameDraw.updatePlayerNames(currentPlayer)
    }

    function updatePlayGround() {
        GameDraw.updatePlayGround(board);
    }

    function update(pressedField) {
        var canExtract = checkIfPlayerCanExtractPieces(currentPlayer, board);

    		// alert(hasChosen +' ' + indexOfChosenField +' ' +  indexOfTargetField +' ' +  canExtract)

    	if (hasChosen) {
    		indexOfTargetField = getIndexOfPossibleTargetFields(indexOfChosenField, currentPlayer, board, dices.numbers);

    		if (indexOfTargetField.indexOf(pressedField) < 0) {
    			return;
    		}

            board.movePiece (indexOfChosenField, pressedField);

            hasChosen = false;

            checkForPinnedPiece(board);

            dices.usedNumber(Math.abs(pressedField - indexOfChosenField));

            if (dices.numbers.length === 0) {
            	alert ('No more moves. Next player turn');
            	setCurrentPlayerOnTurn();
            }

    	} else {
    		if (!canExtract) {
            	var possibleStartPositions = getIndexOfFieldsWithMovesAvailable(currentPlayer, board, dices.numbers);
            	// alert(possibleStartPositions)
            	if (!possibleStartPositions.length) {
                	alert('No moves');
                	dices.clearNumbers();
                	return;
            	} else if (possibleStartPositions.length === 1) {

                	if (board[possibleStartPositions[0]].pieces.length >= 5) {
                   		board[possibleStartPositions[0]].pieces[4].isChosen = true;
                	} else {
                    	board[possibleStartPositions[0]].pieces[board[possibleStartPositions[0]].pieces.length - 1].isChosen = true;
                	}

                	hasChosen = true;
                	indexOfChosenField = possibleStartPositions[0];
                	// alert(hasChosen + ' ' + indexOfChosenField)

            	} else {
            		if (possibleStartPositions.indexOf(pressedField) > -1) {
            			if (board[pressedField].pieces.length >= 5) {
                   			board[pressedField].pieces[4].isChosen = true;
                		} else {
                    		board[pressedField].pieces[board[pressedField].pieces.length - 1].isChosen = true;
                		}
            		} else {
            			return;
            		}

            		hasChosen = true;
                	indexOfChosenField = pressedField;
                	// alert(hasChosen + ' ' + indexOfChosenField)

            	}

        	} else {
        		// drago tuka move da tegli playera..

        	}
    	}

        updatePlayGround();

        if (!currentPlayer.countOfPieces) {
            alert(currentPlayer.name + ' WINS!!!')
        }
    }

    // Event targets can be document elements, the document itself, window or any other object that supports events.
    //function addListenersToPossibleGameFields(gameFields) {
    //    var i,
    //        len,
    //        currentFieldIndex,
    //        currentField;
    //
    //    for (i = 0, len = gameFields.length; i < len; i += 1) {
    //        currentFieldIndex = gameFields[i];
    //        currentField = board[currentFieldIndex];
    //        currentField.addEventListener('mousedown', selectAndPaintLastPiece(currentField));
    //    }
    //}
    //
    //function selectAndPaintLastPiece(gameField) {
    //    var lengthOfPiecesInField = gameField.pieces.length;
    //    gameField.pieces[lengthOfPiecesInField - 1].isChosen = true;
    //}

    function throwFirstDiceToDeterminePlayer() {
        dices.rollDices();
        if (dices.numbers[dices.numbers.length - 2] > dices.numbers[dices.numbers.length - 1]) {
            players[0].isOnTurn = true;
        } else if (dices.numbers[dices.numbers.length - 2] < dices.numbers[dices.numbers.length - 1]) {
            players[1].isOnTurn = true;
        } else {
            firstDiceThrow = true;
            dices.clearNumbers();
            clickedToRollDices();
        }
    }

    function setCurrentPlayerOnTurn() {
        if (players[0].isOnTurn) {
            players[0].isOnTurn = false;
            players[1].isOnTurn = true;
            return players[0];
        } else {
            players[0].isOnTurn = true;
            players[1].isOnTurn = false;
            return players[1];
        }
    }

    function getDices() {
        return dices;
    }

    return {
        start: start,
        update: update,
        clickedToRollDices: clickedToRollDices,
        dices: getDices
    };
}() );

function getIndexOfFieldsWithMovesAvailable(player, board, numbers) {
    var direction,
        i, j,
        result = [],
        color = player.color,
        index;

    color = color.substring(0, 1).toUpperCase() + color.substring(1);

    if (color.toLowerCase() === 'white') {
        direction = 1;

        if (board[0].pieces.length) {
            for (j = 0; j < numbers.length; j += 1) {
                if (board[0 + numbers[j] * direction]['availableFor' + color]) {
                    return [0];
                }
            }

            return result;
        }
    } else {
        direction = -1;

        if (board[board.length - 1].pieces.length) {
            for (j = 0; j < numbers.length; j += 1) {
                if (board[board.length - 1 + numbers[j] * direction]['availableFor' + color]) {
                    return [board.length - 1];
                }
            }

            return result;
        }
    }

    for (i = 1, len = board.length; i < board.length - 1; i += 1) {
        if (board[i].pieces.length > 0 && board[i].pieces[0].color === color.toLowerCase()) {
            for (j = 0; j < numbers.length; j += 1) {
            	index = i + numbers[j] * direction;
                if (0 < index  && index < 25 && board[index]['availableFor' + color]) {
                    result.push(i);
                    break;
                }
            }
        }
    }

    return result;
}

function getIndexOfPossibleTargetFields(markedIndex, player, board, numbers) {
    alert(markedIndex + ' ' + numbers)

    var color = player.color,
        i,
        result = [],
        direction,
        index;

    if (color === 'white') {
        direction = 1
    } else {
        direction = -1
    }

    color = color.substring(0, 1).toUpperCase() + color.substring(1);

    for (i = 0; i < numbers.length; i++) {
    	index = markedIndex + numbers[i] * direction;

        if (0 < index  && index < 25 && board[index]['availableFor' + color]) {
            result.push(markedIndex + numbers[i] * direction);
        }
    }

    return result;
}

function checkForPinnedPiece(board) {
    var i,
        len = board.length;

    for (i = 1; i < len - 1; i += 1) {
        if (board[i].length === 2 && board[i].pieces[0].color !== board[i].pieces[1].color) {
            var shifted = board[i].pieces.shift();
            if (shifted.color === 'white') {
                board[0].push(shifted);
                return;
            } else {
                board[len - 1].push(shifted);
                return;
            }
        }
    }
}

function checkIfPlayerCanExtractPieces(player, board) {
    var color = player.color,
        i,
        count = 0;

    if (color === 'white') {
        for (i = 19; i <= 24; i += 1) {
            if (board[i].pieces.length > 0 && board[i].pieces[0].color === color) {
                count += board[i].pieces.length;
            }
        }
    } else {
        for (i = 1; i <= 6; i += 1) {
            if (board[i].pieces.length > 0 && board[i].pieces[0].color === color) {
                count += board[i].pieces.length;
            }
        }
    }

    return count === player.countOfPieces;
}

// // All events will call GameEngine.Update() and GameDraw.Update().

//// Logic to add
function checkIfPlayerIsMakingNormalMoves(currentActivePlayer, currentPlayerMoves, maximumAllowedMovesInTurn) {
    if (currentPlayerMoves < maximumAllowedMovesInTurn && !currentActivePlayer.hasHitChecker && !currentActivePlayer.canTakeChecker) {
        return true;
    }
    return false;
}

function play() {

    var maximumAllowedMovesInTurn;
    var currentActivePlayer;
    var inactivePlayer;
    var currentPlayerMoves = 0;   // DA SE IZNESAT TEZI PROMENLIVI GORE POSLE

    //FIRST DICE ROLL CHECK
    if (!checkIfDiceHaveBeenThrownBefore(firstDiceThrow)) {
        throwFirstDiceToDeterminePlayer();
    }

    setCurrentPlayerOnTurn(firstPlayerOnTurn); //Will create the current player on turn
    //value,and have a inactivePlayer  value for the one that isnt  on turn at the moment

    //IF NO MOVES AVAILABLE SWITCH PLAYERS WITHOUT THROWING DICE (player hit and all his 6 spaces are currently closed by enemy)
    if (!currentActivePlayer.hasMovesAvailable) {  //LOGIC NEEDS TO BE ADDED,WHEN  HAS MOVES AVAILABLE BECOMES FALSE- IT'S TRUE ON START,BUT CHANGES
        firstPlayerOnTurn = !firstPlayerOnTurn;
        play();
    }

    //THROW DICE
    throwDiceAndCheckMaxMovesForTurn();

    //IF HAS HIT CHECKERS
    while (currentActivePlayer.hasHitChecker) {

        if (!currentActivePlayer.hasHitChecker) {
            break;
        }

        //WHITE PLAYER - player 1
        if (currentActivePlayer.color === 'white') {
            //check if first dice number field is available
            if (board.fields[firstDice.number].availableForWhite) {
                if (board.fields[firstDice.number].length > 0 && board.fields[firstDice.number][0].color === inactivePlayer.color) {
                    //HIT HIS CHECKER AND PLACE YOUR OWN
                }
                else {
                    //TODO JUST ADD YOUR CHECKER
                }
                //CHANGE THE STATE OF THAT BOARD SPOT
                currentPlayerMoves++;
                if (board.fields[25].length === 0) {
                    currentActivePlayer.hasHitChecker = false;
                    break;
                }
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }

            }
            else {
                currentPlayerMoves++; //you lose a move,because u cant set your pull there
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }
            }
            if (board.fields[secondDice.number].availableForWhite) {
                if (board.fields[secondDice.number].length > 0 && board.fields[secondDice.number][0].color === inactivePlayer.color) {
                    //HIT HIS CHECKER AND PLACE YOUR OWN
                }
                else {
                    //TODO JUST ADD YOUR CHECKER
                }
                //CHANGE THE STATE OF THAT BOARD SPOT
                currentPlayerMoves++;
                if (board.fields[25].length === 0) {
                    currentActivePlayer.hasHitChecker = false;
                    break;
                }
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }

            }
            else {
                currentPlayerMoves++; //you lose a move,because u cant set your pull there
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    hasNoMoves = true;
                    break;
                }
            }

        }

        //IF BLACK PLAYER - player 2

        else {
            //check if first dice number field is available
            if (board.fields[firstDice.number].availableForBlack) {
                if (board.fields[firstDice.number].length > 0 && board.fields[firstDice.number][0].color === inactivePlayer.color) {
                    //HIT HIS CHECKER AND PLACE YOUR OWN
                }
                else {
                    //TODO JUST ADD YOUR CHECKER
                }
                //CHANGE THE STATE OF THAT BOARD SPOT
                currentPlayerMoves++;
                if (board.fields[0].length === 0) {
                    currentActivePlayer.hasHitChecker = false;
                    break;
                }
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }

            }
            else {
                currentPlayerMoves++; //you lose a move,because u cant set your pull there
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }
            }
            if (board.fields[secondDice.number].availableForBlack) {
                if (board.fields[secondDice.number].length > 0 && board.fields[secondDice.number][0].color === inactivePlayer.color) {
                    //HIT HIS CHECKER AND PLACE YOUR OWN
                }
                else {
                    //TODO JUST ADD YOUR CHECKER
                }
                //CHANGE THE STATE OF THAT BOARD SPOT
                currentPlayerMoves++;
                if (board.fields[0].length === 0) {
                    currentActivePlayer.hasHitChecker = false;
                    break;
                }
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }

            }
            else {
                currentPlayerMoves++; //you lose a move,because u cant set your pull there
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }
            }

        }

    }

    if (currentPlayerMoves === maximumAllowedMovesInTurn) {
        play();
    }

    //CHECK IF PLAYER IS CURRENTLY TAKING CHECKERS

    if (currentPlayerMoves === maximumAllowedMovesInTurn) {
        play();
    }

    //JUST MOVE CHECKERS
    if (!checkIfPlayerIsMakingNormalMoves(currentActivePlayer, currentPlayerMoves, maximumAllowedMovesInTurn)) { //CHECK IF PLAYER HAS TO END TURN
        firstPlayerOnTurn = !firstPlayerOnTurn;
        play();
    }

    if (currentActivePlayer.color === 'white') {//white player - player1

        var isClickValidForStartPosition = false;

        while (!isClickValidForStartPosition) {
            //wait for mouse click for start position
            //get X and Y of clicked position
            //determine which board.fields place it is
            //check if that position contains his checkers  (isAvailable for white/black)
            //if no -> leave ,the  while loop will repeat itself and await new click for start position
            // if yes =>  isClickValidForStartPosition = true;
            //highlight which board places the checker from the start position can move to
            //wait for click to determine end position
            //get X and Y of clicked position
            //determine which board.fields place it is
            //if it is one of the possible places,considering the start position -> make the move and draw ,
            //else keep highligted and wait correct click for end position
            //currentPlayerMoves++
            //check is all possible moves for this player,for this turn are made and change players if so
        }

    }
    else {  //black player - player 2

    }
    draw();
}
<commit_msg>Fixed current player chang.<commit_after>/// <reference path="GameDraw.js" />
/// <reference path="GameObjects.js" />
var GameEngine = ( function () {
    var board,
        players,
        dices,
        firstDiceThrow,
        currentPlayer,
        hasChosen,
    	indexOfChosenField,
    	indexOfTargetField;

    function start() {
        players = [];
        // playerOneName , playerTwoName - variables containing names from start screen
        players.push(Object.create(GameObjects.Player).init('First', 'white'));
        players.push(Object.create(GameObjects.Player).init('Second', 'black'));

        board = GameObjects.Board.init();

        // dices.rollDices() ; dices.usedNumber(number) ; dices.clearNumbers()
        dices = GameObjects.Dices.init();

        firstDiceThrow = true;

        GameDraw.initGame(board, players);
    }

    function clickedToRollDices() {
        if (firstDiceThrow) {
            firstDiceThrow = false;
            throwFirstDiceToDeterminePlayer();
            currentPlayer = players[0].isOnTurn ? players[0] : players[1];
        }
        if (dices.numbers.length === 0) {
            dices.rollDices();
            currentPlayer = players[0].isOnTurn ? players[0] : players[1];
        }
        GameDraw.updatePlayerNames(currentPlayer)
    }

    function updatePlayGround() {
        GameDraw.updatePlayGround(board);
    }

    function update(pressedField) {
        var canExtract = checkIfPlayerCanExtractPieces(currentPlayer, board);

    		// alert(hasChosen +' ' + indexOfChosenField +' ' +  indexOfTargetField +' ' +  canExtract)

    	if (hasChosen) {
    		indexOfTargetField = getIndexOfPossibleTargetFields(indexOfChosenField, currentPlayer, board, dices.numbers);

    		if (indexOfTargetField.indexOf(pressedField) < 0) {
    			return;
    		}

            board.movePiece (indexOfChosenField, pressedField);

            hasChosen = false;

            checkForPinnedPiece(board);

            dices.usedNumber(Math.abs(pressedField - indexOfChosenField));

            if (dices.numbers.length === 0) {
            	alert ('No more moves. Next player turn');
            	setCurrentPlayerOnTurn();
            }

    	} else {
    		if (!canExtract) {
            	var possibleStartPositions = getIndexOfFieldsWithMovesAvailable(currentPlayer, board, dices.numbers);
            	// alert(possibleStartPositions)
            	if (!possibleStartPositions.length) {
                	alert('No moves');
                	dices.clearNumbers();
                	return;
            	} else if (possibleStartPositions.length === 1) {

                	if (board[possibleStartPositions[0]].pieces.length >= 5) {
                   		board[possibleStartPositions[0]].pieces[4].isChosen = true;
                	} else {
                    	board[possibleStartPositions[0]].pieces[board[possibleStartPositions[0]].pieces.length - 1].isChosen = true;
                	}

                	hasChosen = true;
                	indexOfChosenField = possibleStartPositions[0];
                	// alert(hasChosen + ' ' + indexOfChosenField)

            	} else {
            		if (possibleStartPositions.indexOf(pressedField) > -1) {
            			if (board[pressedField].pieces.length >= 5) {
                   			board[pressedField].pieces[4].isChosen = true;
                		} else {
                    		board[pressedField].pieces[board[pressedField].pieces.length - 1].isChosen = true;
                		}
            		} else {
            			return;
            		}

            		hasChosen = true;
                	indexOfChosenField = pressedField;
                	// alert(hasChosen + ' ' + indexOfChosenField)

            	}

        	} else {
        		// drago tuka move da tegli playera..

        	}
    	}

        updatePlayGround();

        if (!currentPlayer.countOfPieces) {
            alert(currentPlayer.name + ' WINS!!!')
        }
    }

    // Event targets can be document elements, the document itself, window or any other object that supports events.
    //function addListenersToPossibleGameFields(gameFields) {
    //    var i,
    //        len,
    //        currentFieldIndex,
    //        currentField;
    //
    //    for (i = 0, len = gameFields.length; i < len; i += 1) {
    //        currentFieldIndex = gameFields[i];
    //        currentField = board[currentFieldIndex];
    //        currentField.addEventListener('mousedown', selectAndPaintLastPiece(currentField));
    //    }
    //}
    //
    //function selectAndPaintLastPiece(gameField) {
    //    var lengthOfPiecesInField = gameField.pieces.length;
    //    gameField.pieces[lengthOfPiecesInField - 1].isChosen = true;
    //}

    function throwFirstDiceToDeterminePlayer() {
        dices.rollDices();
        if (dices.numbers[dices.numbers.length - 2] > dices.numbers[dices.numbers.length - 1]) {
            players[0].isOnTurn = true;
        } else if (dices.numbers[dices.numbers.length - 2] < dices.numbers[dices.numbers.length - 1]) {
            players[1].isOnTurn = true;
        } else {
            firstDiceThrow = true;
            dices.clearNumbers();
            clickedToRollDices();
        }
    }

    function setCurrentPlayerOnTurn() {
        if (players[0].isOnTurn) {
            players[0].isOnTurn = false;
            players[1].isOnTurn = true;
            return players[0];
        } else {
            players[0].isOnTurn = true;
            players[1].isOnTurn = false;
            return players[1];
        }
    }

    function getDices() {
        return dices;
    }

    return {
        start: start,
        update: update,
        clickedToRollDices: clickedToRollDices,
        dices: getDices
    };
}() );

function getIndexOfFieldsWithMovesAvailable(player, board, numbers) {
    var direction,
        i, j,
        result = [],
        color = player.color,
        index;

    color = color.substring(0, 1).toUpperCase() + color.substring(1);

    if (color.toLowerCase() === 'white') {
        direction = 1;

        if (board[0].pieces.length) {
            for (j = 0; j < numbers.length; j += 1) {
                if (board[0 + numbers[j] * direction]['availableFor' + color]) {
                    return [0];
                }
            }

            return result;
        }
    } else {
        direction = -1;

        if (board[board.length - 1].pieces.length) {
            for (j = 0; j < numbers.length; j += 1) {
                if (board[board.length - 1 + numbers[j] * direction]['availableFor' + color]) {
                    return [board.length - 1];
                }
            }

            return result;
        }
    }

    for (i = 1, len = board.length; i < board.length - 1; i += 1) {
        if (board[i].pieces.length > 0 && board[i].pieces[0].color === color.toLowerCase()) {
            for (j = 0; j < numbers.length; j += 1) {
            	index = i + numbers[j] * direction;
                if (0 < index  && index < 25 && board[index]['availableFor' + color]) {
                    result.push(i);
                    break;
                }
            }
        }
    }

    return result;
}

function getIndexOfPossibleTargetFields(markedIndex, player, board, numbers) {
    alert(markedIndex + ' ' + numbers)

    var color = player.color,
        i,
        result = [],
        direction,
        index;

    if (color === 'white') {
        direction = 1
    } else {
        direction = -1
    }

    color = color.substring(0, 1).toUpperCase() + color.substring(1);

    for (i = 0; i < numbers.length; i++) {
    	index = markedIndex + numbers[i] * direction;

        if (0 < index  && index < 25 && board[index]['availableFor' + color]) {
            result.push(markedIndex + numbers[i] * direction);
        }
    }

    return result;
}

function checkForPinnedPiece(board) {
    var i,
        len = board.length;

    for (i = 1; i < len - 1; i += 1) {
        if (board[i].length === 2 && board[i].pieces[0].color !== board[i].pieces[1].color) {
            var shifted = board[i].pieces.shift();
            if (shifted.color === 'white') {
                board[0].push(shifted);
                return;
            } else {
                board[len - 1].push(shifted);
                return;
            }
        }
    }
}

function checkIfPlayerCanExtractPieces(player, board) {
    var color = player.color,
        i,
        count = 0;

    if (color === 'white') {
        for (i = 19; i <= 24; i += 1) {
            if (board[i].pieces.length > 0 && board[i].pieces[0].color === color) {
                count += board[i].pieces.length;
            }
        }
    } else {
        for (i = 1; i <= 6; i += 1) {
            if (board[i].pieces.length > 0 && board[i].pieces[0].color === color) {
                count += board[i].pieces.length;
            }
        }
    }

    return count === player.countOfPieces;
}

// // All events will call GameEngine.Update() and GameDraw.Update().

//// Logic to add
function checkIfPlayerIsMakingNormalMoves(currentActivePlayer, currentPlayerMoves, maximumAllowedMovesInTurn) {
    if (currentPlayerMoves < maximumAllowedMovesInTurn && !currentActivePlayer.hasHitChecker && !currentActivePlayer.canTakeChecker) {
        return true;
    }
    return false;
}

function play() {

    var maximumAllowedMovesInTurn;
    var currentActivePlayer;
    var inactivePlayer;
    var currentPlayerMoves = 0;   // DA SE IZNESAT TEZI PROMENLIVI GORE POSLE

    //FIRST DICE ROLL CHECK
    if (!checkIfDiceHaveBeenThrownBefore(firstDiceThrow)) {
        throwFirstDiceToDeterminePlayer();
    }

    setCurrentPlayerOnTurn(firstPlayerOnTurn); //Will create the current player on turn
    //value,and have a inactivePlayer  value for the one that isnt  on turn at the moment

    //IF NO MOVES AVAILABLE SWITCH PLAYERS WITHOUT THROWING DICE (player hit and all his 6 spaces are currently closed by enemy)
    if (!currentActivePlayer.hasMovesAvailable) {  //LOGIC NEEDS TO BE ADDED,WHEN  HAS MOVES AVAILABLE BECOMES FALSE- IT'S TRUE ON START,BUT CHANGES
        firstPlayerOnTurn = !firstPlayerOnTurn;
        play();
    }

    //THROW DICE
    throwDiceAndCheckMaxMovesForTurn();

    //IF HAS HIT CHECKERS
    while (currentActivePlayer.hasHitChecker) {

        if (!currentActivePlayer.hasHitChecker) {
            break;
        }

        //WHITE PLAYER - player 1
        if (currentActivePlayer.color === 'white') {
            //check if first dice number field is available
            if (board.fields[firstDice.number].availableForWhite) {
                if (board.fields[firstDice.number].length > 0 && board.fields[firstDice.number][0].color === inactivePlayer.color) {
                    //HIT HIS CHECKER AND PLACE YOUR OWN
                }
                else {
                    //TODO JUST ADD YOUR CHECKER
                }
                //CHANGE THE STATE OF THAT BOARD SPOT
                currentPlayerMoves++;
                if (board.fields[25].length === 0) {
                    currentActivePlayer.hasHitChecker = false;
                    break;
                }
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }

            }
            else {
                currentPlayerMoves++; //you lose a move,because u cant set your pull there
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }
            }
            if (board.fields[secondDice.number].availableForWhite) {
                if (board.fields[secondDice.number].length > 0 && board.fields[secondDice.number][0].color === inactivePlayer.color) {
                    //HIT HIS CHECKER AND PLACE YOUR OWN
                }
                else {
                    //TODO JUST ADD YOUR CHECKER
                }
                //CHANGE THE STATE OF THAT BOARD SPOT
                currentPlayerMoves++;
                if (board.fields[25].length === 0) {
                    currentActivePlayer.hasHitChecker = false;
                    break;
                }
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }

            }
            else {
                currentPlayerMoves++; //you lose a move,because u cant set your pull there
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    hasNoMoves = true;
                    break;
                }
            }

        }

        //IF BLACK PLAYER - player 2

        else {
            //check if first dice number field is available
            if (board.fields[firstDice.number].availableForBlack) {
                if (board.fields[firstDice.number].length > 0 && board.fields[firstDice.number][0].color === inactivePlayer.color) {
                    //HIT HIS CHECKER AND PLACE YOUR OWN
                }
                else {
                    //TODO JUST ADD YOUR CHECKER
                }
                //CHANGE THE STATE OF THAT BOARD SPOT
                currentPlayerMoves++;
                if (board.fields[0].length === 0) {
                    currentActivePlayer.hasHitChecker = false;
                    break;
                }
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }

            }
            else {
                currentPlayerMoves++; //you lose a move,because u cant set your pull there
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }
            }
            if (board.fields[secondDice.number].availableForBlack) {
                if (board.fields[secondDice.number].length > 0 && board.fields[secondDice.number][0].color === inactivePlayer.color) {
                    //HIT HIS CHECKER AND PLACE YOUR OWN
                }
                else {
                    //TODO JUST ADD YOUR CHECKER
                }
                //CHANGE THE STATE OF THAT BOARD SPOT
                currentPlayerMoves++;
                if (board.fields[0].length === 0) {
                    currentActivePlayer.hasHitChecker = false;
                    break;
                }
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }

            }
            else {
                currentPlayerMoves++; //you lose a move,because u cant set your pull there
                if (currentPlayerMoves === maximumAllowedMovesInTurn) {
                    firstPlayerOnTurn = !firstPlayerOnTurn;
                    break;
                }
            }

        }

    }

    if (currentPlayerMoves === maximumAllowedMovesInTurn) {
        play();
    }

    //CHECK IF PLAYER IS CURRENTLY TAKING CHECKERS

    if (currentPlayerMoves === maximumAllowedMovesInTurn) {
        play();
    }

    //JUST MOVE CHECKERS
    if (!checkIfPlayerIsMakingNormalMoves(currentActivePlayer, currentPlayerMoves, maximumAllowedMovesInTurn)) { //CHECK IF PLAYER HAS TO END TURN
        firstPlayerOnTurn = !firstPlayerOnTurn;
        play();
    }

    if (currentActivePlayer.color === 'white') {//white player - player1

        var isClickValidForStartPosition = false;

        while (!isClickValidForStartPosition) {
            //wait for mouse click for start position
            //get X and Y of clicked position
            //determine which board.fields place it is
            //check if that position contains his checkers  (isAvailable for white/black)
            //if no -> leave ,the  while loop will repeat itself and await new click for start position
            // if yes =>  isClickValidForStartPosition = true;
            //highlight which board places the checker from the start position can move to
            //wait for click to determine end position
            //get X and Y of clicked position
            //determine which board.fields place it is
            //if it is one of the possible places,considering the start position -> make the move and draw ,
            //else keep highligted and wait correct click for end position
            //currentPlayerMoves++
            //check is all possible moves for this player,for this turn are made and change players if so
        }

    }
    else {  //black player - player 2

    }
    draw();
}
<|endoftext|>