<commit_before>(function( $ ){
	$.ecobasa = {
		fullcalendar : function() {
			// There are two kinds of calendar in cosinnus: big and small.
			// The .big-calendar fills the content and shows events.
			// Users can add events here.
			// The .small-calendar is for tooltips or small static date chooser.
			// both are based on jQuery fullcalendar. http://arshaw.com/fullcalendar/

			var localeSettings = {
				de: {
					firstDay: 1, // Monday
					buttonText: {
						today: "Heute",
						month: "Monat",
						week: "Woche",
						day: "Tag"
					},
					monthNames: ['Januar','Februar','März','April',
						'Mai','Juni','Juli','August',
						'September','Oktober','November','Dezember'],
					monthNamesShort: ['Jan','Feb','Mär','Apr','Mai',
						'Jun','Jul','Aug','Sept','Okt','Nov','Dez'],
					dayNames: ['Sonntag','Montag','Dienstag',
						'Mittwoch','Donnerstag','Freitag','Samstag'],
					dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'],
					titleFormat: {
						month: 'MMMM yyyy',
						week: "d.[ MMMM][ yyyy]{ - d. MMMM yyyy}",
						day: 'dddd d. MMMM yyyy'
					},
					columnFormat: {
						month: 'ddd',
						week: 'ddd d',
						day: ''
					},
					axisFormat: 'H:mm',
					timeFormat: {
						'': 'H:mm',
						agenda: 'H:mm{ - H:mm}'
					}
				}
			};
			// LanguageCode injected by backend
			var currentLocaleSettings = localeSettings[LanguageCode];

			if ($('.big-calendar').length)
			$('.big-calendar').fullCalendar($.extend({
				header: {
					left: 'prev,next today',
					center: 'title',
					right: 'month,agendaWeek,agendaDay' // basicDay
				},
				defaultView: 'agendaWeek',

				// cosinnus_calendarEvents is a global var containing the events
				// set by the backend somewhere in the BODY.
				events: cosinnus_calendarEvents,
				select: function(startDate, endDate, allDay, jsEvent, view) {
					$(this.element)
						.closest('.big-calendar')
						.trigger('fullCalendarSelect',[startDate, endDate, allDay, jsEvent, view]);
				},
				eventClick: function(event, jsEvent, view) {
					$(this)
						.closest('.big-calendar')
						.trigger('fullCalendarEventClick',[event, jsEvent, view]);
				},
				selectable: true,
				selectHelper: true
			}, currentLocaleSettings));

			if ($('.small-calendar').length)
			$('.small-calendar').fullCalendar($.extend({
				header: {
					left: 'prev',
					center: 'title',
					right: 'next'
				},
				dayClick: function(date, allDay, jsEvent, view) {
					$(this).trigger('fullCalendarDayClick',[date,jsEvent]);
				},
				viewRender: function(date, cell) {
					// A day has to be rendered because of redraw or something
					$(cell).closest('.small-calendar').trigger('fullCalendarViewRender',[cell]);
				}

			}, currentLocaleSettings));
		},

		calendarBig : function() {
			// The big calendar fills the whole content area and contains the user's events.

			$('.big-calendar')
				.on("fullCalendarSelect", function(event, startDate, endDate, allDay, jsEvent, view) {
					// Dates have been selected. Now the user might want to add an event.
					var startDateDataAttr = startDate.getFullYear() + "-"
						+ ((startDate.getMonth()+1).toString().length === 2
							? (startDate.getMonth()+1)
							: "0" + (startDate.getMonth()+1)) + "-"
						+ (startDate.getDate().toString().length === 2
							? startDate.getDate()
							: "0" + startDate.getDate());

					var endDateDataAttr = endDate.getFullYear() + "-"
						+ ((endDate.getMonth()+1).toString().length === 2
							? (endDate.getMonth()+1)
							: "0" + (endDate.getMonth()+1)) + "-"
						+ (endDate.getDate().toString().length === 2
							? endDate.getDate()
							: "0" + endDate.getDate());

					// allDay is always true as times can not be selected.

					$('#calendarConfirmStartDate').val(startDateDataAttr);
					$('#calendarConfirmEndDate').val(endDateDataAttr);

					if (startDateDataAttr == endDateDataAttr) {
						// Event has one day
						$('#calendarConfirmEventOneday').show();
						$('#calendarConfirmEventMultiday').hide();

						moment.lang(moment.lang(),$.cosinnus.momentShort[moment.lang()]);
						eventDate = moment(startDateDataAttr);
						var eventDate = moment(eventDate).calendar();
						$('#calendarConfirmEventDate').text(eventDate);

						$('#confirmEventModal').modal('show');
					} else {
						// Event has multiple days
						$('#calendarConfirmEventOneday').hide();
						$('#calendarConfirmEventMultiday').show();

						// There is no time, so use momentShort.
						moment.lang(moment.lang(),$.cosinnus.momentShort[moment.lang()]);
						startDate = moment(startDateDataAttr);
						var startDate = moment(startDate).calendar();
						$('#calendarConfirmEventStart').text(startDate);

						endDate = moment(endDateDataAttr);
						var endDate = moment(endDate).calendar();
						$('#calendarConfirmEventEnd').text(endDate);

						$('#confirmEventModal').modal('show');
					}
			});

		},

		// When creating or editing an event the user has to select date and time.
		// Clicking one date input shows all calendars on the whole page.
		calendarDayTimeChooser : function() {
			// Hide calendar when clicking outside
			$(document).click(function(event) {
				var thisdaytimechooser = $(event.target).closest('.calendar-date-time-chooser');
				if(thisdaytimechooser.length) {
					// Don't hide any chooser
				} else {
					// hide all
					$('.calendar-date-time-chooser .small-calendar').slideUp();
				}
			});

			$('.calendar-date-time-chooser input.calendar-date-time-chooser-date')
				.click(function() {
				$('.calendar-date-time-chooser .small-calendar').slideDown();
			});

			$('.calendar-date-time-chooser i').click(function() {
				$('.calendar-date-time-chooser .small-calendar').slideDown();
			});

			$('.calendar-date-time-chooser .small-calendar').hide();

			// on every re-drawing of the calendar select the choosen date
			$('.calendar-date-time-chooser .small-calendar')
				.on("fullCalendarViewRender", function(event, cell) {
					// select choosen day

					var date = $(this)
						.closest('.calendar-date-time-chooser')
						.find('.calendar-date-time-chooser-hiddendate')
						.val();
					// "2014-04-28"
					if (date) $(this)
						.find('td[data-date='+date+']')
						.addClass('selected');
				})
				.trigger('fullCalendarViewRender')

				// when clicked on a day: use this!
				.on("fullCalendarDayClick", function(event, date, jsEvent) {
					var dayElement = jsEvent.currentTarget;
					if ($(dayElement).hasClass('fc-other-month')) return;

					var dateDataAttr = date.getFullYear() + "-"
						+ ((date.getMonth()+1).toString().length === 2
							? (date.getMonth()+1)
							: "0" + (date.getMonth()+1)) + "-"
						+ (date.getDate().toString().length === 2
							? date.getDate()
							: "0" + date.getDate());

					// unselect all and re-select later
					$(dayElement).parent().parent().find('td').removeClass('selected');
					$(dayElement).addClass('selected');

					// When date picked, update date in form
					$(this)
						.closest('.calendar-date-time-chooser')
						.find('.calendar-date-time-chooser-hiddendate')
						.val(dateDataAttr);

					// Update INPUT with human readable date
					moment.lang(moment.lang(),$.cosinnus.momentShort[moment.lang()]);
					var humanDateString = moment(dateDataAttr).calendar();
						$(this)
							.closest('.calendar-date-time-chooser')
							.find('.calendar-date-time-chooser-date')
							.val(humanDateString);
				});

			// Set INPUT with human readable date
			$('.calendar-date-time-chooser').each(function() {
				var dateDataAttr = $(this)
					.find('.calendar-date-time-chooser-hiddendate')
					.val();

				if (dateDataAttr) {
					moment.lang(moment.lang(),$.cosinnus.momentShort[moment.lang()]);
					var humanDateString = moment(dateDataAttr).calendar();
						$(this)
							.find('.calendar-date-time-chooser-date')
							.val(humanDateString);
				}
			});
		}
	};
})( jQuery );

