<commit_before>/**
	_moon.CalendarPicker_ is a control that displays a monthly calendar, with the
	month name at the top and a grid of days, grouped into rows by week, below.

	The header buttons are used to navigate to the desired month; the desired day
	is selected by tapping on it.

	{kind: "moon.CalendarPicker", content: "Calendar Title"}
*/
enyo.kind({
	name: "moon.CalendarPickerDate",
	kind: "moon.Button",
	small: true,
	marquee: false,
	classes: "moon-calendar-picker-date enyo-unselectable",
	published: {
		value: null,
		color: 0
	},
	create: function() {
		this.inherited(arguments);
		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({
				type: "date",	//only format the date component, not the time
				date: "d",		//'d' is the date of month
				length: "short"	//it uses 2 chars to abbreviate properly
			});
		}
	},
	colorChanged: function(inOld) {
		if (this.color) {
			this.addClass("moon-calendar-picker-date-shadow");
		} else {
			this.removeClass("moon-calendar-picker-date-shadow");
		}
	},
	valueChanged: function() {
		if (typeof ilib !== "undefined") {
			var date = ilib.Date.newInstance({unixtime: (this.value.getDate() - 1) * (24*60*60*1000)});
			this.setContent(this._tf.format(date));
		} else {
			this.setContent(this.value.getDate());
		}
	}
});

enyo.kind({
	name: "moon.CalendarPicker",
	classes: "moon-calendar-picker",
	events: {
		/**
			Fires when the date changes.

			_inEvent.name_ contains the name of this control.

			_inEvent.value_ contains a standard JavaScript Date object representing
			the current date.
		*/
		onChange: ""
	},
	handlers: {
		ontap: "selectDate"
	},
	published: {
		/**
			ilib locale info instance. It gives information about the paarticular locale.
		*/
		ilibLocaleInfo: null,
		/**
			Current locale used for formatting. May be set after the control is
			created, in which case the control will be updated to reflect the
			new value.
		*/
		locale: "en_us",
		/**
			The current Date object. When a Date object is passed to _setValue_,
			the control is updated to reflect the new value. _getValue_ returns
			a Date object.
		*/
		value: null,
		/**
			The day of week that starts weeks in current locale.
			As a default, Sunday is the first day of week.
		*/
		firstDayOfWeek: 0,
		/**
			The maximum number of weeks to display in a screen.
			If this value is greater than 9, dates two months in the future may be
			shown. Unexpected input may result in errors.
		*/
		maxWeeks: 6,
		months: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"],
		days: ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]
	},
	components: [
		{name: "monthPicker", kind: "moon.SimplePicker"},
		{name: "days", kind: "enyo.Group"},
		{name: "dates", kind: "enyo.Group"}
	],
	create: function() {
		this.inherited(arguments);
		this.initMonthPicker();
		this.initCalendar();

		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({
				type: "date",	//only format the date component, not the time
				date: "w",		//'w' is the day of the week
				length: "short"	//it uses 2 chars to abbreviate properly
			});

			this.ilibLocaleInfo = new ilib.LocaleInfo();
			this.setLocale(this.ilibLocaleInfo.locale);
		} else {
			this.initDefaults();
		}
	},
	initDefaults: function() {
		this.setValue(this.value || new Date());
		//Attempt to use the ilib lib (assuming that it is loaded)
		if (typeof ilib !== "undefined") {
			var dayOfWeek = this.ilibLocaleInfo.getFirstDayOfWeek();
			this.setFirstDayOfWeek(dayOfWeek);
		} else {
			this.setupDays(this.days);
		}
	},
	/**
		Sets up days of the week from first day to last day.
		Initially, SUN is the first day and SAT is the last day.
	*/
	setupDays: function(days) {
		for(var i = 0; i < days.length; i++) {
			this.$.days.createComponent({
				kind: "moon.Button",
				classes: "moon-calendar-picker-day",
				disabled: true,
				content: days[i]
			});
		}
	},
	/**
		Populates SimplePicker with months of the year, from JAN to DEC.
	*/
	initMonthPicker: function() {
		var months = this.months;
		this.$.monthPicker.addClass("moon-simple-picker-button-bold");
		for (var i = 0; i < 12; i++) {
			this.$.monthPicker.createComponent(
				{content: months[i], classes: "picker-content"}
			);
		}
	},
	/**
		Compose calendar with number of calendarDate
	*/
	initCalendar: function() {
		if (!this.$.dates.controls.length) {
			for (var i = 1; i <= this.maxWeeks * 7; i++) {
				this.$.dates.createComponent({kind: "moon.CalendarPickerDate"});
			}
		}
	},
	parseDate: function() {
		if (this._tf) {
			return this._tf.format(new ilib.Date.GregDate({unixtime: this.value.getTime(), timezone:"UTC"}));
		} else {
			return this.months[this.value.getMonth()] + " " + this.value.getDate() + ", " + this.value.getFullYear();
		}
	},
	/**
		Sets up the first week of this month.
		Before the first day of this month, days from the previous month will be
		used to fill the calendar.
	*/
	updatePrevMonth: function() {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setDate(0);
		var thisYear = dt.getFullYear(),
			datesOfPrevMonth = dt.getDate(),
			dayOfLastDate = dt.getDay() - this.firstDayOfWeek,
			prevMonth = dt.getMonth();
		if (dayOfLastDate < 0) {
			dayOfLastDate += 7;
		}
		if (dayOfLastDate !== 6) {
			var dates = this.$.dates.getControls();
			for (var i = 0; i <= dayOfLastDate; i++) {
				dates[i].setValue(new Date(thisYear, prevMonth, datesOfPrevMonth - dayOfLastDate + i));
				dates[i].setColor(1);
			}
			return i;
		}
		return 0;
	},
	/**
		Sets up the last week of this month.
		After the last day of this month, days from the next month will be used to
		fill the calendar.
	*/
	updateNextMonth: function(startIndex) {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setMonth(dt.getMonth() + 1);
		var thisYear = dt.getFullYear(),
			nextMonth = dt.getMonth();
		var dates = this.$.dates.getControls();
		for (var i = 0; i < this.$.dates.controls.length - startIndex; i++) {
			dates[startIndex + i].setValue(new Date(thisYear, nextMonth, i + 1));
			dates[startIndex + i].setColor(1);
		}
	},
	updateDates: function() {
		var datesOfPrevMonth = this.updatePrevMonth();

		var thisYear = this.value.getFullYear(),
			thisMonth = this.value.getMonth();
		var	monthLength = this.getMonthLength(thisYear, thisMonth);
		var dates = this.$.dates.getControls();
		for (var i = 0; i < monthLength; i++) {
			dates[datesOfPrevMonth + i].setValue(new Date(thisYear, thisMonth, i + 1));
			dates[datesOfPrevMonth + i].setColor(0);
		}
		this.$.dates.setActive(dates[datesOfPrevMonth - 1 + this.value.getDate()]);
		this.updateNextMonth(datesOfPrevMonth + monthLength);
	},
	setYear: function(newYear) {
		var value = this.value,
			newValue,
			newMonthLength = this.getMonthLength(newYear, value.getMonth());
		if(newMonthLength < value.getDate()) {
			newValue = new Date(newYear, value.getMonth(), newMonthLength);
		} else {
			newValue = new Date(newYear, value.getMonth(), value.getDate());
		}
		this.setValue(newValue);
	},
	setMonth: function(newMonth) {
		var value = this.value,
			newValue,
			newMonthLength = this.getMonthLength(value.getFullYear(), newMonth - 1);
		if(newMonthLength < value.getDate()) {
			newValue = new Date(value.getFullYear(), newMonth - 1, newMonthLength);
		} else {
			newValue = new Date(value.getFullYear(), newMonth - 1, value.getDate());
		}
		this.setValue(newValue);
	},
	setDate: function(newDate) {
		var value = this.value,
			newValue,
			monthLength = this.getMonthLength(value.getFullYear(), value.getMonth());
		if(monthLength < newDate) {
			newValue = new Date(value.getFullYear(), value.getMonth(), monthLength);
		} else {
			newValue = new Date(value.getFullYear(), value.getMonth(), newDate);
		}
		this.setValue(newValue);
	},
	selectDate: function(inSender, inEvent) {
		if (inEvent.originator.owner.kind == "moon.CalendarPickerDate") {
			var newValue = inEvent.originator.owner.value;
			this.setValue(newValue);
		}
		return true;
	},
	/**
		Returns number of days in a particular month/year.
		@param inYear
		@param inMonth
		@return Number of dates in given year and month
	*/
	getMonthLength: function(inYear, inMonth) {
		if (typeof ilib !== "undefined") {
			var d = ilib.Date.newInstance({unixtime: this.value.getTime()});
			var cal = ilib.Cal.newInstance({name: d.getCalendar()});
			return cal.getMonLength(inMonth, inYear);
		} else {
			return 32 - new Date(inYear, inMonth, 32).getDate();
		}
	},
	localeChanged: function() {
		if (typeof ilib !== "undefined") {
			this.ilibLocaleInfo = new ilib.LocaleInfo(this.locale);
		}
		this.refresh();
		this.doChange({value: this.value});
	},
	valueChanged: function(inOld) {
		if (this.$.monthPicker.getSelectedIndex() != this.value.getMonth()) {
			this.$.monthPicker.setSelectedIndex(this.value.getMonth());
		}
		this.updateDates();
		if (this.value) {
			this.doChange({value: this.value});
		}
	},
	/**
		Sometimes first day of week is channged based on locale changing.
		In this case, destroy day label and reconsturct it.
		Create new ilib.Date instance with time of given day, and get Gregorian
		date instance that represents the first date of the week.

	*/
	firstDayOfWeekChanged: function() {
		this.$.days.destroyClientControls();

		var d = ilib.Date.newInstance({unixtime: this.value.getTime()});
		var firstDate = d.onOrBefore(this.firstDayOfWeek);
		var days = [];
		var firstTime = firstDate.getTime();	//get unix time
		for(var i = 0; i < 7; i++) {
			var date = ilib.Date.newInstance({unixtime: i*(24*60*60*1000) + firstTime});
			days.push(this._tf.format(date));
		}
		this.days = days;
		this.setupDays(this.days);
		this.updateDates();
	},
	refresh: function(){
		this.destroyClientControls();
		this.initDefaults();
		this.render();
	}
});<commit_msg>GF-5466: Add handler for monthPicker's changing event<commit_after>/**
	_moon.CalendarPicker_ is a control that displays a monthly calendar, with the
	month name at the top and a grid of days, grouped into rows by week, below.

	The header buttons are used to navigate to the desired month; the desired day
	is selected by tapping on it.

	{kind: "moon.CalendarPicker", content: "Calendar Title"}