$(document).ready(function() {
	var foobar = function() {
		if( ! $('#myCanvas').tagcanvas({
		     textColour : '#000',
		     outlineColour : '#da6137',
		     outlineMethod : 'colour',
		     outlineThickness : 1,
		     pulsateTime : '1',
		     stretchX : 5,
		     imageScale : 0.6,
		     maxSpeed : 0.03,
		     depth : 0.5,
		     zoom : 0.75,
		     fadeIn: 2000
		   })) {
		     // TagCanvas failed to load
		     $('#myCanvasContainer').hide();
		   }
	};
	window.setTimeout(foobar, 1000);
	$.ecobasa.fullcalendar();
	$.ecobasa.calendarBig();
//	$.ecobasa.calendarDayTimeChooser();

	$('#bus').tooltip();
	$('.container.about #progress_bar').tooltip();
	$('.email').tooltip();
});
<commit_msg>Refers #83 -- Moved initialisation of $.ecobasa into document.ready<commit_after>$(document).ready(function() {
	$.ecobasa = {
		fullcalendar : function() {
			// There are two kinds of calendar in cosinnus: big and small.
			// The .big-calendar fills the content and shows events.
			// Users can add events here.
			// The .small-calendar is for tooltips or small static date chooser.
			// both are based on jQuery fullcalendar. http://arshaw.com/fullcalendar/

			var localeSettings = {
				de: {
					firstDay: 1, // Monday
					buttonText: {
						today: "Heute",
						month: "Monat",
						week: "Woche",
						day: "Tag"
					},
					monthNames: ['Januar','Februar','März','April',
						'Mai','Juni','Juli','August',
						'September','Oktober','November','Dezember'],
					monthNamesShort: ['Jan','Feb','Mär','Apr','Mai',
						'Jun','Jul','Aug','Sept','Okt','Nov','Dez'],
					dayNames: ['Sonntag','Montag','Dienstag',
						'Mittwoch','Donnerstag','Freitag','Samstag'],
					dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'],
					titleFormat: {
						month: 'MMMM yyyy',
						week: "d.[ MMMM][ yyyy]{ - d. MMMM yyyy}",
						day: 'dddd d. MMMM yyyy'
					},
					columnFormat: {
						month: 'ddd',
						week: 'ddd d',
						day: ''
					},
					axisFormat: 'H:mm',
					timeFormat: {
						'': 'H:mm',
						agenda: 'H:mm{ - H:mm}'
					}
				}
			};
			// LanguageCode injected by backend
			var currentLocaleSettings = localeSettings[LanguageCode];

			if ($('.big-calendar').length)
			$('.big-calendar').fullCalendar($.extend({
				header: {
					left: 'prev,next today',
					center: 'title',
					right: 'month,agendaWeek,agendaDay' // basicDay
				},
				defaultView: 'agendaWeek',

				// cosinnus_calendarEvents is a global var containing the events
				// set by the backend somewhere in the BODY.
				events: cosinnus_calendarEvents,
				select: function(startDate, endDate, allDay, jsEvent, view) {
					$(this.element)
						.closest('.big-calendar')
						.trigger('fullCalendarSelect',[startDate, endDate, allDay, jsEvent, view]);
				},
				eventClick: function(event, jsEvent, view) {
					$(this)
						.closest('.big-calendar')
						.trigger('fullCalendarEventClick',[event, jsEvent, view]);
				},
				selectable: true,
				selectHelper: true
			}, currentLocaleSettings));

			if ($('.small-calendar').length)
			$('.small-calendar').fullCalendar($.extend({
				header: {
					left: 'prev',
					center: 'title',
					right: 'next'
				},
				dayClick: function(date, allDay, jsEvent, view) {
					$(this).trigger('fullCalendarDayClick',[date,jsEvent]);
				},
				viewRender: function(date, cell) {
					// A day has to be rendered because of redraw or something
					$(cell).closest('.small-calendar').trigger('fullCalendarViewRender',[cell]);
				}

			}, currentLocaleSettings));
		},

		calendarBig : function() {
			// The big calendar fills the whole content area and contains the user's events.

			$('.big-calendar')
				.on("fullCalendarSelect", function(event, startDate, endDate, allDay, jsEvent, view) {
					// Dates have been selected. Now the user might want to add an event.
					var startDateDataAttr = startDate.getFullYear() + "-"
						+ ((startDate.getMonth()+1).toString().length === 2
							? (startDate.getMonth()+1)
							: "0" + (startDate.getMonth()+1)) + "-"
						+ (startDate.getDate().toString().length === 2
							? startDate.getDate()
							: "0" + startDate.getDate());

					var endDateDataAttr = endDate.getFullYear() + "-"
						+ ((endDate.getMonth()+1).toString().length === 2
							? (endDate.getMonth()+1)
							: "0" + (endDate.getMonth()+1)) + "-"
						+ (endDate.getDate().toString().length === 2
							? endDate.getDate()
							: "0" + endDate.getDate());

					// allDay is always true as times can not be selected.

					$('#calendarConfirmStartDate').val(startDateDataAttr);
					$('#calendarConfirmEndDate').val(endDateDataAttr);

					if (startDateDataAttr == endDateDataAttr) {
						// Event has one day
						$('#calendarConfirmEventOneday').show();
						$('#calendarConfirmEventMultiday').hide();

						moment.lang(moment.lang(),$.cosinnus.momentShort[moment.lang()]);
						eventDate = moment(startDateDataAttr);
						var eventDate = moment(eventDate).calendar();
						$('#calendarConfirmEventDate').text(eventDate);

						$('#confirmEventModal').modal('show');
					} else {
						// Event has multiple days
						$('#calendarConfirmEventOneday').hide();
						$('#calendarConfirmEventMultiday').show();

						// There is no time, so use momentShort.
						moment.lang(moment.lang(),$.cosinnus.momentShort[moment.lang()]);
						startDate = moment(startDateDataAttr);
						var startDate = moment(startDate).calendar();
						$('#calendarConfirmEventStart').text(startDate);

						endDate = moment(endDateDataAttr);
						var endDate = moment(endDate).calendar();
						$('#calendarConfirmEventEnd').text(endDate);

						$('#confirmEventModal').modal('show');
					}
			});

		},

		// When creating or editing an event the user has to select date and time.
		// Clicking one date input shows all calendars on the whole page.
		calendarDayTimeChooser : function() {
			// Hide calendar when clicking outside
			$(document).click(function(event) {
				var thisdaytimechooser = $(event.target).closest('.calendar-date-time-chooser');
				if(thisdaytimechooser.length) {
					// Don't hide any chooser
				} else {
					// hide all
					$('.calendar-date-time-chooser .small-calendar').slideUp();
				}
			});

			$('.calendar-date-time-chooser input.calendar-date-time-chooser-date')
				.click(function() {
				$('.calendar-date-time-chooser .small-calendar').slideDown();
			});

			$('.calendar-date-time-chooser i').click(function() {
				$('.calendar-date-time-chooser .small-calendar').slideDown();
			});

			$('.calendar-date-time-chooser .small-calendar').hide();

			// on every re-drawing of the calendar select the choosen date
			$('.calendar-date-time-chooser .small-calendar')
				.on("fullCalendarViewRender", function(event, cell) {
					// select choosen day

					var date = $(this)
						.closest('.calendar-date-time-chooser')
						.find('.calendar-date-time-chooser-hiddendate')
						.val();
					// "2014-04-28"
					if (date) $(this)
						.find('td[data-date='+date+']')
						.addClass('selected');
				})
				.trigger('fullCalendarViewRender')

				// when clicked on a day: use this!
				.on("fullCalendarDayClick", function(event, date, jsEvent) {
					var dayElement = jsEvent.currentTarget;
					if ($(dayElement).hasClass('fc-other-month')) return;

					var dateDataAttr = date.getFullYear() + "-"
						+ ((date.getMonth()+1).toString().length === 2
							? (date.getMonth()+1)
							: "0" + (date.getMonth()+1)) + "-"
						+ (date.getDate().toString().length === 2
							? date.getDate()
							: "0" + date.getDate());

					// unselect all and re-select later
					$(dayElement).parent().parent().find('td').removeClass('selected');
					$(dayElement).addClass('selected');

					// When date picked, update date in form
					$(this)
						.closest('.calendar-date-time-chooser')
						.find('.calendar-date-time-chooser-hiddendate')
						.val(dateDataAttr);

					// Update INPUT with human readable date
					moment.lang(moment.lang(),$.cosinnus.momentShort[moment.lang()]);
					var humanDateString = moment(dateDataAttr).calendar();
						$(this)
							.closest('.calendar-date-time-chooser')
							.find('.calendar-date-time-chooser-date')
							.val(humanDateString);
				});

			// Set INPUT with human readable date
			$('.calendar-date-time-chooser').each(function() {
				var dateDataAttr = $(this)
					.find('.calendar-date-time-chooser-hiddendate')
					.val();

				if (dateDataAttr) {
					moment.lang(moment.lang(),$.cosinnus.momentShort[moment.lang()]);
					var humanDateString = moment(dateDataAttr).calendar();
						$(this)
							.find('.calendar-date-time-chooser-date')
							.val(humanDateString);
				}
			});
		}
	};

	var foobar = function() {
		if( ! $('#myCanvas').tagcanvas({
		     textColour : '#000',
		     outlineColour : '#da6137',
		     outlineMethod : 'colour',
		     outlineThickness : 1,
		     pulsateTime : '1',
		     stretchX : 5,
		     imageScale : 0.6,
		     maxSpeed : 0.03,
		     depth : 0.5,
		     zoom : 0.75,
		     fadeIn: 2000
		   })) {
		     // TagCanvas failed to load
		     $('#myCanvasContainer').hide();
		   }
	};
	window.setTimeout(foobar, 1000);
	$.ecobasa.fullcalendar();
	$.ecobasa.calendarBig();
//	$.ecobasa.calendarDayTimeChooser();

	$('#bus').tooltip();
	$('.container.about #progress_bar').tooltip();
	$('.email').tooltip();
});
<|endoftext|>


<commit_before>/*global window, console, navigator*/

var Erizo = Erizo || {};

Erizo.sessionId = 103;

Erizo.Connection = function (spec) {
    "use strict";
    var that = {};

    spec.session_id = (Erizo.sessionId += 1);

    // Check which WebRTC Stack is installed.
    that.browser = "";

    if (typeof module !== 'undefined' && module.exports) {
        L.Logger.error('Publish/subscribe video/audio streams not supported in erizofc yet');
        that = Erizo.FcStack(spec);
    } else if (window.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./)[1] === "25" ||
               window.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./)[1] === "26") {
        // Google Chrome Stable.
        console.log("Stable!");
        that = Erizo.ChromeStableStack(spec);
        that.browser = "chrome-stable";
    } else if (window.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./)[1] === "27" ||
               window.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./)[1] === "28") {
        // Google Chrome Canary.
        console.log("Canary!");
        that = Erizo.ChromeCanaryStack(spec);
        that.browser = "chrome-canary";
    } else if (window.navigator.appVersion.match(/Bowser\/([\w\W]*?)\./)[1] === "25") {
        // Bowser
        that.browser = "bowser";
    } else if (window.navigator.appVersion.match(/Mozilla\/([\w\W]*?)\./)[1] === "25") {
        // Firefox
        that.browser = "mozilla";
    } else {
        // None.
        that.browser = "none";
        throw "WebRTC stack not available";
    }

    return that;
};

Erizo.GetUserMedia = function (config, callback) {
    "use strict";

    if (typeof module !== 'undefined' && module.exports) {
        L.Logger.error('Video/audio streams not supported in erizofc yet (only specifying a url)');
    } else {
        try {
            navigator.webkitGetUserMedia("audio, video", callback);
            console.log('GetUserMedia BOWSER');
        } catch (e) {
            navigator.webkitGetUserMedia(config, callback);
            console.log('GetUserMedia CHROME');
        }
    }

};
<commit_msg>Updated chrome versions<commit_after>/*global window, console, navigator*/

var Erizo = Erizo || {};

Erizo.sessionId = 103;

Erizo.Connection = function (spec) {
    "use strict";
    var that = {};

    spec.session_id = (Erizo.sessionId += 1);

    // Check which WebRTC Stack is installed.
    that.browser = "";

    if (typeof module !== 'undefined' && module.exports) {
        L.Logger.error('Publish/subscribe video/audio streams not supported in erizofc yet');
        that = Erizo.FcStack(spec);
    } else if (window.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./)[1] === "26" ||
               window.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./)[1] === "27" ||
	       window.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./)[1] === "28") {
        // Google Chrome Stable.
        console.log("Stable!");
        that = Erizo.ChromeStableStack(spec);
        that.browser = "chrome-stable";
    } else if (window.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./)[1] === "29" ||
               window.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./)[1] === "30") {
        // Google Chrome Canary.
        console.log("Canary!");
        that = Erizo.ChromeCanaryStack(spec);
        that.browser = "chrome-canary";
    } else if (window.navigator.appVersion.match(/Bowser\/([\w\W]*?)\./)[1] === "25") {
        // Bowser
        that.browser = "bowser";
    } else if (window.navigator.appVersion.match(/Mozilla\/([\w\W]*?)\./)[1] === "25") {
        // Firefox
        that.browser = "mozilla";
    } else {
        // None.
        that.browser = "none";
        throw "WebRTC stack not available";
    }

    return that;
};

Erizo.GetUserMedia = function (config, callback) {
    "use strict";

    if (typeof module !== 'undefined' && module.exports) {
        L.Logger.error('Video/audio streams not supported in erizofc yet (only specifying a url)');
    } else {
        try {
            navigator.webkitGetUserMedia("audio, video", callback);
            console.log('GetUserMedia BOWSER');
        } catch (e) {
            navigator.webkitGetUserMedia(config, callback);
            console.log('GetUserMedia CHROME');
        }
    }

};
<|endoftext|>


<commit_before>let options;
let lastNavigation;

const toggleResultHighlighting = (link) => {
  link.classList.toggle('highlighted-search-result')
};

const loadOptions = () => {
  return new Promise((resolve, reject) => {
    chrome.storage.sync.get(
        {
          wrapNavigation: false,
          autoSelectFirst: true,
          nextKey: 'down, j',
          previousKey: 'up, k',
          navigatePreviousResultPage: 'left, h',
          navigateNextResultPage: 'right, l',
          navigateKey: 'return, space',
          navigateNewTabKey: 'ctrl+return, command+return, ctrl+space',
          navigateSearchTab: 'a, s',
          navigateImagesTab: 'i',
          navigateVideosTab: 'v',
          navigateMapsTab: 'm',
          navigateNewsTab: 'n',
          focusSearchInput: '/, escape'
        },
        (items) => {
          options = items;
          if (chrome.runtime.lastError) {
            reject();
          } else {
            resolve();
          }
        });
  });
};

const loadLastNavigation = () => {
  return new Promise((resolve, reject) => {
    chrome.storage.local.get(
    {
      lastQueryUrl: false,
      lastFocusedIndex: 0
    },
    (items) =>{
      lastNavigation = items;
      if (chrome.runtime.lastError) {
        reject();
      } else {
        resolve();
      }
    });
  });
}

const getNextIndex = (currentIndex, numResults, shouldWrap) => {
  if (currentIndex < numResults - 1) {
    return currentIndex + 1;
  }
  if (!shouldWrap) {
    return currentIndex;
  }
  return 0;
};

const getPreviousIndex = (currentIndex, numResults, shouldWrap) => {
  if (currentIndex > 0) {
    return currentIndex - 1;
  }
  if (!shouldWrap) {
    return currentIndex;
  }
  return numResults - 1;
};

const initResultsNavigation = (results) => {
  let isFirstNavigation = true;
  let resultIndex = 0;
  const updateHighlightedResult = (newResultIndex) => {
    if (results.length > 0) {
      results[resultIndex].classList.remove('highlighted-search-result')
      resultIndex = newResultIndex;
      results[resultIndex].classList.add('highlighted-search-result')
      results[resultIndex].focus();
    }
  };
  if (options.autoSelectFirst) {
    // Highlight the first result when the page is loaded.
    updateHighlightedResult(0);
  }
  loadLastNavigation().then(() => {
    if (location.href === lastNavigation.lastQueryUrl) {
      isFirstNavigation = false;
      updateHighlightedResult(lastNavigation.lastFocusedIndex);
    }
  });
  key(options.nextKey, (event) => {
    let nextIndex =
        getNextIndex(resultIndex, results.length, options.wrapNavigation);
    if (!options.autoSelectFirst && isFirstNavigation) {
      nextIndex = 0;
      isFirstNavigation = false;
    }
    updateHighlightedResult(nextIndex);
    handleEvent(event);
  });
  key(options.previousKey, (event) => {
    let previousIndex =
        getPreviousIndex(resultIndex, results.length, options.wrapNavigation);
    if (!options.autoSelectFirst && isFirstNavigation) {
      previousIndex = 0;
      isFirstNavigation = false;
    }
    updateHighlightedResult(previousIndex);
    handleEvent(event);
  });
  key(options.navigateKey, (event) => {
    let link = results[resultIndex];
    saveLastNavigation();
    link.click();
    handleEvent(event);
  });
  key(options.navigateNewTabKey, (event) => {
    let link = results[resultIndex];
    window.open(link.href);
    handleEvent(event);
  });
};

const initCommonGoogleSearchNavigation = () => {
  let searchInput = document.getElementById('lst-ib');
  key(options.focusSearchInput, (event) => {
    searchInput.focus();
    searchInput.select();
    handleEvent(event);
  });
  let all = getElementByXpath(
      '//a[contains(@class, \'q qs\') and not (contains(@href, \'&tbm=\')) and not (contains(@href, \'maps.google.\'))]');
  key(options.navigateSearchTab, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(all, event);
  });
  let images = getElementByXpath(
      '//a[contains(@class, \'q qs\') and (contains(@href, \'&tbm=isch\'))]');
  key(options.navigateImagesTab, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(images, event);
  });
  let videos = getElementByXpath(
      '//a[contains(@class, \'q qs\') and (contains(@href, \'&tbm=vid\'))]');
  key(options.navigateVideosTab, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(videos, event);
  });
  let maps = getElementByXpath(
      '//a[contains(@class, \'q qs\') and (contains(@href, \'maps.google.\'))]');
  key(options.navigateMapsTab, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(maps, event);
  });
  let news = getElementByXpath(
      '//a[contains(@class, \'q qs\') and (contains(@href, \'&tbm=nws\'))]');
  key(options.navigateNewsTab, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(news, event);
  });
  let previousResultPage = document.querySelector('#pnprev');
  key(options.navigatePreviousResultPage, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(previousResultPage, event);
  });
  let nextResultPage = document.querySelector('#pnnext');
  key(options.navigateNextResultPage, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(nextResultPage, event);
  });
};

const updateUrlWithNodeHrefAndHandleEvent = (node, event) => {
  if (node !== null) {
    location.href = node.href;
  }
  handleEvent(event);
};

const handleEvent = (event) => {
  if (event !== null) {
    event.stopPropagation();
    event.preventDefault();
  }
};

const getQueryStringParams = () => {
  const encodedQueryString = window.location.search.slice(1);
  const encodedParams = encodedQueryString.split('&');
  let params = {};
  for (const encodedParam of encodedParams) {
    let [key, encodedValue] = encodedParam.split('=');
    if (!encodedValue) {
      encodedValue = '';
    }
    // + (plus sign) is not decoded by decodeURIComponent so we need to decode
    // it manually.
    encodedValue = encodedValue.replace(/\+/g, ' ');
    params[key] = decodeURIComponent(encodedValue);
  }
  return params;
};