*/
enyo.kind({
	name: "moon.CalendarPickerDate",
	kind: "moon.Button",
	small: true,
	marquee: false,
	classes: "moon-calendar-picker-date enyo-unselectable",
	published: {
		value: null,
		color: 0
	},
	create: function() {
		this.inherited(arguments);
		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({
				type: "date",	//only format the date component, not the time
				date: "d",		//'d' is the date of month
				length: "short"	//it uses 2 chars to abbreviate properly
			});
		}
	},
	colorChanged: function(inOld) {
		if (this.color) {
			this.addClass("moon-calendar-picker-date-shadow");
		} else {
			this.removeClass("moon-calendar-picker-date-shadow");
		}
	},
	valueChanged: function() {
		if (typeof ilib !== "undefined") {
			var date = ilib.Date.newInstance({unixtime: (this.value.getDate() - 1) * (24*60*60*1000)});
			this.setContent(this._tf.format(date));
		} else {
			this.setContent(this.value.getDate());
		}
	}
});

enyo.kind({
	name: "moon.CalendarPicker",
	classes: "moon-calendar-picker",
	events: {
		/**
			Fires when the date changes.

			_inEvent.name_ contains the name of this control.

			_inEvent.value_ contains a standard JavaScript Date object representing
			the current date.
		*/
		onChange: ""
	},
	handlers: {
		ontap: "selectDate"
	},
	published: {
		/**
			ilib locale info instance. It gives information about the paarticular locale.
		*/
		ilibLocaleInfo: null,
		/**
			Current locale used for formatting. May be set after the control is
			created, in which case the control will be updated to reflect the
			new value.
		*/
		locale: "en_us",
		/**
			The current Date object. When a Date object is passed to _setValue_,
			the control is updated to reflect the new value. _getValue_ returns
			a Date object.
		*/
		value: null,
		/**
			The day of week that starts weeks in current locale.
			As a default, Sunday is the first day of week.
		*/
		firstDayOfWeek: 0,
		/**
			The maximum number of weeks to display in a screen.
			If this value is greater than 9, dates two months in the future may be
			shown. Unexpected input may result in errors.
		*/
		maxWeeks: 6,
		months: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"],
		days: ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]
	},
	components: [
		{name: "monthPicker", kind: "moon.SimplePicker", onChange: "updateMonthPicker"},
		{name: "days", kind: "enyo.Group"},
		{name: "dates", kind: "enyo.Group"}
	],
	create: function() {
		this.inherited(arguments);
		this.initMonthPicker();
		this.initCalendar();

		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({
				type: "date",	//only format the date component, not the time
				date: "w",		//'w' is the day of the week
				length: "short"	//it uses 2 chars to abbreviate properly
			});

			this.ilibLocaleInfo = new ilib.LocaleInfo();
			this.setLocale(this.ilibLocaleInfo.locale);
		} else {
			this.initDefaults();
		}
	},
	initDefaults: function() {
		this.setValue(this.value || new Date());
		//Attempt to use the ilib lib (assuming that it is loaded)
		if (typeof ilib !== "undefined") {
			var dayOfWeek = this.ilibLocaleInfo.getFirstDayOfWeek();
			this.setFirstDayOfWeek(dayOfWeek);
		} else {
			this.setupDays(this.days);
		}
	},
	/**
		Sets up days of the week from first day to last day.
		Initially, SUN is the first day and SAT is the last day.
	*/
	setupDays: function(days) {
		for(var i = 0; i < days.length; i++) {
			this.$.days.createComponent({
				kind: "moon.Button",
				classes: "moon-calendar-picker-day",
				disabled: true,
				content: days[i]
			});
		}
	},
	/**
		Populates SimplePicker with months of the year, from JAN to DEC.
	*/
	initMonthPicker: function() {
		var months = this.months;
		this.$.monthPicker.addClass("moon-simple-picker-button-bold");
		for (var i = 0; i < 12; i++) {
			this.$.monthPicker.createComponent(
				{content: months[i], classes: "picker-content"}
			);
		}
	},
	/**
		Compose calendar with number of calendarDate
	*/
	initCalendar: function() {
		if (!this.$.dates.controls.length) {
			for (var i = 1; i <= this.maxWeeks * 7; i++) {
				this.$.dates.createComponent({kind: "moon.CalendarPickerDate"});
			}
		}
	},
	parseDate: function() {
		if (this._tf) {
			return this._tf.format(new ilib.Date.GregDate({unixtime: this.value.getTime(), timezone:"UTC"}));
		} else {
			return this.months[this.value.getMonth()] + " " + this.value.getDate() + ", " + this.value.getFullYear();
		}
	},
	updateMonthPicker: function(inSender, inEvent) {
		var month = this.$.monthPicker.getSelectedIndex();
		this.value.setMonth(month);
	},
	/**
		Sets up the first week of this month.
		Before the first day of this month, days from the previous month will be
		used to fill the calendar.
	*/
	updatePrevMonth: function() {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setDate(0);
		var thisYear = dt.getFullYear(),
			datesOfPrevMonth = dt.getDate(),
			dayOfLastDate = dt.getDay() - this.firstDayOfWeek,
			prevMonth = dt.getMonth();
		if (dayOfLastDate < 0) {
			dayOfLastDate += 7;
		}
		if (dayOfLastDate !== 6) {
			var dates = this.$.dates.getControls();
			for (var i = 0; i <= dayOfLastDate; i++) {
				dates[i].setValue(new Date(thisYear, prevMonth, datesOfPrevMonth - dayOfLastDate + i));
				dates[i].setColor(1);
			}
			return i;
		}
		return 0;
	},
	/**
		Sets up the last week of this month.
		After the last day of this month, days from the next month will be used to
		fill the calendar.
	*/
	updateNextMonth: function(startIndex) {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setMonth(dt.getMonth() + 1);
		var thisYear = dt.getFullYear(),
			nextMonth = dt.getMonth();
		var dates = this.$.dates.getControls();
		for (var i = 0; i < this.$.dates.controls.length - startIndex; i++) {
			dates[startIndex + i].setValue(new Date(thisYear, nextMonth, i + 1));
			dates[startIndex + i].setColor(1);
		}
	},
	updateDates: function() {
		var datesOfPrevMonth = this.updatePrevMonth();

		var thisYear = this.value.getFullYear(),
			thisMonth = this.value.getMonth();
		var	monthLength = this.getMonthLength(thisYear, thisMonth);
		var dates = this.$.dates.getControls();
		for (var i = 0; i < monthLength; i++) {
			dates[datesOfPrevMonth + i].setValue(new Date(thisYear, thisMonth, i + 1));
			dates[datesOfPrevMonth + i].setColor(0);
		}
		this.$.dates.setActive(dates[datesOfPrevMonth - 1 + this.value.getDate()]);
		this.updateNextMonth(datesOfPrevMonth + monthLength);
	},
	setYear: function(newYear) {
		var value = this.value,
			newValue,
			newMonthLength = this.getMonthLength(newYear, value.getMonth());
		if(newMonthLength < value.getDate()) {
			newValue = new Date(newYear, value.getMonth(), newMonthLength);
		} else {
			newValue = new Date(newYear, value.getMonth(), value.getDate());
		}
		this.setValue(newValue);
	},
	setMonth: function(newMonth) {
		var value = this.value,
			newValue,
			newMonthLength = this.getMonthLength(value.getFullYear(), newMonth - 1);
		if(newMonthLength < value.getDate()) {
			newValue = new Date(value.getFullYear(), newMonth - 1, newMonthLength);
		} else {
			newValue = new Date(value.getFullYear(), newMonth - 1, value.getDate());
		}
		this.setValue(newValue);
	},
	setDate: function(newDate) {
		var value = this.value,
			newValue,
			monthLength = this.getMonthLength(value.getFullYear(), value.getMonth());
		if(monthLength < newDate) {
			newValue = new Date(value.getFullYear(), value.getMonth(), monthLength);
		} else {
			newValue = new Date(value.getFullYear(), value.getMonth(), newDate);
		}
		this.setValue(newValue);
	},
	selectDate: function(inSender, inEvent) {
		if (inEvent.originator.owner.kind == "moon.CalendarPickerDate") {
			var newValue = inEvent.originator.owner.value;
			this.setValue(newValue);
		}
		return true;
	},
	/**
		Returns number of days in a particular month/year.
		@param inYear
		@param inMonth
		@return Number of dates in given year and month
	*/
	getMonthLength: function(inYear, inMonth) {
		if (typeof ilib !== "undefined") {
			var d = ilib.Date.newInstance({unixtime: this.value.getTime()});
			var cal = ilib.Cal.newInstance({name: d.getCalendar()});
			return cal.getMonLength(inMonth, inYear);
		} else {
			return 32 - new Date(inYear, inMonth, 32).getDate();
		}
	},
	localeChanged: function() {
		if (typeof ilib !== "undefined") {
			this.ilibLocaleInfo = new ilib.LocaleInfo(this.locale);
		}
		this.refresh();
		this.doChange({value: this.value});
	},
	valueChanged: function(inOld) {
		if (this.$.monthPicker.getSelectedIndex() != this.value.getMonth()) {
			this.$.monthPicker.setSelectedIndex(this.value.getMonth());
		}
		this.updateDates();
		if (this.value) {
			this.doChange({value: this.value});
		}
	},
	/**
		Sometimes first day of week is channged based on locale changing.
		In this case, destroy day label and reconsturct it.
		Create new ilib.Date instance with time of given day, and get Gregorian
		date instance that represents the first date of the week.

	*/
	firstDayOfWeekChanged: function() {
		this.$.days.destroyClientControls();

		var d = ilib.Date.newInstance({unixtime: this.value.getTime()});
		var firstDate = d.onOrBefore(this.firstDayOfWeek);
		var days = [];
		var firstTime = firstDate.getTime();	//get unix time
		for(var i = 0; i < 7; i++) {
			var date = ilib.Date.newInstance({unixtime: i*(24*60*60*1000) + firstTime});
			days.push(this._tf.format(date));
		}
		this.days = days;
		this.setupDays(this.days);
		this.updateDates();
	},
	refresh: function(){
		this.destroyClientControls();
		this.initDefaults();
		this.render();
	}
});<|endoftext|>