const getGoogleSearchLinks = function() {
  let results = Array.prototype.slice.call(document.querySelectorAll('h3.r a'));
  let prevPage = document.querySelector('#pnprev');
  if (prevPage !== null) {
    results.push(prevPage);
  }
  let nextPage = document.querySelector('#pnnext');
  if (nextPage !== null) {
    results.push(nextPage);
  }
  return results;
};

const initGoogleSearch = function() {
  const params = getQueryStringParams();
  // Don't initialize results navigation on image search, since it doesn't work
  // there.
  if (params['tbm'] !== 'isch') {
    // This file is loaded only after the DOM is ready, so no need to wait for
    // DOMContentLoaded.
    loadOptions().then(() => {
      initResultsNavigation(getGoogleSearchLinks());
    });
  }
  loadOptions().then(() => {
    initCommonGoogleSearchNavigation();
  });
};

const initPageIfNeeded = () => {
  if (/^(www|encrypted)\.google\./.test(window.location.hostname)) {
    initGoogleSearch();
  }
};

function getElementByXpath(path) {
  return document
      .evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
      .singleNodeValue;
};

const saveLastNavigation = () => {
  chrome.storage.local.set(
      {
        lastQueryUrl: location.href,
        lastFocusedIndex: resultIndex
      }, null);
    link.click();
}

initPageIfNeeded();

<commit_msg>Amend last commit<commit_after>let options;
let lastNavigation;

const toggleResultHighlighting = (link) => {
  link.classList.toggle('highlighted-search-result')
};

const loadOptions = () => {
  return new Promise((resolve, reject) => {
    chrome.storage.sync.get(
        {
          wrapNavigation: false,
          autoSelectFirst: true,
          nextKey: 'down, j',
          previousKey: 'up, k',
          navigatePreviousResultPage: 'left, h',
          navigateNextResultPage: 'right, l',
          navigateKey: 'return, space',
          navigateNewTabKey: 'ctrl+return, command+return, ctrl+space',
          navigateSearchTab: 'a, s',
          navigateImagesTab: 'i',
          navigateVideosTab: 'v',
          navigateMapsTab: 'm',
          navigateNewsTab: 'n',
          focusSearchInput: '/, escape'
        },
        (items) => {
          options = items;
          if (chrome.runtime.lastError) {
            reject();
          } else {
            resolve();
          }
        });
  });
};

const loadLastNavigation = () => {
  return new Promise((resolve, reject) => {
    chrome.storage.local.get(
    {
      lastQueryUrl: false,
      lastFocusedIndex: 0
    },
    (items) =>{
      lastNavigation = items;
      if (chrome.runtime.lastError) {
        reject();
      } else {
        resolve();
      }
    });
  });
}

const getNextIndex = (currentIndex, numResults, shouldWrap) => {
  if (currentIndex < numResults - 1) {
    return currentIndex + 1;
  }
  if (!shouldWrap) {
    return currentIndex;
  }
  return 0;
};

const getPreviousIndex = (currentIndex, numResults, shouldWrap) => {
  if (currentIndex > 0) {
    return currentIndex - 1;
  }
  if (!shouldWrap) {
    return currentIndex;
  }
  return numResults - 1;
};

const initResultsNavigation = (results) => {
  let isFirstNavigation = true;
  let resultIndex = 0;
  const updateHighlightedResult = (newResultIndex) => {
    if (results.length > 0) {
      results[resultIndex].classList.remove('highlighted-search-result')
      resultIndex = newResultIndex;
      results[resultIndex].classList.add('highlighted-search-result')
      results[resultIndex].focus();
    }
  };
  if (options.autoSelectFirst) {
    // Highlight the first result when the page is loaded.
    updateHighlightedResult(0);
  }
  loadLastNavigation().then(() => {
    if (location.href === lastNavigation.lastQueryUrl) {
      isFirstNavigation = false;
      updateHighlightedResult(lastNavigation.lastFocusedIndex);
    }
  });
  key(options.nextKey, (event) => {
    let nextIndex =
        getNextIndex(resultIndex, results.length, options.wrapNavigation);
    if (!options.autoSelectFirst && isFirstNavigation) {
      nextIndex = 0;
      isFirstNavigation = false;
    }
    updateHighlightedResult(nextIndex);
    handleEvent(event);
  });
  key(options.previousKey, (event) => {
    let previousIndex =
        getPreviousIndex(resultIndex, results.length, options.wrapNavigation);
    if (!options.autoSelectFirst && isFirstNavigation) {
      previousIndex = 0;
      isFirstNavigation = false;
    }
    updateHighlightedResult(previousIndex);
    handleEvent(event);
  });
  key(options.navigateKey, (event) => {
    let link = results[resultIndex];
    saveLastNavigation();
    link.click();
    handleEvent(event);
  });
  key(options.navigateNewTabKey, (event) => {
    let link = results[resultIndex];
    window.open(link.href);
    handleEvent(event);
  });
};

const initCommonGoogleSearchNavigation = () => {
  let searchInput = document.getElementById('lst-ib');
  key(options.focusSearchInput, (event) => {
    searchInput.focus();
    searchInput.select();
    handleEvent(event);
  });
  let all = getElementByXpath(
      '//a[contains(@class, \'q qs\') and not (contains(@href, \'&tbm=\')) and not (contains(@href, \'maps.google.\'))]');
  key(options.navigateSearchTab, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(all, event);
  });
  let images = getElementByXpath(
      '//a[contains(@class, \'q qs\') and (contains(@href, \'&tbm=isch\'))]');
  key(options.navigateImagesTab, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(images, event);
  });
  let videos = getElementByXpath(
      '//a[contains(@class, \'q qs\') and (contains(@href, \'&tbm=vid\'))]');
  key(options.navigateVideosTab, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(videos, event);
  });
  let maps = getElementByXpath(
      '//a[contains(@class, \'q qs\') and (contains(@href, \'maps.google.\'))]');
  key(options.navigateMapsTab, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(maps, event);
  });
  let news = getElementByXpath(
      '//a[contains(@class, \'q qs\') and (contains(@href, \'&tbm=nws\'))]');
  key(options.navigateNewsTab, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(news, event);
  });
  let previousResultPage = document.querySelector('#pnprev');
  key(options.navigatePreviousResultPage, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(previousResultPage, event);
  });
  let nextResultPage = document.querySelector('#pnnext');
  key(options.navigateNextResultPage, (event) => {
    updateUrlWithNodeHrefAndHandleEvent(nextResultPage, event);
  });
};

const updateUrlWithNodeHrefAndHandleEvent = (node, event) => {
  if (node !== null) {
    location.href = node.href;
  }
  handleEvent(event);
};

const handleEvent = (event) => {
  if (event !== null) {
    event.stopPropagation();
    event.preventDefault();
  }
};

const getQueryStringParams = () => {
  const encodedQueryString = window.location.search.slice(1);
  const encodedParams = encodedQueryString.split('&');
  let params = {};
  for (const encodedParam of encodedParams) {
    let [key, encodedValue] = encodedParam.split('=');
    if (!encodedValue) {
      encodedValue = '';
    }
    // + (plus sign) is not decoded by decodeURIComponent so we need to decode
    // it manually.
    encodedValue = encodedValue.replace(/\+/g, ' ');
    params[key] = decodeURIComponent(encodedValue);
  }
  return params;
};

const getGoogleSearchLinks = function() {
  let results = Array.prototype.slice.call(document.querySelectorAll('h3.r a'));
  let prevPage = document.querySelector('#pnprev');
  if (prevPage !== null) {
    results.push(prevPage);
  }
  let nextPage = document.querySelector('#pnnext');
  if (nextPage !== null) {
    results.push(nextPage);
  }
  return results;
};

const initGoogleSearch = function() {
  const params = getQueryStringParams();
  // Don't initialize results navigation on image search, since it doesn't work
  // there.
  if (params['tbm'] !== 'isch') {
    // This file is loaded only after the DOM is ready, so no need to wait for
    // DOMContentLoaded.
    loadOptions().then(() => {
      initResultsNavigation(getGoogleSearchLinks());
    });
  }
  loadOptions().then(() => {
    initCommonGoogleSearchNavigation();
  });
};

const initPageIfNeeded = () => {
  if (/^(www|encrypted)\.google\./.test(window.location.hostname)) {
    initGoogleSearch();
  }
};

function getElementByXpath(path) {
  return document
      .evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
      .singleNodeValue;
};

const saveLastNavigation = () => {
  chrome.storage.local.set(
      {
        lastQueryUrl: location.href,
        lastFocusedIndex: resultIndex
      }, null);
}

initPageIfNeeded();

<|endoftext|>


<commit_before>export default {
  "passages": [
    {
      "text": "It's 9th grade geometry.  This is the end part of a lesson on chords, secants, and solving algebraic expressions to solve geometry problems.\n\n\n\nStudents are working on goformative.com, working through three or four problems to practice the kinds of problems you just demonstrated on the board.\n\n[[Do nothing->nothing]]\n[[Check in on Alicia->Alicia]]\n[[Check in on Samantha->Samantha]]\n[[Check in on Ken->Ken]]\n[[Check in on Ryan->Ryan]]",
      "links": [
        {
          "name": "Do nothing",
          "link": "nothing",
          "pid": "2"
        },
        {
          "name": "Check in on Alicia",
          "link": "Alicia",
          "pid": "3"
        },
        {
          "name": "Check in on Samantha",
          "link": "Samantha",
          "pid": "8"
        },
        {
          "name": "Check in on Ken",
          "link": "Ken",
          "pid": "4"
        },
        {
          "name": "Check in on Ryan",
          "link": "Ryan",
          "pid": "5"
        }
      ],
      "name": "Geometry with chords",
      "pid": "1",
      "position": {
        "x": "512",
        "y": "299"
      }
    },
    {
      "text": "Really?  You should be helping students.\n\n[[Try again->Geometry with chords]]",
      "links": [
        {
          "name": "Try again",
          "link": "Geometry with chords",
          "pid": "1"
        }
      ],
      "name": "nothing",
      "pid": "2",
      "position": {
        "x": "290",
        "y": "475"
      }
    },
    {
      "text": "Alicia is looking through her notebook.\n\n[[\"What are you looking for?\"]]\n[[\"How's it going?\"]]",
      "links": [
        {
          "name": "\"What are you looking for?\"",
          "link": "\"What are you looking for?\"",
          "pid": "6"
        },
        {
          "name": "\"How's it going?\"",
          "link": "\"How's it going?\"",
          "pid": "7"
        }
      ],
      "name": "Alicia",
      "pid": "3",
      "position": {
        "x": "697",
        "y": "482"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "Ken",
      "pid": "4",
      "position": {
        "x": "875",
        "y": "308"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "Ryan",
      "pid": "5",
      "position": {
        "x": "962",
        "y": "450"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "\"What are you looking for?\"",
      "pid": "6",
      "position": {
        "x": "639",
        "y": "644"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "\"How's it going?\"",
      "pid": "7",
      "position": {
        "x": "789",
        "y": "644"
      }
    },
    {
      "text": "\"This problem looks wrong,\" she says.\n\n[[\"What's the problem?\"]]\n[[\"What do you mean?\"]]\n[[\"That sounds unlikely.\"]]",
      "links": [
        {
          "name": "\"What's the problem?\"",
          "link": "\"What's the problem?\"",
          "pid": "9"
        },
        {
          "name": "\"What do you mean?\"",
          "link": "\"What do you mean?\"",
          "pid": "10"
        },
        {
          "name": "\"That sounds unlikely.\"",
          "link": "\"That sounds unlikely.\"",
          "pid": "11"
        }
      ],
      "name": "Samantha",
      "pid": "8",
      "position": {
        "x": "432",
        "y": "509"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "\"What's the problem?\"",
      "pid": "9",
      "position": {
        "x": "309",
        "y": "686"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "\"What do you mean?\"",
      "pid": "10",
      "position": {
        "x": "434",
        "y": "720"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "\"That sounds unlikely.\"",
      "pid": "11",
      "position": {
        "x": "569",
        "y": "769"
      }
    }
  ],
  "name": "Alicia in Geometry",
  "startnode": "1",
  "creator": "Twine",
  "creator-version": "2.0.11",
  "ifid": "1CB6AE37-3E47-447E-8BB0-AE4FD79D2D9A"
};<commit_msg>Patch Twineimage link, which changed<commit_after>export default {
  "passages": [
    {
      "text": "It's 9th grade geometry.  This is the end part of a lesson on chords, secants, and solving algebraic expressions to solve geometry problems.\n\n\n\nStudents are working on goformative.com, working through three or four problems to practice the kinds of problems you just demonstrated on the board.\n\n[[Do nothing->nothing]]\n[[Check in on Alicia->Alicia]]\n[[Check in on Samantha->Samantha]]\n[[Check in on Ken->Ken]]\n[[Check in on Ryan->Ryan]]",
      "links": [
        {
          "name": "Do nothing",
          "link": "nothing",
          "pid": "2"
        },
        {
          "name": "Check in on Alicia",
          "link": "Alicia",
          "pid": "3"
        },
        {
          "name": "Check in on Samantha",
          "link": "Samantha",
          "pid": "8"
        },
        {
          "name": "Check in on Ken",
          "link": "Ken",
          "pid": "4"
        },
        {
          "name": "Check in on Ryan",
          "link": "Ryan",
          "pid": "5"
        }
      ],
      "name": "Geometry with chords",
      "pid": "1",
      "position": {
        "x": "512",
        "y": "299"
      }
    },
    {
      "text": "Really?  You should be helping students.\n\n[[Try again->Geometry with chords]]",
      "links": [
        {
          "name": "Try again",
          "link": "Geometry with chords",
          "pid": "1"
        }
      ],
      "name": "nothing",
      "pid": "2",
      "position": {
        "x": "290",
        "y": "475"
      }
    },
    {
      "text": "Alicia is looking through her notebook.\n\n[[\"What are you looking for?\"]]\n[[\"How's it going?\"]]",
      "links": [
        {
          "name": "\"What are you looking for?\"",
          "link": "\"What are you looking for?\"",
          "pid": "6"
        },
        {
          "name": "\"How's it going?\"",
          "link": "\"How's it going?\"",
          "pid": "7"
        }
      ],
      "name": "Alicia",
      "pid": "3",
      "position": {
        "x": "697",
        "y": "482"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "Ken",
      "pid": "4",
      "position": {
        "x": "875",
        "y": "308"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "Ryan",
      "pid": "5",
      "position": {
        "x": "962",
        "y": "450"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "\"What are you looking for?\"",
      "pid": "6",
      "position": {
        "x": "639",
        "y": "644"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "\"How's it going?\"",
      "pid": "7",
      "position": {
        "x": "789",
        "y": "644"
      }
    },
    {
      "text": "\"This problem looks wrong,\" she says.\n\n[[\"What's the problem?\"]]\n[[\"What do you mean?\"]]\n[[\"That sounds unlikely.\"]]",
      "links": [
        {
          "name": "\"What's the problem?\"",
          "link": "\"What's the problem?\"",
          "pid": "9"
        },
        {
          "name": "\"What do you mean?\"",
          "link": "\"What do you mean?\"",
          "pid": "10"
        },
        {
          "name": "\"That sounds unlikely.\"",
          "link": "\"That sounds unlikely.\"",
          "pid": "11"
        }
      ],
      "name": "Samantha",
      "pid": "8",
      "position": {
        "x": "432",
        "y": "509"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "\"What's the problem?\"",
      "pid": "9",
      "position": {
        "x": "309",
        "y": "686"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "\"What do you mean?\"",
      "pid": "10",
      "position": {
        "x": "434",
        "y": "720"
      }
    },
    {
      "text": "Double-click this passage to edit it.",
      "name": "\"That sounds unlikely.\"",
      "pid": "11",
      "position": {
        "x": "569",
        "y": "769"
      }
    }
  ],
  "name": "Alicia in Geometry",
  "startnode": "1",
  "creator": "Twine",
  "creator-version": "2.0.11",
  "ifid": "1CB6AE37-3E47-447E-8BB0-AE4FD79D2D9A"
};<|endoftext|>


<commit_before>var define = define || function() {
};

define(function() {
   return TP_Server;
});

function TP_Server() {
   var self = this;

   // private vars
   var protocol = {
      request : {},
      response : {},
      struct : {},
      message : {},
      consts : {},
      reverseMap : {}
   };
   var requestCounter = 0;
   var requestHandlers = [];
   var messageHandlers = [];
   var openHandlers = [];
   var closeHandlers = [];
   var socket;
   var simulator = null;
   var lastHeardFrom = 0;

   // public interface
   self.commsLog = false;
   self.register = register;
   self.registerConst = registerConst;
   self.addMessageHandler = addMessageHandler;
   self.send = send;
   self.sendRequest = sendRequest;
   self.connect = connect;
   self.disconnect = disconnect;
   self.nameOf = nameOf;
   self.consts = protocol.consts;
   self.connected = false;
   self.setSimulator = function(s) {
      simulator = s;
   };

   for (var i = 0; i < arguments.length; i++) {
      new arguments[i](self);
   }

   function register(type, contractName, structName, contractId, structId) {
      var map = protocol[type];
      var val = {
         contractId : contractId,
         structId : structId
      };
      map[contractName + "." + structName] = val;
      if (map[structName]) {
         map[structName] = 0;
      } else {
         map[structName] = val;
      }
      protocol.reverseMap["" + contractId + "." + structId] = contractName + "." + structName;
   }

   function registerConst(contractName, structName, constName, constValue) {
      var map = protocol["consts"];
      var o;
      if (structName != null && structName != "null") {
         o = map[contractName + "." + structName] || {};
         map[contractName + "." + structName] = o;
      } else {
         o = map[contractName] || {};
         map[contractName] = o;
      }
      o[constName] = constValue;
   }

   function consts(name) {
      return protocol["consts"][name];
   }

   function nameOf(arg1, arg2) {
      // call with either a single arg = object with _contractId and _structId
      // or with explicit contractId, structId
      var key;
      if (typeof arg2 !== 'undefined') {
         key = "" + arg1 + "." + arg2;
      } else {
         key = "" + arg1._contractId + "." + arg1._structId;
      }
      return protocol.reverseMap[key];
   }

   function addMessageHandler(message, handler) {
      var val = protocol.message[message];
      if (!val) {
         console.log("unknown message: " + message);
         return;
      }
      if (val === 0) {
         console.log("ambiguous message: " + message);
         return;
      }
      messageHandlers[val.contractId + "." + val.structId] = handler;
   }

   function send(request, args, toId) {
      var val = protocol.request[request];
      if (!val) {
         console.log("unknown request: " + request);
         return;
      }
      if (val === 0) {
         console.log("ambiguous request: " + request);
         return;
      }
      return sendRequest(val.contractId, val.structId, args, toId);
   }

   function sendRequest(contractId, structId, args, toId) {
      toId = typeof toId !== 'undefined' ? toId : 0;
      var requestId = requestCounter++;
      args._requestId = requestId;
      args._contractId = contractId;
      args._structId = structId;
      args._toId = toId;
      if (self.commsLog)
         logRequest(args);

      if (simulator != null) {
         var resp = simulator.request(request, args, toId);
         var i;
         for (i = 0; i < resp.messages.length; i++) {
            var mess = resp.messages[i];
            handleMessage(mess);
         }
         return {
            handle : function(func) {
               requestHandlers[requestId] = func;
               handleResponse(resp.response);
            }
         }
      }

      if (socket && socket.readyState == WebSocket.OPEN) {
         socket.send(JSON.stringify(args, null, 3))
      }

      return {
         handle : function(func) {
            requestHandlers[requestId] = func;
         }
      }
   }

   function connect(server, port) {
      port = typeof port !== 'undefined' ? port : 9903;
      if (!window.WebSocket) {
         window.WebSocket = window.MozWebSocket;
      }
      if (!window.WebSocket) {
         return null;
      }
      if (simulator != null) {
         return {
            listen : function(onOpen, onClose) {
               onOpen();
            }
         }
      }
      socket = new WebSocket("wss://" + server + ":" + port + "/sockets");
      socket.onopen = onSocketOpen;
      socket.onmessage = onSocketMessage;
      socket.onclose = onSocketClose;
      socket.onerror = onSocketError;
      return {
         listen : function(onOpen, onClose) {
            openHandlers = [];
            closeHandlers = [];
            openHandlers.push(onOpen);
            closeHandlers.push(onClose);
         }
      }
   }

   function disconnect() {
      if (self.commsLog)
         console.log("Disconnecting...");
      socket.close();
   }

   function logResponse(result) {
      var str = "";
      if (result.isError()) {
         str = "ERROR " + result.errorCode;
      } else if (result._contractId == 1 && result._structId == 2) {
         str = "SUCCESS";
      } else {
         str = JSON.stringify(result);
      }
      console.log("[%d] <- %s%s", result._requestId, nameOf(result), JSON.stringify(result, dropUnderscored));
   }

   function logRequest(result) {
      console.log("[%d] => %s%s", result._requestId, nameOf(result), JSON.stringify(result, dropUnderscored));
   }

   function logMessage(result) {
      console.log("[M:%d] <- %s%s", result._topicId, nameOf(result), JSON.stringify(result, dropUnderscored));
   }

   function dropUnderscored(key, value) {
      if (typeof key == 'string' || key instanceof String) {
         if (key.indexOf("_") == 0)
            return undefined;
      }
      return value;
   }

   function handleResponse(result) {
      result.isError = function() {
         return result._contractId == 1 && result._structId == 1;
      };
      if (self.commsLog)
         logResponse(result);
      var func = requestHandlers[result._requestId];
      if (func) {
         func(result);
      }
   }

   function handleMessage(result) {
      if (self.commsLog)
         logMessage(result);
      var func = messageHandlers[result._contractId + "." + result._structId];
      if (func) {
         func(result);
      }
   }

   // --- socket methods

   function onSocketOpen(event) {
      self.connected = true;
      if (self.commsLog)
         console.log("[socket] open")
      var i, array = openHandlers;
      for (i = 0; i < array.length; i++)
         array[i]();

      self.keepAlive = setInterval(function() {
         send("KeepAlive", {}, 1/*Core.DIRECT*/);
         var elapsed = Date.now() - lastHeardFrom;
         if (elapsed > 6000) {
            console.log("We haven't heard from the server in " + elapsed + " ms")
            // TODO: mark as bad connection
         }
         if (elapsed > 20000) {
            disconnect();
         }
      }, 5000);

   }

   function onSocketClose(event) {
      self.connected = false;
      if (self.keepAlive != null) {
         clearInterval(self.keepAlive);
         self.keepAlive = null;
      }
      if (self.commsLog)
         console.log("[socket] closed")
      var i, array = closeHandlers;
      for (i = 0; i < array.length; i++)
         array[i]();
   }

   function onSocketMessage(event) {
      var result = null;
      if (event.data.indexOf("{") == 0) {
         result = JSON.parse(event.data);
      } else {
         // TODO figure out how to communicate errors
         result = parseInt(event.data);
      }
      if (result._requestId != null) {
         handleResponse(result);
      } else if (result._topicId != null) {
         handleMessage(result);
      }
      lastHeardFrom = Date.now();
   }

   function onSocketError(event) {
      if (self.commsLog)
         console.log("[socket] error " + JSON.stringify(event));
   }

}
<commit_msg>more loggings<commit_after>var define = define || function() {
};

define(function() {
   return TP_Server;
});

function TP_Server() {
   var self = this;

   // private vars
   var protocol = {
      request : {},
      response : {},
      struct : {},
      message : {},
      consts : {},
      reverseMap : {}
   };
   var requestCounter = 0;
   var requestHandlers = [];
   var messageHandlers = [];
   var openHandlers = [];
   var closeHandlers = [];
   var socket;
   var simulator = null;
   var lastHeardFrom = 0;

   // public interface
   self.commsLog = false;
   self.register = register;
   self.registerConst = registerConst;
   self.addMessageHandler = addMessageHandler;
   self.send = send;
   self.sendRequest = sendRequest;
   self.connect = connect;
   self.disconnect = disconnect;
   self.nameOf = nameOf;
   self.consts = protocol.consts;
   self.connected = false;
   self.setSimulator = function(s) {
      simulator = s;
   };

   for (var i = 0; i < arguments.length; i++) {
      new arguments[i](self);
   }

   function register(type, contractName, structName, contractId, structId) {
      var map = protocol[type];
      var val = {
         contractId : contractId,
         structId : structId
      };
      map[contractName + "." + structName] = val;
      if (map[structName]) {
         map[structName] = 0;
      } else {
         map[structName] = val;
      }
      protocol.reverseMap["" + contractId + "." + structId] = contractName + "." + structName;
   }

   function registerConst(contractName, structName, constName, constValue) {
      var map = protocol["consts"];
      var o;
      if (structName != null && structName != "null") {
         o = map[contractName + "." + structName] || {};
         map[contractName + "." + structName] = o;
      } else {
         o = map[contractName] || {};
         map[contractName] = o;
      }
      o[constName] = constValue;
   }

   function consts(name) {
      return protocol["consts"][name];
   }

   function nameOf(arg1, arg2) {
      // call with either a single arg = object with _contractId and _structId
      // or with explicit contractId, structId
      var key;
      if (typeof arg2 !== 'undefined') {
         key = "" + arg1 + "." + arg2;
      } else {
         key = "" + arg1._contractId + "." + arg1._structId;
      }
      return protocol.reverseMap[key];
   }

   function addMessageHandler(message, handler) {
      var val = protocol.message[message];
      if (!val) {
         console.log("unknown message: " + message);
         return;
      }
      if (val === 0) {
         console.log("ambiguous message: " + message);
         return;
      }
      messageHandlers[val.contractId + "." + val.structId] = handler;
   }

   function send(request, args, toId) {
      var val = protocol.request[request];
      if (!val) {
         console.log("unknown request: " + request);
         return;
      }
      if (val === 0) {
         console.log("ambiguous request: " + request);
         return;
      }
      return sendRequest(val.contractId, val.structId, args, toId);
   }

   function sendRequest(contractId, structId, args, toId) {
      toId = typeof toId !== 'undefined' ? toId : 0;
      var requestId = requestCounter++;
      args._requestId = requestId;
      args._contractId = contractId;
      args._structId = structId;
      args._toId = toId;
      if (self.commsLog)
         logRequest(args);

      if (simulator != null) {
         var resp = simulator.request(request, args, toId);
         var i;
         for (i = 0; i < resp.messages.length; i++) {
            var mess = resp.messages[i];
            handleMessage(mess);
         }
         return {
            handle : function(func) {
               requestHandlers[requestId] = func;
               handleResponse(resp.response);
            }
         }
      }

      if (socket && socket.readyState == WebSocket.OPEN) {
         socket.send(JSON.stringify(args, null, 3))
      }

      return {
         handle : function(func) {
            requestHandlers[requestId] = func;
         }
      }
   }

   function connect(server, port) {
      port = typeof port !== 'undefined' ? port : 9903;
      if (!window.WebSocket) {
         window.WebSocket = window.MozWebSocket;
      }
      if (!window.WebSocket) {
         return null;
      }
      if (simulator != null) {
         return {
            listen : function(onOpen, onClose) {
               onOpen();
            }
         }
      }
      var url = "wss://" + server + ":" + port + "/sockets";
      console.log("Connecting to " + url);
      socket = new WebSocket(url);
      socket.onopen = onSocketOpen;
      socket.onmessage = onSocketMessage;
      socket.onclose = onSocketClose;
      socket.onerror = onSocketError;
      return {
         listen : function(onOpen, onClose) {
            openHandlers = [];
            closeHandlers = [];
            openHandlers.push(onOpen);
            closeHandlers.push(onClose);
         }
      }
   }

   function disconnect() {
      if (self.commsLog)
         console.log("Disconnecting...");
      socket.close();
   }

   function logResponse(result) {
      var str = "";
      if (result.isError()) {
         str = "ERROR " + result.errorCode;
      } else if (result._contractId == 1 && result._structId == 2) {
         str = "SUCCESS";
      } else {
         str = JSON.stringify(result);
      }
      console.log("[%d] <- %s%s", result._requestId, nameOf(result), JSON.stringify(result, dropUnderscored));
   }

   function logRequest(result) {
      console.log("[%d] => %s%s", result._requestId, nameOf(result), JSON.stringify(result, dropUnderscored));
   }

   function logMessage(result) {
      console.log("[M:%d] <- %s%s", result._topicId, nameOf(result), JSON.stringify(result, dropUnderscored));
   }

   function dropUnderscored(key, value) {
      if (typeof key == 'string' || key instanceof String) {
         if (key.indexOf("_") == 0)
            return undefined;
      }
      return value;
   }

   function handleResponse(result) {
      result.isError = function() {
         return result._contractId == 1 && result._structId == 1;
      };
      if (self.commsLog)
         logResponse(result);
      var func = requestHandlers[result._requestId];
      if (func) {
         func(result);
      }
   }

   function handleMessage(result) {
      if (self.commsLog)
         logMessage(result);
      var func = messageHandlers[result._contractId + "." + result._structId];
      if (func) {
         func(result);
      }
   }

   // --- socket methods

   function onSocketOpen(event) {
      self.connected = true;
      if (self.commsLog)
         console.log("[socket] open: " + socket.URL);
      var i, array = openHandlers;
      for (i = 0; i < array.length; i++)
         array[i]();

      self.keepAlive = setInterval(function() {
         send("KeepAlive", {}, 1/* Core.DIRECT */);
         var elapsed = Date.now() - lastHeardFrom;
         if (elapsed > 6000) {
            console.log("We haven't heard from the server in " + elapsed + " ms")
            // TODO: mark as bad connection
         }
         if (elapsed > 20000) {
            disconnect();
         }
      }, 5000);

   }

   function onSocketClose(event) {
      self.connected = false;
      if (self.keepAlive != null) {
         clearInterval(self.keepAlive);
         self.keepAlive = null;
      }
      if (self.commsLog)
         console.log("[socket] closed")
      var i, array = closeHandlers;
      for (i = 0; i < array.length; i++)
         array[i]();
   }

   function onSocketMessage(event) {
      var result = null;
      if (event.data.indexOf("{") == 0) {
         result = JSON.parse(event.data);
      } else {
         // TODO figure out how to communicate errors
         result = parseInt(event.data);
      }
      if (result._requestId != null) {
         handleResponse(result);
      } else if (result._topicId != null) {
         handleMessage(result);
      }
      lastHeardFrom = Date.now();
   }

   function onSocketError(event) {
      if (self.commsLog)
         console.log("[socket] error: " + JSON.stringify(event));
   }

}
<|endoftext|>


<commit_before>// Generated by Creer at 04:13PM on December 01, 2015 UTC, git hash: '1b69e788060071d644dd7b8745dca107577844e1'

var Class = require(__basedir + "/utilities/class");
var serializer = require(__basedir + "/gameplay/serializer");
var log = require(__basedir + "/gameplay/log");
var TwoPlayerGame = require(__basedir + "/gameplay/shared/twoPlayerGame");
var TurnBasedGame = require(__basedir + "/gameplay/shared/turnBasedGame");

//<<-- Creer-Merge: requires -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

var Chess = require("chess.js").Chess; // a very popular chess framework we will use to run all chess logic. This Game is basically an interface to chess.js

//<<-- /Creer-Merge: requires -->>

// @class Game: The traditional 8x8 chess board with pieces.
var Game = Class(TwoPlayerGame, TurnBasedGame, {
    /**
     * Initializes Games.
     *
     * @param {Object} data - a simple mapping passsed in to the constructor with whatever you sent with it. GameSettings are in here by key/value as well.
     */
    init: function(data) {
        TurnBasedGame.init.apply(this, arguments);
        TwoPlayerGame.init.apply(this, arguments);

        /**
         *  The list of moves in standard algebraic notation (SAN).
         *
         * @type {list.<string>}
         */
        this._addProperty("moves", serializer.defaultArray(data.moves));

        /**
         * All the uncaptured pieces in the game.
         *
         * @type {list.<Piece>}
         */
        this._addProperty("pieces", serializer.defaultArray(data.pieces));

        /**
         * How many turns until the game ends because no pawn has moved and no piece has been taken
         *
         * @type {number}
         */
        this._addProperty("turnsToStalemate", serializer.defaultInteger(data.turnsToStalemate));

        //<<-- Creer-Merge: init -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        this.chess = new Chess();
        this.maxTurns = 6000; // longest possible game without stalemate is 5,950

        //<<-- /Creer-Merge: init -->>
    },

    name: "Chess",
    webserverID: "MegaMinerAI-2015-Chess",

    /**
     * This is called when the game begins, once players are connected and ready to play, and game objects have been initialized. Anything in init() may not have the appropriate game objects created yet..
     */
    begin: function() {
        TurnBasedGame.begin.apply(this, arguments);
        TwoPlayerGame.begin.apply(this, arguments);

        //<<-- Creer-Merge: begin -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        this.players[0].color = "White";
        this.players[0].fileDirection = 1;
        this.players[1].color = "Black";
        this.players[1].fileDirection = -1;

        var backRank = {a: "Rook", b: "Knight", c: "Bishop", d: "Queen", e: "King", f: "Bishop", g: "Knight", h: "Rook"};

        for(var file = 1; file <= 8; file++) {
            var type = undefined;
            var lookupType = false;
            var owner = undefined;

            if(file === 2 || file === 7) {
                type = "Pawn";
            }
            else if(file === 1 || file === 8) {
                lookupType = true;
            }

            if(file <= 2) {
                owner = this.players[0];
            }
            else if(file >= 7) {
                owner = this.players[1];
            }

            if((type || lookupType) && owner) {
                for(var i = 0; i < 8; i++) {
                    var rank = String.fromCharCode(97 + i); // number to character, so 0 -> 'a', 1 -> 'b', etc.

                    if(lookupType) { // lookup type based on rank
                        type = backRank[rank];
                    }

                    var piece = this.create("Piece", {
                        type: type,
                        rank: rank,
                        file: file,
                        owner: owner,
                    });

                    this.pieces.push(piece);
                    piece.owner.pieces.push(piece);
                }
            }
        }

        this._generateMoves();

        //<<-- /Creer-Merge: begin -->>
    },

    /**
     * This is called when the game has started, after all the begin()s. This is a good spot to send orders.
     */
    _started: function() {
        TurnBasedGame._started.apply(this, arguments);
        TwoPlayerGame._started.apply(this, arguments);

        //<<-- Creer-Merge: _started -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.
        // any logic for _started can be put here
        //<<-- /Creer-Merge: _started -->>
    },

    //<<-- Creer-Merge: added-functions -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

    _generateMoves: function() {
        this.moves = this.chess.moves({verbose: true});
    },

    /**
     * Looks at the chess.js instance to update this Game's GameObjects. Should be called after a move is made.
     *
     * @param {Piece} piece - the piece that moved resulting in this update
     * @param {Object} result - result object returned from chess.move
     */
    update: function(piece, result) {
        var captured;
        if(result.flags === "c") { // normal capture
            captured = this._getPieceAt(result.to);
        }
        else if(result.flags === "e") { // en passant capture
            captured = this._getPieceAt(result.to[0] + result.from[1]);
        }

        if(captured) {
            captured.captured = true;
            captured.rank = "";
            captured.file = -1;
            this.pieces.removeElement(captured);
            captured.owner.pieces.removeElement(captured);
        }

        if(result.flags === "q" || result.flags === "k") { // queen or king side castle
            var rank = result.flags === "q" ? "a" : "h"; // queenside rook at rank "a", kingside at "h"
            var file = result.to[1];
            var rook = this._getPieceAt(rank + file);

            var rookRank = result.flags === "q" ? "c" : "g"; // queenside castle ends up at rank "c", kingside at "g"
            rook.rank = rookRank;
            rook.file = parseInt(file);
            rook.hasMoved = true;
        }

        piece.rank = result.to[0];
        piece.file = parseInt(result.to[1]);
        piece.hasMoved = true;
        this.moves.push(result.san);

        /*if(piece.type !== "Pawn" && !captured) {
            this.turnsToStalemate--;
            if(this.turnsToStalemate <= 0) {
                this.declareLosers(this.players, "100 turns passed with no pawn advancement or captures, draw.");
            }
        }*/

        if(result.promotion) {
            piece.type = ({
                n: "Knight",
                b: "Bishop",
                r: "Rook",
                q: "Queen",
            })[result.promotion.toLowerCase()];
        }

        piece.owner.madeMove = true;
        piece.owner.inCheck = false; // you can only move out of check
        piece.owner.otherPlayer.inCheck = this.chess.in_check();

        if(this.chess.in_checkmate()) {
            this.declareWinner(this.currentPlayer, "Checkmate!");
            this.declareLoser(this.currentPlayer.otherPlayer, "Checkmated");
        }
        if(this.chess.insufficient_material()) {
            this.declareLosers(this.players, "Draw - Insufficient material (K vs. K, K vs. KB, or K vs. KN)");
        }
        else if(this.chess.in_draw()) {
            this.declareLosers(this.players, "Draw - 50-move rule");
        }
        else if(this.chess.in_stalemate()) {
            this.declareLosers(this.players, "Stalemate - The side to move has been stalemated");
        }
        else if(this.chess.in_threefold_repetition()) {
            this.declareLosers(this.players, "Stalemate - Board position has occurred three or more times");
        }

        this._generateMoves();
    },

    _getPieceAt: function(pos) {
        for(var i = 0; i < this.pieces.length; i++) {
            var piece = this.pieces[i];
            if(pos === (piece.rank + piece.file)) {
                return piece;
            }
        }
    },

    nextTurn: function(/* ... */) {
        if(!this.currentPlayer.madeMove) {
            this.declareLoser(this.currentPlayer, "Did not make a move on turn number {}".format(this.currentTurn));
            this.declareWinner(this.currentPlayer.otherPlayer, "Other player did not make a move on turn number {}".format(this.currentTurn));
            return; // game over
        }

        for(var i = 0; i < this.players.length; i++) {
            this.players[i].madeMove = false;
        }

        return TurnBasedGame.nextTurn.apply(this, arguments);
    }

    //<<-- /Creer-Merge: added-functions -->>

});

module.exports = Game;
<commit_msg>Castling error<commit_after>// Generated by Creer at 04:13PM on December 01, 2015 UTC, git hash: '1b69e788060071d644dd7b8745dca107577844e1'

var Class = require(__basedir + "/utilities/class");
var serializer = require(__basedir + "/gameplay/serializer");
var log = require(__basedir + "/gameplay/log");
var TwoPlayerGame = require(__basedir + "/gameplay/shared/twoPlayerGame");
var TurnBasedGame = require(__basedir + "/gameplay/shared/turnBasedGame");

//<<-- Creer-Merge: requires -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

var Chess = require("chess.js").Chess; // a very popular chess framework we will use to run all chess logic. This Game is basically an interface to chess.js

//<<-- /Creer-Merge: requires -->>

// @class Game: The traditional 8x8 chess board with pieces.
var Game = Class(TwoPlayerGame, TurnBasedGame, {
    /**
     * Initializes Games.
     *
     * @param {Object} data - a simple mapping passsed in to the constructor with whatever you sent with it. GameSettings are in here by key/value as well.
     */
    init: function(data) {
        TurnBasedGame.init.apply(this, arguments);
        TwoPlayerGame.init.apply(this, arguments);

        /**
         *  The list of moves in standard algebraic notation (SAN).
         *
         * @type {list.<string>}
         */
        this._addProperty("moves", serializer.defaultArray(data.moves));

        /**
         * All the uncaptured pieces in the game.
         *
         * @type {list.<Piece>}
         */
        this._addProperty("pieces", serializer.defaultArray(data.pieces));

        /**
         * How many turns until the game ends because no pawn has moved and no piece has been taken
         *
         * @type {number}
         */
        this._addProperty("turnsToStalemate", serializer.defaultInteger(data.turnsToStalemate));

        //<<-- Creer-Merge: init -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        this.chess = new Chess();
        this.maxTurns = 6000; // longest possible game without stalemate is 5,950

        //<<-- /Creer-Merge: init -->>
    },

    name: "Chess",
    webserverID: "MegaMinerAI-2015-Chess",

    /**
     * This is called when the game begins, once players are connected and ready to play, and game objects have been initialized. Anything in init() may not have the appropriate game objects created yet..
     */
    begin: function() {
        TurnBasedGame.begin.apply(this, arguments);
        TwoPlayerGame.begin.apply(this, arguments);

        //<<-- Creer-Merge: begin -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        this.players[0].color = "White";
        this.players[0].fileDirection = 1;
        this.players[1].color = "Black";
        this.players[1].fileDirection = -1;

        var backRank = {a: "Rook", b: "Knight", c: "Bishop", d: "Queen", e: "King", f: "Bishop", g: "Knight", h: "Rook"};

        for(var file = 1; file <= 8; file++) {
            var type = undefined;
            var lookupType = false;
            var owner = undefined;

            if(file === 2 || file === 7) {
                type = "Pawn";
            }
            else if(file === 1 || file === 8) {
                lookupType = true;
            }

            if(file <= 2) {
                owner = this.players[0];
            }
            else if(file >= 7) {
                owner = this.players[1];
            }

            if((type || lookupType) && owner) {
                for(var i = 0; i < 8; i++) {
                    var rank = String.fromCharCode(97 + i); // number to character, so 0 -> 'a', 1 -> 'b', etc.

                    if(lookupType) { // lookup type based on rank
                        type = backRank[rank];
                    }

                    var piece = this.create("Piece", {
                        type: type,
                        rank: rank,
                        file: file,
                        owner: owner,
                    });

                    this.pieces.push(piece);
                    piece.owner.pieces.push(piece);
                }
            }
        }

        this._generateMoves();

        //<<-- /Creer-Merge: begin -->>
    },

    /**
     * This is called when the game has started, after all the begin()s. This is a good spot to send orders.
     */
    _started: function() {
        TurnBasedGame._started.apply(this, arguments);
        TwoPlayerGame._started.apply(this, arguments);

        //<<-- Creer-Merge: _started -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.
        // any logic for _started can be put here
        //<<-- /Creer-Merge: _started -->>
    },

    //<<-- Creer-Merge: added-functions -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

    _generateMoves: function() {
        this.moves = this.chess.moves({verbose: true});
    },

    /**
     * Looks at the chess.js instance to update this Game's GameObjects. Should be called after a move is made.
     *
     * @param {Piece} piece - the piece that moved resulting in this update
     * @param {Object} result - result object returned from chess.move
     */
    update: function(piece, result) {
        var captured;
        if(result.flags === "c") { // normal capture
            captured = this._getPieceAt(result.to);
        }
        else if(result.flags === "e") { // en passant capture
            captured = this._getPieceAt(result.to[0] + result.from[1]);
        }

        if(captured) {
            captured.captured = true;
            captured.rank = "";
            captured.file = -1;
            this.pieces.removeElement(captured);
            captured.owner.pieces.removeElement(captured);
        }

        if(result.flags === "q" || result.flags === "k") { // queen or king side castle
            var rank = result.flags === "q" ? "a" : "h"; // queenside rook at rank "a", kingside at "h"
            var file = result.to[1];
            var rook = this._getPieceAt(rank + file);

            var rookRank = result.flags === "q" ? "d" : "f"; // queenside castle ends up at rank "c", kingside at "g"
            rook.rank = rookRank;
            rook.file = parseInt(file);
            rook.hasMoved = true;
        }

        piece.rank = result.to[0];
        piece.file = parseInt(result.to[1]);
        piece.hasMoved = true;
        this.moves.push(result.san);

        /*if(piece.type !== "Pawn" && !captured) {
            this.turnsToStalemate--;
            if(this.turnsToStalemate <= 0) {
                this.declareLosers(this.players, "100 turns passed with no pawn advancement or captures, draw.");
            }
        }*/

        if(result.promotion) {
            piece.type = ({
                n: "Knight",
                b: "Bishop",
                r: "Rook",
                q: "Queen",
            })[result.promotion.toLowerCase()];
        }

        piece.owner.madeMove = true;
        piece.owner.inCheck = false; // you can only move out of check
        piece.owner.otherPlayer.inCheck = this.chess.in_check();

        if(this.chess.in_checkmate()) {
            this.declareWinner(this.currentPlayer, "Checkmate!");
            this.declareLoser(this.currentPlayer.otherPlayer, "Checkmated");
        }
        if(this.chess.insufficient_material()) {
            this.declareLosers(this.players, "Draw - Insufficient material (K vs. K, K vs. KB, or K vs. KN)");
        }
        else if(this.chess.in_draw()) {
            this.declareLosers(this.players, "Draw - 50-move rule");
        }
        else if(this.chess.in_stalemate()) {
            this.declareLosers(this.players, "Stalemate - The side to move has been stalemated");
        }
        else if(this.chess.in_threefold_repetition()) {
            this.declareLosers(this.players, "Stalemate - Board position has occurred three or more times");
        }

        this._generateMoves();
    },

    _getPieceAt: function(pos) {
        for(var i = 0; i < this.pieces.length; i++) {
            var piece = this.pieces[i];
            if(pos === (piece.rank + piece.file)) {
                return piece;
            }
        }
    },

    nextTurn: function(/* ... */) {
        if(!this.currentPlayer.madeMove) {
            this.declareLoser(this.currentPlayer, "Did not make a move on turn number {}".format(this.currentTurn));
            this.declareWinner(this.currentPlayer.otherPlayer, "Other player did not make a move on turn number {}".format(this.currentTurn));
            return; // game over
        }

        for(var i = 0; i < this.players.length; i++) {
            this.players[i].madeMove = false;
        }

        return TurnBasedGame.nextTurn.apply(this, arguments);
    }

    //<<-- /Creer-Merge: added-functions -->>

});

module.exports = Game;
<|endoftext|>