<commit_before>/**
	_moon.CalendarPicker_ is a control that displays a monthly calendar, with the
	month name at the top and a grid of days, grouped into rows by week, below.

	The header buttons are used to navigate to the desired month; the desired day
	is selected by tapping on it.

	{kind: "moon.CalendarPicker", content: "Calendar Title"}
*/
enyo.kind({
	name: "moon.CalendarPickerDate",
	kind: "moon.Button",
	small: true,
	marquee: false,
	classes: "moon-calendar-picker-date enyo-unselectable",
	published: {
		value: null,
		color: 0
	},
	colorChanged: function(inOld) {
		if (this.color) {
			this.addClass("moon-calendar-picker-date-shadow");
		} else {
			this.removeClass("moon-calendar-picker-date-shadow");
		}
	},
	valueChanged: function() {
		this.setContent(this.value.getDate());
	}
});

enyo.kind({
	name: "moon.CalendarPicker",
	classes: "moon-calendar-picker",
	events: {
		/**
			Fires when the date changes.

			_inEvent.name_ contains the name of this control.

			_inEvent.value_ contains a standard JavaScript Date object representing
			the current date.
		*/
		onChange: ""
	},
	handlers: {
		ontap: "selectDate",
		//* Handler for _onChange_ events coming from constituent controls
		onChange: "updateSimplePicker"
	},
	published: {
		/**
			Current locale used for formatting. May be set after the control is
			created, in which case the control will be updated to reflect the
			new value.
		*/
		locale: "en_us",
		/**
			The current Date object. When a Date object is passed to _setValue_,
			the control is updated to reflect the new value. _getValue_ returns
			a Date object.
		*/
		value: null,
		/**
			The day of week that starts weeks in current locale.
			As a default, Sunday is the first day of week.
		*/
		firstDayOfWeek: 0,
		/**
			The maximum number of weeks to display in a screen.
			If this value is greater than 9, dates two months in the future may be
			shown. Unexpected input may result in errors.
		*/
		maxWeeks: 6,
		months: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"],
		days: ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]
	},
	components: [
		{name: "simplePicker", kind: "moon.SimplePicker", classes: "moon-calendar-picker-simplepicker"},
		{name: "days", kind: "enyo.Group"},
		{name: "dates", kind: "enyo.Group"}
	],
	create: function() {
		this.inherited(arguments);
		if (typeof ilib !== "undefined") {
			this.locale = this.locale || ilib.getLocale();
		}
		this.setupSimplePicker();
		this.setupDays(this.days);
		this.setupCalendar();
		this.initDefaults();
	},
	initDefaults: function() {
		this.setValue(this.value || new Date());
		//Attempt to use the ilib lib (assuming that it is loaded)
		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({locale:this.locale});
			var localeInfo = new ilib.LocaleInfo(this.locale);
			this.setFirstDayOfWeek(localeInfo.getFirstDayOfWeek());
		}
	},
	/**
		Sets up days of the week from first day to last day.
		Initially, SUN is the first day and SAT is the last day.
	*/
	setupDays: function(days) {
		for(var i = 0; i < days.length; i++) {
			this.$.days.createComponent({
				kind: "moon.Button",
				classes: "moon-calendar-picker-day",
				disabled: true,
				content: days[i]
			});
		}
	},
	/**
		Populates SimplePicker with months of the year, from JAN to DEC.
	*/
	setupSimplePicker: function() {
		var months = this.months;
		this.$.simplePicker.addClass("moon-simple-picker-button-bold");
		for (var i = 0; i < 12; i++) {
			this.$.simplePicker.createComponent(
				{content: months[i], classes: "picker-content"}
			);
		}
	},
	/**
		Compose calendar with number of calendarDate
	*/
	setupCalendar: function() {
		if (!this.$.dates.controls.length) {
			for (var i = 1; i <= this.maxWeeks * 7; i++) {
				this.$.dates.createComponent({kind: "moon.CalendarPickerDate"});
			}
		}
	},
	parseDate: function() {
		if (this._tf) {
			return this._tf.format(new ilib.Date.GregDate({unixtime: this.value.getTime(), timezone:"UTC"}));
		} else {
			return this.months[this.value.getMonth()] + " " + this.value.getDate() + ", " + this.value.getFullYear();
		}
	},
	/**
		Sets up the first week of this month.
		Before the first day of this month, days from the previous month will be
		used to fill the calendar.
	*/
	updatePrevMonth: function() {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setDate(0);
		var thisYear = dt.getFullYear(),
			datesOfPrevMonth = dt.getDate(),
			dayOfLastDate = dt.getDay() - this.firstDayOfWeek,
			prevMonth = dt.getMonth();
		if (dayOfLastDate < 0) {
			dayOfLastDate += 7;
		}
		if (dayOfLastDate !== 6) {
			var dates = this.$.dates.getControls();
			for (var i = 0; i <= dayOfLastDate; i++) {
				dates[i].setValue(new Date(thisYear, prevMonth, datesOfPrevMonth - dayOfLastDate + i));
				dates[i].setColor(1);
			}
			return i;
		}
		return 0;
	},
	/**
		Sets up the last week of this month.
		After the last day of this month, days from the next month will be used to
		fill the calendar.
	*/
	updateNextMonth: function(startIndex) {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setMonth(dt.getMonth() + 1);
		var thisYear = dt.getFullYear(),
			nextMonth = dt.getMonth();
		var dates = this.$.dates.getControls();
		for (var i = 0; i < this.$.dates.controls.length - startIndex; i++) {
			dates[startIndex + i].setValue(new Date(thisYear, nextMonth, i + 1));
			dates[startIndex + i].setColor(1);
		}
	},
	updateDates: function() {
		var datesOfPrevMonth = this.updatePrevMonth();

		var thisYear = this.value.getFullYear(),
			thisMonth = this.value.getMonth();
		var	monthLength = this.monthLength(thisYear, thisMonth);
		var dates = this.$.dates.getControls();
		for (var i = 0; i < monthLength; i++) {
			dates[datesOfPrevMonth + i].setValue(new Date(thisYear, thisMonth, i + 1));
			dates[datesOfPrevMonth + i].setColor(0);
		}
		this.$.dates.setActive(dates[datesOfPrevMonth - 1 + this.value.getDate()]);
		this.updateNextMonth(datesOfPrevMonth + monthLength);
	},
	setYear: function(newYear) {
		var value = this.value,
			newValue,
			newMonthLength = this.monthLength(newYear, value.getMonth());
		if(newMonthLength < value.getDate()) {
			newValue = new Date(newYear, value.getMonth(), newMonthLength);
		} else {
			newValue = new Date(newYear, value.getMonth(), value.getDate());
		}
		this.setValue(newValue);
	},
	setMonth: function(newMonth) {
		var value = this.value,
			newValue,
			newMonthLength = this.monthLength(value.getFullYear(), newMonth - 1);
		if(newMonthLength < value.getDate()) {
			newValue = new Date(value.getFullYear(), newMonth - 1, newMonthLength);
		} else {
			newValue = new Date(value.getFullYear(), newMonth - 1, value.getDate());
		}
		this.setValue(newValue);
	},
	setDate: function(newDate) {
		var value = this.value,
			newValue,
			monthLength = this.monthLength(value.getFullYear(), value.getMonth());
		if(monthLength < newDate) {
			newValue = new Date(value.getFullYear(), value.getMonth(), monthLength);
		} else {
			newValue = new Date(value.getFullYear(), value.getMonth(), newDate);
		}
		this.setValue(newValue);
	},
	selectDate: function(inSender, inEvent) {
		if (inEvent.originator.owner.kind == "moon.CalendarPickerDate") {
			var newValue = inEvent.originator.owner.value;
			this.setValue(newValue);
		}
		return true;
	},
	/**
		Returns number of days in a particular month/year.
	*/
	monthLength: function(inYear, inMonth) {
		return 32 - new Date(inYear, inMonth, 32).getDate();
	},
	/**
		Updates calendar when value of DatePicker changes.
	*/
	updateSimplePicker: function(inSender, inEvent) {
		//* Avoid onChange events coming from itself
		if (inEvent && inEvent.originator == this || inEvent.originator.kind == "Selection") {
			return;
		}
		var value = this.value,
			month = this.$.simplePicker.getSelectedIndex();
		//* Determine whether calender need to redraw or not
		if (month != this.value.getMonth()) {
			this.setValue(new Date(value.getFullYear(), month, value.getDate()));
		} else {
			this.value.setYear(value.getFullYear());
			this.value.setMonth(month);
			this.value.setDate(value.getDate());
		}
		return true;
	},
	localeChanged: function() {
		this.refresh();
		this.doChange({value: this.value});
	},
	valueChanged: function(inOld) {
		if (this.$.simplePicker.getSelectedIndex() != this.value.getMonth()) {
			this.$.simplePicker.setSelectedIndex(this.value.getMonth());
		}
		this.updateDates();
		if (this.value) {
			this.doChange({value: this.value});
		}
	},
	firstDayOfWeekChanged: function() {
		this.$.days.destroyClientControls();
		var days = [];
		for(var i = 0; i < 7; i++) {
			var index = i + this.firstDayOfWeek;
			if(index > 6) {
				index -= 7;
			}
			days.push(ilib.data.sysres["EEE" + index]);
		}
		this.days = days;
		this.setupDays(this.days);
		this.updateDates();
	},
	refresh: function(){
		this.destroyClientControls();
		this.initDefaults();
		this.render();
	}
});<commit_msg>GF-12815: Replace ilib internal fucntion with ilib API<commit_after>/**
	_moon.CalendarPicker_ is a control that displays a monthly calendar, with the
	month name at the top and a grid of days, grouped into rows by week, below.

	The header buttons are used to navigate to the desired month; the desired day
	is selected by tapping on it.

	{kind: "moon.CalendarPicker", content: "Calendar Title"}
*/
enyo.kind({
	name: "moon.CalendarPickerDate",
	kind: "moon.Button",
	small: true,
	marquee: false,
	classes: "moon-calendar-picker-date enyo-unselectable",
	published: {
		value: null,
		color: 0
	},
	colorChanged: function(inOld) {
		if (this.color) {
			this.addClass("moon-calendar-picker-date-shadow");
		} else {
			this.removeClass("moon-calendar-picker-date-shadow");
		}
	},
	valueChanged: function() {
		this.setContent(this.value.getDate());
	}
});

enyo.kind({
	name: "moon.CalendarPicker",
	classes: "moon-calendar-picker",
	events: {
		/**
			Fires when the date changes.

			_inEvent.name_ contains the name of this control.

			_inEvent.value_ contains a standard JavaScript Date object representing
			the current date.
		*/
		onChange: ""
	},
	handlers: {
		ontap: "selectDate",
		//* Handler for _onChange_ events coming from constituent controls
		onChange: "updateSimplePicker"
	},
	published: {
		/**
			Current locale used for formatting. May be set after the control is
			created, in which case the control will be updated to reflect the
			new value.
		*/
		locale: "en_us",
		/**
			The current Date object. When a Date object is passed to _setValue_,
			the control is updated to reflect the new value. _getValue_ returns
			a Date object.
		*/
		value: null,
		/**
			The day of week that starts weeks in current locale.
			As a default, Sunday is the first day of week.
		*/
		firstDayOfWeek: 0,
		/**
			The maximum number of weeks to display in a screen.
			If this value is greater than 9, dates two months in the future may be
			shown. Unexpected input may result in errors.
		*/
		maxWeeks: 6,
		months: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"],
		days: ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]
	},
	components: [
		{name: "simplePicker", kind: "moon.SimplePicker", classes: "moon-calendar-picker-simplepicker"},
		{name: "days", kind: "enyo.Group"},
		{name: "dates", kind: "enyo.Group"}
	],
	create: function() {
		this.inherited(arguments);
		this.setupSimplePicker();
		this.setupCalendar();

		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({
				type: "date",	//only format the date component, not the time
				date: "w",		//'w' is the day of the week
				length: "short"	//it uses 2 chars to abbreviate properly
			});

			this.localeInfo = new ilib.LocaleInfo();
			this.setLocale(this.localeInfo.locale);
		} else {
			this.initDefaults();
		}
	},
	initDefaults: function() {
		this.setValue(this.value || new Date());
		//Attempt to use the ilib lib (assuming that it is loaded)
		if (typeof ilib !== "undefined") {
			var dayOfWeek = this.localeInfo.getFirstDayOfWeek();
			this.setFirstDayOfWeek(dayOfWeek);
		} else {
			this.setupDays(this.days);
		}
	},
	/**
		Sets up days of the week from first day to last day.
		Initially, SUN is the first day and SAT is the last day.
	*/
	setupDays: function(days) {
		for(var i = 0; i < days.length; i++) {
			this.$.days.createComponent({
				kind: "moon.Button",
				classes: "moon-calendar-picker-day",
				disabled: true,
				content: days[i]
			});
		}
	},
	/**
		Populates SimplePicker with months of the year, from JAN to DEC.
	*/
	setupSimplePicker: function() {
		var months = this.months;
		this.$.simplePicker.addClass("moon-simple-picker-button-bold");
		for (var i = 0; i < 12; i++) {
			this.$.simplePicker.createComponent(
				{content: months[i], classes: "picker-content"}
			);
		}
	},
	/**
		Compose calendar with number of calendarDate
	*/
	setupCalendar: function() {
		if (!this.$.dates.controls.length) {
			for (var i = 1; i <= this.maxWeeks * 7; i++) {
				this.$.dates.createComponent({kind: "moon.CalendarPickerDate"});
			}
		}
	},
	parseDate: function() {
		if (this._tf) {
			return this._tf.format(new ilib.Date.GregDate({unixtime: this.value.getTime(), timezone:"UTC"}));
		} else {
			return this.months[this.value.getMonth()] + " " + this.value.getDate() + ", " + this.value.getFullYear();
		}
	},
	/**
		Sets up the first week of this month.
		Before the first day of this month, days from the previous month will be
		used to fill the calendar.
	*/
	updatePrevMonth: function() {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setDate(0);
		var thisYear = dt.getFullYear(),
			datesOfPrevMonth = dt.getDate(),
			dayOfLastDate = dt.getDay() - this.firstDayOfWeek,
			prevMonth = dt.getMonth();
		if (dayOfLastDate < 0) {
			dayOfLastDate += 7;
		}
		if (dayOfLastDate !== 6) {
			var dates = this.$.dates.getControls();
			for (var i = 0; i <= dayOfLastDate; i++) {
				dates[i].setValue(new Date(thisYear, prevMonth, datesOfPrevMonth - dayOfLastDate + i));
				dates[i].setColor(1);
			}
			return i;
		}
		return 0;
	},
	/**
		Sets up the last week of this month.
		After the last day of this month, days from the next month will be used to
		fill the calendar.
	*/
	updateNextMonth: function(startIndex) {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setMonth(dt.getMonth() + 1);
		var thisYear = dt.getFullYear(),
			nextMonth = dt.getMonth();
		var dates = this.$.dates.getControls();
		for (var i = 0; i < this.$.dates.controls.length - startIndex; i++) {
			dates[startIndex + i].setValue(new Date(thisYear, nextMonth, i + 1));
			dates[startIndex + i].setColor(1);
		}
	},
	updateDates: function() {
		var datesOfPrevMonth = this.updatePrevMonth();

		var thisYear = this.value.getFullYear(),
			thisMonth = this.value.getMonth();
		var	monthLength = this.monthLength(thisYear, thisMonth);
		var dates = this.$.dates.getControls();
		for (var i = 0; i < monthLength; i++) {
			dates[datesOfPrevMonth + i].setValue(new Date(thisYear, thisMonth, i + 1));
			dates[datesOfPrevMonth + i].setColor(0);
		}
		this.$.dates.setActive(dates[datesOfPrevMonth - 1 + this.value.getDate()]);
		this.updateNextMonth(datesOfPrevMonth + monthLength);
	},
	setYear: function(newYear) {
		var value = this.value,
			newValue,
			newMonthLength = this.monthLength(newYear, value.getMonth());
		if(newMonthLength < value.getDate()) {
			newValue = new Date(newYear, value.getMonth(), newMonthLength);
		} else {
			newValue = new Date(newYear, value.getMonth(), value.getDate());
		}
		this.setValue(newValue);
	},
	setMonth: function(newMonth) {
		var value = this.value,
			newValue,
			newMonthLength = this.monthLength(value.getFullYear(), newMonth - 1);
		if(newMonthLength < value.getDate()) {
			newValue = new Date(value.getFullYear(), newMonth - 1, newMonthLength);
		} else {
			newValue = new Date(value.getFullYear(), newMonth - 1, value.getDate());
		}
		this.setValue(newValue);
	},
	setDate: function(newDate) {
		var value = this.value,
			newValue,
			monthLength = this.monthLength(value.getFullYear(), value.getMonth());
		if(monthLength < newDate) {
			newValue = new Date(value.getFullYear(), value.getMonth(), monthLength);
		} else {
			newValue = new Date(value.getFullYear(), value.getMonth(), newDate);
		}
		this.setValue(newValue);
	},
	selectDate: function(inSender, inEvent) {
		if (inEvent.originator.owner.kind == "moon.CalendarPickerDate") {
			var newValue = inEvent.originator.owner.value;
			this.setValue(newValue);
		}
		return true;
	},
	/**
		Returns number of days in a particular month/year.
	*/
	monthLength: function(inYear, inMonth) {
		return 32 - new Date(inYear, inMonth, 32).getDate();
	},
	/**
		Updates calendar when value of DatePicker changes.
	*/
	updateSimplePicker: function(inSender, inEvent) {
		//* Avoid onChange events coming from itself
		if (inEvent && inEvent.originator == this || inEvent.originator.kind == "Selection") {
			return;
		}
		var value = this.value,
			month = this.$.simplePicker.getSelectedIndex();
		//* Determine whether calender need to redraw or not
		if (month != this.value.getMonth()) {
			this.setValue(new Date(value.getFullYear(), month, value.getDate()));
		} else {
			this.value.setYear(value.getFullYear());
			this.value.setMonth(month);
			this.value.setDate(value.getDate());
		}
		return true;
	},
	localeChanged: function() {
		this.refresh();
		this.doChange({value: this.value});
	},
	valueChanged: function(inOld) {
		if (this.$.simplePicker.getSelectedIndex() != this.value.getMonth()) {
			this.$.simplePicker.setSelectedIndex(this.value.getMonth());
		}
		this.updateDates();
		if (this.value) {
			this.doChange({value: this.value});
		}
	},
	/**
		Sometimes first day of week is channged based on locale changing.
		In this case, destroy day label and reconsturct it.
		Create new ilib.Date instance with time of given day, and get Gregorian
		date instance that represents the first date of the week.

	*/
	firstDayOfWeekChanged: function() {
		this.$.days.destroyClientControls();

		var d = ilib.Date.newInstance({unixtime: this.value.getTime()});
		var firstDate = d.onOrBefore(this.firstDayOfWeek);
		var days = [];
		var firstTime = firstDate.getTime();	//get unix time
		for(var i = 0; i < 7; i++) {
			var date = ilib.Date.newInstance({unixtime: i*(24*60*60*1000) + firstTime});
			days.push(this._tf.format(date));
		}
		this.days = days;
		this.setupDays(this.days);
		this.updateDates();
	},
	refresh: function(){
		this.destroyClientControls();
		this.initDefaults();
		this.render();
	}
});<|endoftext|>


<commit_before>/**
 * @fileoverview Komito Analytics is an enhancement for the most popular
 * analytics software. It unlocks power of digital analytics with additional
 * insights about visitor's behavior.
 *
 * @link http://google.github.io/styleguide/javascriptguide.xml
 * @link http://developers.google.com/closure/compiler/docs/js-for-compiler
 *
 * @link http://developers.google.com/analytics/devguides/collection/gajs
 * @link http://developers.google.com/analytics/devguides/collection/analyticsjs
 * @link http://tongji.baidu.com/open/api/more?p=ref_trackEvent
 *
 * Include script before closing </body> tag.
 *
 *   <script src="//datamart.github.io/Komito/komito.js"></script>
 *
 */

/**
 * Defines komito namespace.
 * @namespace
 */
var komito = {
  /**
   * Default tracking options.
   * @type {!Object.<string, number|string|Array>}
   * @see komito.config
   */
  DEFAULTS: {
    'trackTwitter': 1,
    'trackFacebook': 1,
    'trackLinkedIn': 1,
    'trackDownloads': 1,
    'trackOutbound': 1,
    'trackForms': 1,
    'trackUsers': 1,
    'trackActions': 1,
    'trackPrint': 1,
    'trackMedia': 1,
    'trackScroll': 1,
    'trackOrientation': 1,
    'trackAdblock': 0, // Experimental feature.
    'trackErrorPages': 0, // Experimental feature.
    'sendHeartbeat': 0, // Experimental feature.
    // 'trackingIds': ['List of tracking Ids'],
    'nonInteraction': [
      'form', 'print', 'scroll', 'video', 'orientation',
      'adblock', 'heartbeat'],
    'debugMode': /[?&]debug=1/.test(location.search)
  },

  /**
   * List of file extensions treated as downloadable.
   * @const {string}
   */
  DOWNLOADS: ',mp3,mp4,wma,mov,avi,wmv,mkv' + // Media
      ',eot,woff,ttf' +                       // Fonts
      ',txt,csv,tsv' +                        // Text
      ',pdf,doc,docx,xls,xlsx,ppt,pptx' +     // Docs
      ',zip,tar,tgz,bz2,gz,rar,dmg,pkg,7z' +  // Archives
      ',ida,exe,sh,bat,',                     // Other

  /**
   * File extension pattern.
   * @const {!RegExp}
   */
  EXT_PATTERN: /\.([0-9a-z]+)(?:[\?#]|$)/i,

  /**
   * @type {string}
   */
  GA_KEY: dom.context['GoogleAnalyticsObject'] || 'ga',

  /**
   * @const {number}
   */
  EVENT_ACTION_TYPE: 0,

  /**
   * @const {number}
   */
  SOCIAL_ACTION_TYPE: 1,

  /**
   * Performs trackers function execution.
   * @param {...*} var_args
   *
   * _gaq.push(['_trackEvent', category, action, opt_label, opt_value]);
   * ga('send', 'event', category, action, opt_label, opt_value);
   * __utmTrackEvent(category, action, opt_label, opt_value);
   * ClickTaleEvent('category:action:opt_label:opt_value');
   * _hmt.push(['_trackEvent', category, action, opt_label, opt_value]);
   *
   * _gaq.push(['_trackSocial', network, action, target]);
   * ga('send', 'social', network, action, target);
   * __utmTrackEvent('social:action', network, target);
   * ClickTaleEvent('social:action:network:target');
   * _hmt.push(['_trackEvent', 'social:action', network, target]);
   */
  track: function(var_args) {
    /** @type {!Array} */ var args = util.Array.toArray(arguments);
    /** @type {Array} */ var argv;
    args[0] = args[0] ? 'social' : 'event'; // Action type: 1 or 0;

    komito.sendGa_(args);
    komito.sendTagLoader_(args);
    argv = komito.convert_(args);
    komito.send_([dom.context], 'ClickTaleEvent', [argv.join(':')]);
    komito.send_([dom.context], '__utmTrackEvent', argv);
    dom.context['_hmt'] && komito.send_(
        [dom.context['_hmt']], 'push', [['_trackEvent'].concat(argv)]);
    komito.sendClassicGa_(args);
    komito.sendYm_(args);
  },

  /**
   * Initializes extension.
   * @private
   */
  init_: function() {
    if (!komito.initialized_) {
      komito.initialized_ = 1; // Initialize Komito Analytics only once.
      /** @type {string} */ var key = dom.document.readyState;

      function ready() {
        komito.config = dom.context['_komito'] || {};

        for (key in komito.DEFAULTS) {
          if (!(key in komito.config)) {
            komito.config[key] = komito.DEFAULTS[key];
          }
        }

        komito.trackers.dom.init();
        komito.trackers.media.init();
        komito.trackers.social.init();
      }

      'interactive' === key || 'complete' === key ?
          setTimeout(ready, 1E3) :
          dom.events.addEventListener(dom.context, 'DOMContentLoaded', ready);
    }
  },

  /**
   * Performs Adobe Tag Loader execution.
   * @param {Array} args The arguments to send.
   * @private
   */
  sendTagLoader_: function(args) {
    /** @type {Function} */ var loader = dom.context['TagLoader'];
    /** @type {Object} */ var tracker = dom.context['s'];
    /** @type {!Array} */ var vars = [];
    /** @type {number} */ var i = 1;
    /** @type {string} */ var key;

    if (loader && tracker && (tracker instanceof loader)) {
      for (; i < args.length; i++) {
        key = 'prop' + i;
        vars.push(key);
        tracker[key] = args[i];
      }

      tracker['linkTrackEvents'] = 'None';
      tracker['linkTrackVars'] = vars.join(',');
      komito.send_(
          [tracker], 'tl', // 'tl' is 'trackLink' method.
          [tracker, 'download' === args[1] ? 'd' : 'o', args[0]]);
    }
  },

  /**
   * Performs Google Analytics execution.
   * @param {!Array} args The arguments to send.
   * @private
   */
  sendGa_(args) {
    if ('function' === typeof dom.context[komito.GA_KEY]) {
      /** @type {Array.<Object>} */ var trackers =
          dom.context[komito.GA_KEY]['getAll'] &&
          dom.context[komito.GA_KEY]['getAll']();
      /** @type {*} */ var trackingIds = komito.config['trackingIds'];
      /** @type {!Object.<string, boolean>} */ var uniques = {};

      if (trackers && trackingIds) {
        if (!util.Array.isArray(trackingIds)) {
          trackingIds = ['' + trackingIds];
        }

        trackers = util.Array.filter(trackers, function(tracker) {
          /** @type {string} */ var trackingId = tracker['get']('trackingId');
          /** @type {boolean} */ var result = util.Array.contains(
              /** @type {!Array} */ (trackingIds), trackingId);
          return result;
        });
      }

      trackers = util.Array.filter(trackers, function(tracker) {
        /** @type {string} */ var trackingId = tracker['get']('trackingId');
        /** @type {boolean} */ var result = !uniques[trackingId];
        uniques[trackingId] = true;
        return result;
      });

      var data = komito.isNonInteraction_(args) ?
          args.concat([{'nonInteraction': 1}]) : args;
      trackers && komito.send_(trackers, 'send', data);
    }
  },

  /**
   * Performs Classic Google Analytics execution.
   * @param {!Array} args The arguments to send.
   * @private
   */
  sendClassicGa_: function(args) {
    if (dom.context['_gat'] || dom.context['_gaq']) {
      /** @type {Array.<Object>} */
      var trackers = dom.context['_gat'] &&
                     dom.context['_gat']['_getTrackers'] &&
                     dom.context['_gat']['_getTrackers']();
      var data = komito.isNonInteraction_(args) ? args.concat([1]) : args;
      data[0] = ({'social': '_trackSocial', 'event': '_trackEvent'})[data[0]];

      if (trackers) {
        komito.send_(trackers, data[0], data.slice(1));
      } else if (dom.context['_gaq']) {
        komito.send_([dom.context['_gaq']], 'push', [data]);
      }
    }
  },

  /**
   * Performs Yandex Metrica execution.
   * @param {!Array} args The arguments to send.
   * @see https://yandex.com/support/metrica/objects/_method-reference.html
   * @see https://yandex.com/support/metrica/objects/extlink.html
   * @see https://yandex.com/support/metrica/objects/file.html
   * @private
   */
  sendYm_: function(args) {
    /** @type {!Array.<Object>} */ var trackers = [];
    /** @type {string} */ var func = 'params';
    /** @type {string} */ var key;

    for (key in dom.context) {
      if (0 == key.indexOf('yaCounter')) {
        trackers.push(dom.context[key]);
      }
    }

    key = args[1];
    if ('outbound' === key) {
      func = 'extLink';
      args = [args.pop()]; // Outbound link URL.
    } else if ('download' === key) {
      func = 'file';
      args = [args.pop()]; // URL of downloaded file.
    }

    komito.send_(trackers, func, args);
  },

  /**
   * @param {!Array} trackers The list of trackers.
   * @param {string} func The function name.
   * @param {!Array} args The func arguments.
   * @private
   */
  send_: function(trackers, func, args) {
    /** @type {number} */ var length = trackers.length;
    /** @type {Object} */ var tracker;

    komito.debug_(func, args);
    for (; length;) {
      tracker = trackers[--length];
      if ('function' === typeof tracker[func])
        tracker[func].apply(tracker, args);
    }
  },

  /**
   * Converts 'social' interaction to 'event'.
   * @param {!Array} args The arguments to convert.
   * @return {!Array} Returns converted arguments.
   * @private
   */
  convert_: function(args) {
    /** @type {!Array} */ var argv = [].concat(args);

    if ('social' === argv[0]) {
      // from: ['social', 'network', 'action', 'target']
      // to: ['social', 'social:action', 'network', 'target']
      argv[1] = 'social' + ':' + argv.splice(2, 1, argv[1])[0];
    }

    return argv.slice(1);
  },

  /**
   * @param {...*} var_args Arguments to log.
   * @private
   */
  debug_: function(var_args) {
    /** @type {Console} */ var logger = dom.context.console;
    if (komito.config['debugMode'] && logger)
      logger.log.apply(logger, arguments);
  },

  /**
   * @param {!Array} args The arguments to send.
   * @return {boolean} Return 'true' if is non interaction event.
   * @private
   */
  isNonInteraction_: function(args) {
    var type = args[1].split(/\W/)[0]; // video:html5, cta:mailto, scroll.
    var list = komito.config['nonInteraction'];
    return util.Array.contains(/** @type {!Array} */ (list), type);
  },

  /**
   * The configuration options.
   * @type {!Object.<string, number|string|Array>}
   * @see komito.DEFAULTS
   * @see komito.init_
   */
  config: {}
};

'web.archive.org' !== location.hostname && komito.init_();
<commit_msg>Updated Adobe analytics tracking.<commit_after>/**
 * @fileoverview Komito Analytics is an enhancement for the most popular
 * analytics software. It unlocks power of digital analytics with additional
 * insights about visitor's behavior.
 *
 * @link http://google.github.io/styleguide/javascriptguide.xml
 * @link http://developers.google.com/closure/compiler/docs/js-for-compiler
 *
 * @link http://developers.google.com/analytics/devguides/collection/gajs
 * @link http://developers.google.com/analytics/devguides/collection/analyticsjs
 * @link http://tongji.baidu.com/open/api/more?p=ref_trackEvent
 *
 * Include script before closing </body> tag.
 *
 *   <script src="//datamart.github.io/Komito/komito.js"></script>
 *
 */

/**
 * Defines komito namespace.
 * @namespace
 */
var komito = {
  /**
   * Default tracking options.
   * @type {!Object.<string, number|string|Array>}
   * @see komito.config
   */
  DEFAULTS: {
    'trackTwitter': 1,
    'trackFacebook': 1,
    'trackLinkedIn': 1,
    'trackDownloads': 1,
    'trackOutbound': 1,
    'trackForms': 1,
    'trackUsers': 1,
    'trackActions': 1,
    'trackPrint': 1,
    'trackMedia': 1,
    'trackScroll': 1,
    'trackOrientation': 1,
    'trackAdblock': 0, // Experimental feature.
    'trackErrorPages': 0, // Experimental feature.
    'sendHeartbeat': 0, // Experimental feature.
    // 'trackingIds': ['List of tracking Ids'],
    'nonInteraction': [
      'form', 'print', 'scroll', 'video', 'orientation',
      'adblock', 'heartbeat'],
    'debugMode': /[?&]debug=1/.test(location.search)
  },

  /**
   * List of file extensions treated as downloadable.
   * @const {string}
   */
  DOWNLOADS: ',mp3,mp4,wma,mov,avi,wmv,mkv' + // Media
      ',eot,woff,ttf' +                       // Fonts
      ',txt,csv,tsv' +                        // Text
      ',pdf,doc,docx,xls,xlsx,ppt,pptx' +     // Docs
      ',zip,tar,tgz,bz2,gz,rar,dmg,pkg,7z' +  // Archives
      ',ida,exe,sh,bat,',                     // Other

  /**
   * File extension pattern.
   * @const {!RegExp}
   */
  EXT_PATTERN: /\.([0-9a-z]+)(?:[\?#]|$)/i,

  /**
   * @type {string}
   */
  GA_KEY: dom.context['GoogleAnalyticsObject'] || 'ga',

  /**
   * @const {number}
   */
  EVENT_ACTION_TYPE: 0,

  /**
   * @const {number}
   */
  SOCIAL_ACTION_TYPE: 1,

  /**
   * Performs trackers function execution.
   * @param {...*} var_args
   *
   * _gaq.push(['_trackEvent', category, action, opt_label, opt_value]);
   * ga('send', 'event', category, action, opt_label, opt_value);
   * __utmTrackEvent(category, action, opt_label, opt_value);
   * ClickTaleEvent('category:action:opt_label:opt_value');
   * _hmt.push(['_trackEvent', category, action, opt_label, opt_value]);
   *
   * _gaq.push(['_trackSocial', network, action, target]);
   * ga('send', 'social', network, action, target);
   * __utmTrackEvent('social:action', network, target);
   * ClickTaleEvent('social:action:network:target');
   * _hmt.push(['_trackEvent', 'social:action', network, target]);
   */
  track: function(var_args) {
    /** @type {!Array} */ var args = util.Array.toArray(arguments);
    /** @type {Array} */ var argv;
    args[0] = args[0] ? 'social' : 'event'; // Action type: 1 or 0;

    komito.sendGa_(args);
    komito.sendTagLoader_(args);
    argv = komito.convert_(args);
    komito.send_([dom.context], 'ClickTaleEvent', [argv.join(':')]);
    komito.send_([dom.context], '__utmTrackEvent', argv);
    dom.context['_hmt'] && komito.send_(
        [dom.context['_hmt']], 'push', [['_trackEvent'].concat(argv)]);
    komito.sendClassicGa_(args);
    komito.sendYm_(args);
  },

  /**
   * Initializes extension.
   * @private
   */
  init_: function() {
    if (!komito.initialized_) {
      komito.initialized_ = 1; // Initialize Komito Analytics only once.
      /** @type {string} */ var key = dom.document.readyState;

      function ready() {
        komito.config = dom.context['_komito'] || {};

        for (key in komito.DEFAULTS) {
          if (!(key in komito.config)) {
            komito.config[key] = komito.DEFAULTS[key];
          }
        }

        komito.trackers.dom.init();
        komito.trackers.media.init();
        komito.trackers.social.init();
      }

      'interactive' === key || 'complete' === key ?
          setTimeout(ready, 1E3) :
          dom.events.addEventListener(dom.context, 'DOMContentLoaded', ready);
    }
  },

  /**
   * Performs Adobe Tag Loader execution.
   * @param {Array} args The arguments to send.
   * @private
   */
  sendTagLoader_: function(args) {
    /** @type {Function} */ var loader = dom.context['TagLoader'] ||
                                         dom.context['AppMeasurement'];
    /** @type {Object} */ var tracker = dom.context['s'];
    /** @type {!Array} */ var vars = [];
    /** @type {number} */ var i = 1;
    /** @type {string} */ var key;

    if (loader && tracker && (tracker instanceof loader)) {
      for (; i < args.length; i++) {
        key = 'prop' + i;
        vars.push(key);
        tracker[key] = args[i];
      }

      tracker['linkTrackEvents'] = 'None';
      tracker['linkTrackVars'] = vars.join(',');
      komito.send_(
          [tracker], 'tl', // 'tl' is 'trackLink' method.
          [tracker, 'download' === args[1] ? 'd' : 'o', args[0]]);
    }
  },

  /**
   * Performs Google Analytics execution.
   * @param {!Array} args The arguments to send.
   * @private
   */
  sendGa_(args) {
    if ('function' === typeof dom.context[komito.GA_KEY]) {
      /** @type {Array.<Object>} */ var trackers =
          dom.context[komito.GA_KEY]['getAll'] &&
          dom.context[komito.GA_KEY]['getAll']();
      /** @type {*} */ var trackingIds = komito.config['trackingIds'];
      /** @type {!Object.<string, boolean>} */ var uniques = {};

      if (trackers && trackingIds) {
        if (!util.Array.isArray(trackingIds)) {
          trackingIds = ['' + trackingIds];
        }

        trackers = util.Array.filter(trackers, function(tracker) {
          /** @type {string} */ var trackingId = tracker['get']('trackingId');
          /** @type {boolean} */ var result = util.Array.contains(
              /** @type {!Array} */ (trackingIds), trackingId);
          return result;
        });
      }

      trackers = util.Array.filter(trackers, function(tracker) {
        /** @type {string} */ var trackingId = tracker['get']('trackingId');
        /** @type {boolean} */ var result = !uniques[trackingId];
        uniques[trackingId] = true;
        return result;
      });

      var data = komito.isNonInteraction_(args) ?
          args.concat([{'nonInteraction': 1}]) : args;
      trackers && komito.send_(trackers, 'send', data);
    }
  },

  /**
   * Performs Classic Google Analytics execution.
   * @param {!Array} args The arguments to send.
   * @private
   */
  sendClassicGa_: function(args) {
    if (dom.context['_gat'] || dom.context['_gaq']) {
      /** @type {Array.<Object>} */
      var trackers = dom.context['_gat'] &&
                     dom.context['_gat']['_getTrackers'] &&
                     dom.context['_gat']['_getTrackers']();
      var data = komito.isNonInteraction_(args) ? args.concat([1]) : args;
      data[0] = ({'social': '_trackSocial', 'event': '_trackEvent'})[data[0]];

      if (trackers) {
        komito.send_(trackers, data[0], data.slice(1));
      } else if (dom.context['_gaq']) {
        komito.send_([dom.context['_gaq']], 'push', [data]);
      }
    }
  },

  /**
   * Performs Yandex Metrica execution.
   * @param {!Array} args The arguments to send.
   * @see https://yandex.com/support/metrica/objects/_method-reference.html
   * @see https://yandex.com/support/metrica/objects/extlink.html
   * @see https://yandex.com/support/metrica/objects/file.html
   * @private
   */
  sendYm_: function(args) {
    /** @type {!Array.<Object>} */ var trackers = [];
    /** @type {string} */ var func = 'params';
    /** @type {string} */ var key;

    for (key in dom.context) {
      if (0 == key.indexOf('yaCounter')) {
        trackers.push(dom.context[key]);
      }
    }

    key = args[1];
    if ('outbound' === key) {
      func = 'extLink';
      args = [args.pop()]; // Outbound link URL.
    } else if ('download' === key) {
      func = 'file';
      args = [args.pop()]; // URL of downloaded file.
    }

    komito.send_(trackers, func, args);
  },

  /**
   * @param {!Array} trackers The list of trackers.
   * @param {string} func The function name.
   * @param {!Array} args The func arguments.
   * @private
   */
  send_: function(trackers, func, args) {
    /** @type {number} */ var length = trackers.length;
    /** @type {Object} */ var tracker;

    komito.debug_(func, args);
    for (; length;) {
      tracker = trackers[--length];
      if ('function' === typeof tracker[func])
        tracker[func].apply(tracker, args);
    }
  },

  /**
   * Converts 'social' interaction to 'event'.
   * @param {!Array} args The arguments to convert.
   * @return {!Array} Returns converted arguments.
   * @private
   */
  convert_: function(args) {
    /** @type {!Array} */ var argv = [].concat(args);

    if ('social' === argv[0]) {
      // from: ['social', 'network', 'action', 'target']
      // to: ['social', 'social:action', 'network', 'target']
      argv[1] = 'social' + ':' + argv.splice(2, 1, argv[1])[0];
    }

    return argv.slice(1);
  },

  /**
   * @param {...*} var_args Arguments to log.
   * @private
   */
  debug_: function(var_args) {
    /** @type {Console} */ var logger = dom.context.console;
    if (komito.config['debugMode'] && logger)
      logger.log.apply(logger, arguments);
  },

  /**
   * @param {!Array} args The arguments to send.
   * @return {boolean} Return 'true' if is non interaction event.
   * @private
   */
  isNonInteraction_: function(args) {
    var type = args[1].split(/\W/)[0]; // video:html5, cta:mailto, scroll.
    var list = komito.config['nonInteraction'];
    return util.Array.contains(/** @type {!Array} */ (list), type);
  },

  /**
   * The configuration options.
   * @type {!Object.<string, number|string|Array>}
   * @see komito.DEFAULTS
   * @see komito.init_
   */
  config: {}
};

'web.archive.org' !== location.hostname && komito.init_();
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'

import ExpandableList from './ExpandableList'
import List from './List'
import Close, { CloseIcon } from './Close'

import { focus } from './utility/focus'

// `PureComponent` is only available in React >= 15.3.0.
const PureComponent = React.PureComponent || React.Component

export default class ExpandableMenu extends PureComponent
{
	static propTypes =
	{
		// Component CSS class
		className  : PropTypes.string,

		// CSS style object
		style      : PropTypes.object,

		// `aria-label` for the "Close" button
		// (which is an "x" visible in fullscreen mode).
		closeLabel : PropTypes.string,

		// The "x" button icon that closes the `<Select/>`
		// in fullscreen mode on mobile devices.
		closeButtonIcon : PropTypes.oneOfType([PropTypes.func, PropTypes.oneOf([false])]).isRequired,

		// Must accept a `ref`.
		// Receives properties: `onClick`, `onKeyDown`, `onBlur`, `aria-expanded`.
		button : PropTypes.elementType,
		buttonProps : PropTypes.object,
		buttonClassName : PropTypes.string,

		toggleElement : PropTypes.node,

		// (deprecated, use `button` component instead)
		toggler : PropTypes.func,

		// (deprecated, use `buttonProps` instead)
		togglerAriaLabel : PropTypes.string,

		// (deprecated, use `buttonProps` instead)
		togglerAriaHasPopup : PropTypes.string,

		// (deprecated, use `buttonProps` instead)
		togglerClassName : PropTypes.string,

		buttonTitle: PropTypes.string,
		disabled: PropTypes.bool
	}

	static defaultProps =
	{
		// The "x" button icon that closes the `<Select/>`
		// in fullscreen mode on mobile devices.
		closeButtonIcon : CloseIcon
	}

	state = {}

	onExpand = () => this.setState({ isExpanded: true })

	onCollapse = ({ focusOut }) => {
		if (!focusOut) {
			this.focus()
		}
		this.setState({ isExpanded: false })
	}

	// `this.toggler` is deprecated.
	focus = () => focus(this.toggler || this.togglerNode)

	expand   = () => this.list.expand()
	collapse = () => this.list.collapse()
	toggle   = () => this.list.toggle()

	onFocusOut = () => {
		// `window.rruiCollapseOnFocusOut` can be used
		// for debugging expandable contents.
		if (window.rruiCollapseOnFocusOut !== false) {
			this.collapse()
		}
	}

	storeListRef = (ref) => this.list = ref

	// (legacy) (deprecated)
	// Is used to focus legacy togglers.
	storeTogglerRef = (ref) => this.toggler = ref

	// `this.togglerNode` is only used to determine
	// whether the focus is "inside" the component or "outside" of it.
	storeTogglerNode = (node) => this.togglerNode = node
	getTogglerNode = () => this.togglerNode

	render()
	{
		const
		{
			buttonTitle,
			disabled,
			style,
			className,
			toggler,
			button,
			buttonProps,
			buttonClassName,
			toggleElement,
			togglerAriaLabel,
			togglerAriaHasPopup,
			togglerClassName,
			children,
			...rest
		}
		= this.props

		const { isExpanded } = this.state

		let menuToggler
		let menuItems

		if (toggler || button || toggleElement) {
			// "button" string is used instead of a `DefaultTogglerButton`
			// so that the `ref` is the `<button/>` DOM Element.
			// (`.focus()`, `.contains()`).
			const TogglerButton = button || 'button'
			const togglerElement = toggleElement || (toggler ? React.createElement(toggler) : null)
			menuItems = children
			menuToggler = (
				<TogglerButton
					type={button ? undefined : 'button'}
					aria-haspopup={ togglerAriaHasPopup }
					aria-label={ togglerAriaLabel }
					className={ classNames(togglerClassName, buttonClassName, {
						'rrui__button-reset': toggleElement,
						'rrui__outline': toggleElement
					}) }
					{...buttonProps}
					ref={ this.storeTogglerNode }
					onClick={ this.onClick }
					onKeyDown={ this.onKeyDown }
					onBlur={ this.onBlur }
					aria-expanded={ isExpanded ? true : false }
					title={ buttonTitle }
					disabled={ disabled }>
					{ togglerElement }
				</TogglerButton>
			)
		} else {
			menuItems = React.Children.toArray(children)
			menuToggler = menuItems.shift()
			menuToggler = (
				<div
					ref={ this.storeTogglerNode }
					onClick={ this.onClick }
					onKeyDown={ this.onKeyDown }
					onBlur={ this.onBlur }>
					{ React.cloneElement(menuToggler, { ref : this.storeTogglerRef }) }
				</div>
			)
		}

		return (
			<div
				style={ style }
				className={ classNames('rrui__menu', className) }>

				{menuToggler}

				<ExpandableList
					{...rest}
					animation="fade-up"
					ref={this.storeListRef}
					aria-label={this.props['aria-label']}
					tabbable={false}
					scrollMaxItems={0}
					onCollapse={this.onCollapse}
					onExpand={this.onExpand}
					onFocusOut={this.onFocusOut}
					getTogglerNode={this.getTogglerNode}
					focusSelectedItem={false}
					className="rrui__shadow">
					{menuItems}
				</ExpandableList>
			</div>
		)
	}

	onBlur = (event) => this.list && this.list.onBlur(event)

	onClick = (event) => this.toggle()

	onKeyDown = (event) =>
	{
		if (event.defaultPrevented) {
			return
		}

		if (event.ctrlKey || event.altKey || event.shiftKey || event.metaKey) {
			return
		}

		switch (event.keyCode)
		{
			// "Up" arrow.
			// Select the previous item (if present).
			case 38:
			// "Down" arrow.
			// Select the next item (if present).
			case 40:
				return this.list.onKeyDown(event)

			// "Enter".
			case 13:
				// Submit containing `<form/>`.
				// Expand otherwise.
				this.expand()
				return event.preventDefault()
		}
	}
}<commit_msg>Refactored <ExpandableMenu/><commit_after>import React from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import createRef from 'react-create-ref'

import ExpandableList from './ExpandableList'
import List from './List'
import Close, { CloseIcon } from './Close'

import { focus } from './utility/focus'

// `PureComponent` is only available in React >= 15.3.0.
const PureComponent = React.PureComponent || React.Component

export default class ExpandableMenu extends PureComponent
{
	static propTypes =
	{
		// Component CSS class
		className  : PropTypes.string,

		// CSS style object
		style      : PropTypes.object,

		// `aria-label` for the "Close" button
		// (which is an "x" visible in fullscreen mode).
		closeLabel : PropTypes.string,

		// The "x" button icon that closes the `<Select/>`
		// in fullscreen mode on mobile devices.
		closeButtonIcon : PropTypes.oneOfType([PropTypes.func, PropTypes.oneOf([false])]).isRequired,

		// Must accept a `ref`.
		// Receives properties: `onClick`, `onKeyDown`, `onBlur`, `aria-expanded`.
		button : PropTypes.elementType,
		buttonProps : PropTypes.object,
		buttonClassName : PropTypes.string,

		toggleElement : PropTypes.node,

		// (deprecated, use `button` component instead)
		toggler : PropTypes.func,

		// (deprecated, use `buttonProps` instead)
		togglerAriaLabel : PropTypes.string,

		// (deprecated, use `buttonProps` instead)
		togglerAriaHasPopup : PropTypes.string,

		// (deprecated, use `buttonProps` instead)
		togglerClassName : PropTypes.string,

		buttonTitle: PropTypes.string,
		disabled: PropTypes.bool
	}

	static defaultProps =
	{
		// The "x" button icon that closes the `<Select/>`
		// in fullscreen mode on mobile devices.
		closeButtonIcon : CloseIcon
	}

	state = {}

	// The DOM Element reference is only used to determine
	// whether the focus is "inside" the component or "outside" of it.
	button = createRef()

	onExpand = () => this.setState({ isExpanded: true })

	onCollapse = ({ focusOut }) => {
		if (!focusOut) {
			this.focus()
		}
		this.setState({ isExpanded: false })
	}

	// `this.toggler` is deprecated.
	focus = () => focus(this.toggler || this.togglerNode)

	expand   = () => this.list.expand()
	collapse = () => this.list.collapse()
	toggle   = () => this.list.toggle()

	onFocusOut = () => {
		// `window.rruiCollapseOnFocusOut` can be used
		// for debugging expandable contents.
		if (window.rruiCollapseOnFocusOut !== false) {
			this.collapse()
		}
	}

	storeListRef = (ref) => this.list = ref

	// (legacy) (deprecated)
	// Is used to focus legacy togglers.
	storeTogglerRef = (ref) => this.toggler = ref

	getButton = () => this.button.current

	render()
	{
		const
		{
			buttonTitle,
			disabled,
			style,
			className,
			toggler,
			button,
			buttonProps,
			buttonClassName,
			toggleElement,
			togglerAriaLabel,
			togglerAriaHasPopup,
			togglerClassName,
			children,
			...rest
		}
		= this.props

		const { isExpanded } = this.state

		let menuToggler
		let menuItems

		if (toggler || button || toggleElement) {
			// "button" string is used instead of a `DefaultTogglerButton`
			// so that the `ref` is the `<button/>` DOM Element.
			// (`.focus()`, `.contains()`).
			const TogglerButton = button || 'button'
			const togglerElement = toggleElement || (toggler ? React.createElement(toggler) : null)
			menuItems = children
			menuToggler = (
				<TogglerButton
					type={button ? undefined : 'button'}
					aria-haspopup={ togglerAriaHasPopup }
					aria-label={ togglerAriaLabel }
					className={ classNames(togglerClassName, buttonClassName, {
						'rrui__button-reset': toggleElement,
						'rrui__outline': toggleElement
					}) }
					{...buttonProps}
					ref={ this.button }
					onClick={ this.onClick }
					onKeyDown={ this.onKeyDown }
					onBlur={ this.onBlur }
					aria-expanded={ isExpanded ? true : false }
					title={ buttonTitle }
					disabled={ disabled }>
					{ togglerElement }
				</TogglerButton>
			)
		} else {
			// Legacy way: the first child was the toggler.
			menuItems = React.Children.toArray(children)
			menuToggler = menuItems.shift()
			menuToggler = (
				<div
					ref={ this.button }
					onClick={ this.onClick }
					onKeyDown={ this.onKeyDown }
					onBlur={ this.onBlur }>
					{ React.cloneElement(menuToggler, { ref : this.storeTogglerRef }) }
				</div>
			)
		}

		return (
			<div
				style={ style }
				className={ classNames('rrui__menu', className) }>

				{menuToggler}

				<ExpandableList
					{...rest}
					animation="fade-up"
					ref={this.storeListRef}
					aria-label={this.props['aria-label']}
					tabbable={false}
					scrollMaxItems={0}
					onCollapse={this.onCollapse}
					onExpand={this.onExpand}
					onFocusOut={this.onFocusOut}
					getTogglerNode={this.getButton}
					focusSelectedItem={false}
					className="rrui__shadow">
					{menuItems}
				</ExpandableList>
			</div>
		)
	}

	onBlur = (event) => this.list && this.list.onBlur(event)

	onClick = (event) => this.toggle()

	onKeyDown = (event) =>
	{
		if (event.defaultPrevented) {
			return
		}

		if (event.ctrlKey || event.altKey || event.shiftKey || event.metaKey) {
			return
		}

		switch (event.keyCode)
		{
			// "Up" arrow.
			// Select the previous item (if present).
			case 38:
			// "Down" arrow.
			// Select the next item (if present).
			case 40:
				return this.list.onKeyDown(event)

			// "Enter".
			case 13:
				// Submit containing `<form/>`.
				// Expand otherwise.
				this.expand()
				return event.preventDefault()
		}
	}
}<|endoftext|>


<commit_before>// @flow

import React, { Component } from 'react';
import { Navigator, StyleSheet, BackAndroid, Platform } from 'react-native';

import Carousel from './components/Carousel';

const HARDWARE_BACK_PRESS_EVENT: string = 'hardwareBackPress';

const styles = StyleSheet.create({
  navigator: {
    flex: 1,
    backgroundColor: 'black',
  },
});

type Props = {
  onShow?: () => void;
  onDismiss?: () => void;
  dismissOnHardwareBackPress?: boolean;
  show?: boolean;
  navigatorStyle?: any;
  carouselStyle?: any;
  children: any;
}

const defaultProps = {
  onShow: () => {},
  onDismiss: () => {},
  dismissOnHardwareBackPress: true,
  navigatorStyle: null,
  carouselStyle: null,
  show: false,
};

class CarouselComponent extends Component {
  props: Props

  static defaultProps = defaultProps

  constructor(props: Props) {
    super(props);

    (this: any).renderScene = this.renderScene.bind(this);
    (this: any).show = this.show.bind(this);
    (this: any).dismiss = this.dismiss.bind(this);
    (this: any).didFocus = this.didFocus.bind(this);
  }

  componentDidMount() {
    if (this.props.show) {
      this.show(this.props.onShow);
    }

    if (Platform.OS === 'android') {
      const { dismissOnHardwareBackPress, onDismiss } = this.props;

      BackAndroid.addEventListener(HARDWARE_BACK_PRESS_EVENT, () => {
        if (dismissOnHardwareBackPress) {
          this.dismiss(onDismiss);
          return false;
        }
        return true;
      });
    }
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.show !== nextProps.show) {
      if (nextProps.show) {
        this.show(this.props.onShow);
        return;
      }
      this.dismiss(this.props.onDismiss);
    }
  }

  componentWillUnmount() {
    if (Platform.OS === 'android') {
      BackAndroid.removeEventListener(HARDWARE_BACK_PRESS_EVENT);
    }
  }

  show(callback?: Function = () => {}): void {
    this.navigator.push({ show: true });
    callback();
  }

  dismiss(callback?: Function = () => {}): void {
    this.navigator.pop();
    callback();
  }

  didFocus({ show }) {
    if (show === null) {
      return;
    }

    const callback = show ? this.props.onShow : this.props.onDismiss;
    callback();
  }

  configureScene() {
    return Navigator.SceneConfigs.FloatFromBottom;
  }

  renderScene(route, navigator) {
    if (route.show) {
      return (
        <Carousel
          {...this.props}
          style={this.props.carouselStyle}
          navigator={navigator}
        />
      );
    }
    return this.props.children;
  }

  render() {
    const { navigatorStyle } = this.props;

    return (
      <Navigator
        ref={(navigator) => { this.navigator = navigator; }}
        initialRoute={{ show: null }}
        configureScene={this.configureScene}
        renderScene={this.renderScene}
        onDidFocus={this.didFocus}
        style={[styles.navigator, navigatorStyle]}
      />
    );
  }
}

export default CarouselComponent;
<commit_msg>bug fix - should call onShow/onDimiss callback when Did Focus<commit_after>// @flow

import React, { Component } from 'react';
import { Navigator, StyleSheet, BackAndroid, Platform } from 'react-native';

import Carousel from './components/Carousel';

const HARDWARE_BACK_PRESS_EVENT: string = 'hardwareBackPress';

const styles = StyleSheet.create({
  navigator: {
    flex: 1,
    backgroundColor: 'black',
  },
});

type Props = {
  onShow?: () => void;
  onDismiss?: () => void;
  dismissOnHardwareBackPress?: boolean;
  show?: boolean;
  navigatorStyle?: any;
  carouselStyle?: any;
  children: any;
}

const defaultProps = {
  onShow: () => {},
  onDismiss: () => {},
  dismissOnHardwareBackPress: true,
  navigatorStyle: null,
  carouselStyle: null,
  show: null,
};

class CarouselComponent extends Component {
  props: Props

  static defaultProps = defaultProps

  constructor(props: Props) {
    super(props);

    this.state = {
      show: null,
    };

    (this: any).renderScene = this.renderScene.bind(this);
    (this: any).show = this.show.bind(this);
    (this: any).dismiss = this.dismiss.bind(this);
    (this: any).didFocus = this.didFocus.bind(this);
  }

  componentDidMount() {
    if (this.props.show) {
      this.show();
    }

    if (Platform.OS === 'android') {
      const { dismissOnHardwareBackPress } = this.props;

      BackAndroid.addEventListener(HARDWARE_BACK_PRESS_EVENT, () => {
        if (dismissOnHardwareBackPress) {
          this.dismiss();
          return false;
        }
        return true;
      });
    }
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.show !== nextProps.show) {
      if (nextProps.show) {
        this.show();
        return;
      }
      this.dismiss();
    }
  }

  componentWillUnmount() {
    if (Platform.OS === 'android') {
      BackAndroid.removeEventListener(HARDWARE_BACK_PRESS_EVENT);
    }
  }

  show(callback?: Function = () => {}): void {
    this.navigator.push({ show: true });
    this.setState({ show: true });
    callback();
  }

  dismiss(callback?: Function = () => {}): void {
    this.navigator.pop();
    this.setState({ show: false });
    callback();
  }

  didFocus() {
    const { show } = this.state;

    if (show === null) {
      return;
    }

    const callback = show ? this.props.onShow : this.props.onDismiss;
    callback();
    this.setState({ show: !show });
  }

  configureScene() {
    return Navigator.SceneConfigs.FloatFromBottom;
  }

  renderScene(route, navigator) {
    if (route.show) {
      return (
        <Carousel
          {...this.props}
          style={this.props.carouselStyle}
          navigator={navigator}
        />
      );
    }
    return this.props.children;
  }

  render() {
    const { navigatorStyle } = this.props;

    return (
      <Navigator
        ref={(navigator) => { this.navigator = navigator; }}
        initialRoute={{ show: null }}
        configureScene={this.configureScene}
        renderScene={this.renderScene}
        onDidFocus={this.didFocus}
        style={[styles.navigator, navigatorStyle]}
      />
    );
  }
}

export default CarouselComponent;
<|endoftext|>