<commit_before>
/* A component that renders a grid of whole-days that runs horizontally. There can be multiple rows, one per week.
----------------------------------------------------------------------------------------------------------------------*/

var DayGrid = Grid.extend({

	numbersVisible: false, // should render a row for day/week numbers? set by outside view. TODO: make internal
	bottomCoordPadding: 0, // hack for extending the hit area for the last row of the coordinate grid
	breakOnWeeks: null, // should create a new row for each week? set by outside view

	cellDates: null, // flat chronological array of each cell's dates
	dayToCellOffsets: null, // maps days offsets from grid's start date, to cell offsets

	rowEls: null, // set of fake row elements
	dayEls: null, // set of whole-day elements comprising the row's background
	helperEls: null, // set of cell skeleton elements for rendering the mock event "helper"

	// Renders the rows and columns into the component's `this.el`, which should already be assigned.
	// isRigid determins whether the individual rows should ignore the contents and be a constant height.
	// Relies on the view's colCnt and rowCnt. In the future, this component should probably be self-sufficient.
	renderDates: function(isRigid) {
		var view = this.view;
		var rowCnt = this.rowCnt;
		var colCnt = this.colCnt;
		var cellCnt = rowCnt * colCnt;
		var html = '';
		var row;
		var i, cell;

		for (row = 0; row < rowCnt; row++) {
			html += this.dayRowHtml(row, isRigid);
		}
		this.el.html(html);

		this.rowEls = this.el.find('.fc-row');
		this.dayEls = this.el.find('.fc-day');

		// trigger dayRender with each cell's element
		for (i = 0; i < cellCnt; i++) {
			cell = this.getCell(i);
			view.trigger('dayRender', null, cell.start, this.dayEls.eq(i));
		}
	},

	destroyDates: function() {
		this.destroySegPopover();
	},

	// Generates the HTML for a single row. `row` is the row number.
	dayRowHtml: function(row, isRigid) {
		var view = this.view;
		var classes = [ 'fc-row', 'fc-week', view.widgetContentClass ];

		if (isRigid) {
			classes.push('fc-rigid');
		}

		return '' +
			'
' +
				'
' +
					'
' +
						this.rowHtml('day', row) + // leverages RowRenderer. calls dayCellHtml()
					'
' +
				'
' +
				'
' +
					'
' +
						(this.numbersVisible ?
							'
' +
								this.rowHtml('number', row) + // leverages RowRenderer. View will define render method
							'
' :
							''
							) +
					'
' +
				'
' +
			'
';
	},

	// Renders the HTML for a whole-day cell. Will eventually end up in the day-row's background.
	// We go through a 'day' row type instead of just doing a 'bg' row type so that the View can do custom rendering
	// specifically for whole-day rows, whereas a 'bg' might also be used for other purposes (TimeGrid bg for example).
	dayCellHtml: function(cell) {
		return this.bgCellHtml(cell);
	},

	/* Options
	------------------------------------------------------------------------------------------------------------------*/

	// Computes a default column header formatting string if `colFormat` is not explicitly defined
	computeColHeadFormat: function() {
		if (this.rowCnt > 1) { // more than one week row. day numbers will be in each cell
			return 'ddd'; // "Sat"
		}
		else if (this.colCnt > 1) { // multiple days, so full single date string WON'T be in title text
			return this.view.opt('dayOfMonthFormat'); // "Sat 12/10"
		}
		else { // single day, so full single date string will probably be in title text
			return 'dddd'; // "Saturday"
		}
	},

	// Computes a default event time formatting string if `timeFormat` is not explicitly defined
	computeEventTimeFormat: function() {
		return this.view.opt('extraSmallTimeFormat'); // like "6p" or "6:30p"
	},

	// Computes a default `displayEventEnd` value if one is not expliclty defined
	computeDisplayEventEnd: function() {
		return this.colCnt == 1; // we'll likely have space if there's only one day
	},

	/* Cell System
	------------------------------------------------------------------------------------------------------------------*/

	// Initializes row/col information
	updateCells: function() {
		var cellDates;
		var firstDay;
		var rowCnt;
		var colCnt;

		this.updateCellDates(); // populates cellDates and dayToCellOffsets
		cellDates = this.cellDates;

		if (this.breakOnWeeks) {
			// count columns until the day-of-week repeats
			firstDay = cellDates[0].day();
			for (colCnt = 1; colCnt < cellDates.length; colCnt++) {
				if (cellDates[colCnt].day() == firstDay) {
					break;
				}
			}
			rowCnt = Math.ceil(cellDates.length / colCnt);
		}
		else {
			rowCnt = 1;
			colCnt = cellDates.length;
		}

		this.rowCnt = rowCnt;
		this.colCnt = colCnt;
	},

	// Populates cellDates and dayToCellOffsets
	updateCellDates: function() {
		var view = this.view;
		var date = this.start.clone();
		var dates = [];
		var offset = -1;
		var offsets = [];

		while (date.isBefore(this.end)) { // loop each day from start to end
			if (view.isHiddenDay(date)) {
				offsets.push(offset + 0.5); // mark that it's between offsets
			}
			else {
				offset++;
				offsets.push(offset);
				dates.push(date.clone());
			}
			date.add(1, 'days');
		}

		this.cellDates = dates;
		this.dayToCellOffsets = offsets;
	},

	// Given a cell object, generates a range object
	computeCellRange: function(cell) {
		var colCnt = this.colCnt;
		var index = cell.row * colCnt + (this.isRTL ? colCnt - cell.col - 1 : cell.col);
		var start = this.cellDates[index].clone();
		var end = start.clone().add(1, 'day');

		return { start: start, end: end };
	},

	// Retrieves the element representing the given row
	getRowEl: function(row) {
		return this.rowEls.eq(row);
	},

	// Retrieves the element representing the given column
	getColEl: function(col) {
		return this.dayEls.eq(col);
	},

	// Gets the whole-day element associated with the cell
	getCellDayEl: function(cell) {
		return this.dayEls.eq(cell.row * this.colCnt + cell.col);
	},

	// Overrides Grid's method for when row coordinates are computed
	computeRowCoords: function() {
		var rowCoords = Grid.prototype.computeRowCoords.call(this); // call the super-method

		// hack for extending last row (used by AgendaView)
		rowCoords[rowCoords.length - 1].bottom += this.bottomCoordPadding;

		return rowCoords;
	},

	/* Dates
	------------------------------------------------------------------------------------------------------------------*/

	// Slices up a date range by row into an array of segments
	rangeToSegs: function(range) {
		var isRTL = this.isRTL;
		var rowCnt = this.rowCnt;
		var colCnt = this.colCnt;
		var segs = [];
		var first, last; // inclusive cell-offset range for given range
		var row;
		var rowFirst, rowLast; // inclusive cell-offset range for current row
		var isStart, isEnd;
		var segFirst, segLast; // inclusive cell-offset range for segment
		var seg;

		range = this.view.computeDayRange(range); // make whole-day range, considering nextDayThreshold
		first = this.dateToCellOffset(range.start);
		last = this.dateToCellOffset(range.end.subtract(1, 'days')); // offset of inclusive end date

		for (row = 0; row < rowCnt; row++) {
			rowFirst = row * colCnt;
			rowLast = rowFirst + colCnt - 1;

			// intersect segment's offset range with the row's
			segFirst = Math.max(rowFirst, first);
			segLast = Math.min(rowLast, last);

			// deal with in-between indices
			segFirst = Math.ceil(segFirst); // in-between starts round to next cell
			segLast = Math.floor(segLast); // in-between ends round to prev cell

			if (segFirst <= segLast) { // was there any intersection with the current row?

				// must be matching integers to be the segment's start/end
				isStart = segFirst === first;
				isEnd = segLast === last;

				// translate offsets to be relative to start-of-row
				segFirst -= rowFirst;
				segLast -= rowFirst;

				seg = { row: row, isStart: isStart, isEnd: isEnd };
				if (isRTL) {
					seg.leftCol = colCnt - segLast - 1;
					seg.rightCol = colCnt - segFirst - 1;
				}
				else {
					seg.leftCol = segFirst;
					seg.rightCol = segLast;
				}
				segs.push(seg);
			}
		}

		return segs;
	},

	// Given a date, returns its chronolocial cell-offset from the first cell of the grid.
	// If the date lies between cells (because of hiddenDays), returns a floating-point value between offsets.
	// If before the first offset, returns a negative number.
	// If after the last offset, returns an offset past the last cell offset.
	// Only works for *start* dates of cells. Will not work for exclusive end dates for cells.
	dateToCellOffset: function(date) {
		var offsets = this.dayToCellOffsets;
		var day = date.diff(this.start, 'days');

		if (day < 0) {
			return offsets[0] - 1;
		}
		else if (day >= offsets.length) {
			return offsets[offsets.length - 1] + 1;
		}
		else {
			return offsets[day];
		}
	},

	/* Event Drag Visualization
	------------------------------------------------------------------------------------------------------------------*/
	// TODO: move to DayGrid.event, similar to what we did with Grid's drag methods

	// Renders a visual indication of an event or external element being dragged.
	// The dropLocation's end can be null. seg can be null. See Grid::renderDrag for more info.
	renderDrag: function(dropLocation, seg) {
		var opacity;

		// always render a highlight underneath
		this.renderHighlight(
			this.view.calendar.ensureVisibleEventRange(dropLocation) // needs to be a proper range
		);

		// if a segment from the same calendar but another component is being dragged, render a helper event
		if (seg && !seg.el.closest(this.el).length) {

			this.renderRangeHelper(dropLocation, seg);

			opacity = this.view.opt('dragOpacity');
			if (opacity !== undefined) {
				this.helperEls.css('opacity', opacity);
			}

			return true; // a helper has been rendered
		}
	},

	// Unrenders any visual indication of a hovering event
	destroyDrag: function() {
		this.destroyHighlight();
		this.destroyHelper();
	},

	/* Event Resize Visualization
	------------------------------------------------------------------------------------------------------------------*/

	// Renders a visual indication of an event being resized
	renderEventResize: function(range, seg) {
		this.renderHighlight(range);
		this.renderRangeHelper(range, seg);
	},

	// Unrenders a visual indication of an event being resized
	destroyEventResize: function() {
		this.destroyHighlight();
		this.destroyHelper();
	},

	/* Event Helper
	------------------------------------------------------------------------------------------------------------------*/

	// Renders a mock "helper" event. `sourceSeg` is the associated internal segment object. It can be null.
	renderHelper: function(event, sourceSeg) {
		var helperNodes = [];
		var segs = this.eventsToSegs([ event ]);
		var rowStructs;

		segs = this.renderFgSegEls(segs); // assigns each seg's el and returns a subset of segs that were rendered
		rowStructs = this.renderSegRows(segs);

		// inject each new event skeleton into each associated row
		this.rowEls.each(function(row, rowNode) {
			var rowEl = $(rowNode); // the .fc-row
			var skeletonEl = $('

'); // will be absolutely positioned
			var skeletonTop;

			// If there is an original segment, match the top position. Otherwise, put it at the row's top level
			if (sourceSeg && sourceSeg.row === row) {
				skeletonTop = sourceSeg.el.position().top;
			}
			else {
				skeletonTop = rowEl.find('.fc-content-skeleton tbody').position().top;
			}

			skeletonEl.css('top', skeletonTop)
				.find('table')
					.append(rowStructs[row].tbodyEl);

			rowEl.append(skeletonEl);
			helperNodes.push(skeletonEl[0]);
		});

		this.helperEls = $(helperNodes); // array -> jQuery set
	},

	// Unrenders any visual indication of a mock helper event
	destroyHelper: function() {
		if (this.helperEls) {
			this.helperEls.remove();
			this.helperEls = null;
		}
	},

	/* Fill System (highlight, background events, business hours)
	------------------------------------------------------------------------------------------------------------------*/

	fillSegTag: 'td', // override the default tag name

	// Renders a set of rectangles over the given segments of days.
	// Only returns segments that successfully rendered.
	renderFill: function(type, segs) {
		var nodes = [];
		var i, seg;
		var skeletonEl;

		segs = this.renderFillSegEls(type, segs); // assignes `.el` to each seg. returns successfully rendered segs

		for (i = 0; i < segs.length; i++) {
			seg = segs[i];
			skeletonEl = this.renderFillRow(type, seg);
			this.rowEls.eq(seg.row).append(skeletonEl);
			nodes.push(skeletonEl[0]);
		}

		this.elsByFill[type] = $(nodes);

		return segs;
	},

	// Generates the HTML needed for one row of a fill. Requires the seg's el to be rendered.
	renderFillRow: function(type, seg) {
		var colCnt = this.colCnt;
		var startCol = seg.leftCol;
		var endCol = seg.rightCol + 1;
		var skeletonEl;
		var trEl;

		skeletonEl = $(
			'
' +
				'

' +
			'
'
		);
		trEl = skeletonEl.find('tr');

		if (startCol > 0) {
			trEl.append('');
		}

		trEl.append(
			seg.el.attr('colspan', endCol - startCol)
		);

		if (endCol < colCnt) {
			trEl.append('');
		}

		this.bookendCells(trEl, type);

		return skeletonEl;
	}

});
<commit_msg>DayGrid::renderFillRow className param<commit_after>
/* A component that renders a grid of whole-days that runs horizontally. There can be multiple rows, one per week.
----------------------------------------------------------------------------------------------------------------------*/

var DayGrid = Grid.extend({

	numbersVisible: false, // should render a row for day/week numbers? set by outside view. TODO: make internal
	bottomCoordPadding: 0, // hack for extending the hit area for the last row of the coordinate grid
	breakOnWeeks: null, // should create a new row for each week? set by outside view

	cellDates: null, // flat chronological array of each cell's dates
	dayToCellOffsets: null, // maps days offsets from grid's start date, to cell offsets

	rowEls: null, // set of fake row elements
	dayEls: null, // set of whole-day elements comprising the row's background
	helperEls: null, // set of cell skeleton elements for rendering the mock event "helper"

	// Renders the rows and columns into the component's `this.el`, which should already be assigned.
	// isRigid determins whether the individual rows should ignore the contents and be a constant height.
	// Relies on the view's colCnt and rowCnt. In the future, this component should probably be self-sufficient.
	renderDates: function(isRigid) {
		var view = this.view;
		var rowCnt = this.rowCnt;
		var colCnt = this.colCnt;
		var cellCnt = rowCnt * colCnt;
		var html = '';
		var row;
		var i, cell;

		for (row = 0; row < rowCnt; row++) {
			html += this.dayRowHtml(row, isRigid);
		}
		this.el.html(html);

		this.rowEls = this.el.find('.fc-row');
		this.dayEls = this.el.find('.fc-day');

		// trigger dayRender with each cell's element
		for (i = 0; i < cellCnt; i++) {
			cell = this.getCell(i);
			view.trigger('dayRender', null, cell.start, this.dayEls.eq(i));
		}
	},

	destroyDates: function() {
		this.destroySegPopover();
	},

	// Generates the HTML for a single row. `row` is the row number.
	dayRowHtml: function(row, isRigid) {
		var view = this.view;
		var classes = [ 'fc-row', 'fc-week', view.widgetContentClass ];

		if (isRigid) {
			classes.push('fc-rigid');
		}

		return '' +
			'
' +
				'
' +
					'
' +
						this.rowHtml('day', row) + // leverages RowRenderer. calls dayCellHtml()
					'
' +
				'
' +
				'
' +
					'
' +
						(this.numbersVisible ?
							'
' +
								this.rowHtml('number', row) + // leverages RowRenderer. View will define render method
							'
' :
							''
							) +
					'
' +
				'
' +
			'
';
	},

	// Renders the HTML for a whole-day cell. Will eventually end up in the day-row's background.
	// We go through a 'day' row type instead of just doing a 'bg' row type so that the View can do custom rendering
	// specifically for whole-day rows, whereas a 'bg' might also be used for other purposes (TimeGrid bg for example).
	dayCellHtml: function(cell) {
		return this.bgCellHtml(cell);
	},

	/* Options
	------------------------------------------------------------------------------------------------------------------*/

	// Computes a default column header formatting string if `colFormat` is not explicitly defined
	computeColHeadFormat: function() {
		if (this.rowCnt > 1) { // more than one week row. day numbers will be in each cell
			return 'ddd'; // "Sat"
		}
		else if (this.colCnt > 1) { // multiple days, so full single date string WON'T be in title text
			return this.view.opt('dayOfMonthFormat'); // "Sat 12/10"
		}
		else { // single day, so full single date string will probably be in title text
			return 'dddd'; // "Saturday"
		}
	},

	// Computes a default event time formatting string if `timeFormat` is not explicitly defined
	computeEventTimeFormat: function() {
		return this.view.opt('extraSmallTimeFormat'); // like "6p" or "6:30p"
	},

	// Computes a default `displayEventEnd` value if one is not expliclty defined
	computeDisplayEventEnd: function() {
		return this.colCnt == 1; // we'll likely have space if there's only one day
	},

	/* Cell System
	------------------------------------------------------------------------------------------------------------------*/

	// Initializes row/col information
	updateCells: function() {
		var cellDates;
		var firstDay;
		var rowCnt;
		var colCnt;

		this.updateCellDates(); // populates cellDates and dayToCellOffsets
		cellDates = this.cellDates;

		if (this.breakOnWeeks) {
			// count columns until the day-of-week repeats
			firstDay = cellDates[0].day();
			for (colCnt = 1; colCnt < cellDates.length; colCnt++) {
				if (cellDates[colCnt].day() == firstDay) {
					break;
				}
			}
			rowCnt = Math.ceil(cellDates.length / colCnt);
		}
		else {
			rowCnt = 1;
			colCnt = cellDates.length;
		}

		this.rowCnt = rowCnt;
		this.colCnt = colCnt;
	},

	// Populates cellDates and dayToCellOffsets
	updateCellDates: function() {
		var view = this.view;
		var date = this.start.clone();
		var dates = [];
		var offset = -1;
		var offsets = [];

		while (date.isBefore(this.end)) { // loop each day from start to end
			if (view.isHiddenDay(date)) {
				offsets.push(offset + 0.5); // mark that it's between offsets
			}
			else {
				offset++;
				offsets.push(offset);
				dates.push(date.clone());
			}
			date.add(1, 'days');
		}

		this.cellDates = dates;
		this.dayToCellOffsets = offsets;
	},

	// Given a cell object, generates a range object
	computeCellRange: function(cell) {
		var colCnt = this.colCnt;
		var index = cell.row * colCnt + (this.isRTL ? colCnt - cell.col - 1 : cell.col);
		var start = this.cellDates[index].clone();
		var end = start.clone().add(1, 'day');

		return { start: start, end: end };
	},

	// Retrieves the element representing the given row
	getRowEl: function(row) {
		return this.rowEls.eq(row);
	},

	// Retrieves the element representing the given column
	getColEl: function(col) {
		return this.dayEls.eq(col);
	},

	// Gets the whole-day element associated with the cell
	getCellDayEl: function(cell) {
		return this.dayEls.eq(cell.row * this.colCnt + cell.col);
	},

	// Overrides Grid's method for when row coordinates are computed
	computeRowCoords: function() {
		var rowCoords = Grid.prototype.computeRowCoords.call(this); // call the super-method

		// hack for extending last row (used by AgendaView)
		rowCoords[rowCoords.length - 1].bottom += this.bottomCoordPadding;

		return rowCoords;
	},

	/* Dates
	------------------------------------------------------------------------------------------------------------------*/

	// Slices up a date range by row into an array of segments
	rangeToSegs: function(range) {
		var isRTL = this.isRTL;
		var rowCnt = this.rowCnt;
		var colCnt = this.colCnt;
		var segs = [];
		var first, last; // inclusive cell-offset range for given range
		var row;
		var rowFirst, rowLast; // inclusive cell-offset range for current row
		var isStart, isEnd;
		var segFirst, segLast; // inclusive cell-offset range for segment
		var seg;

		range = this.view.computeDayRange(range); // make whole-day range, considering nextDayThreshold
		first = this.dateToCellOffset(range.start);
		last = this.dateToCellOffset(range.end.subtract(1, 'days')); // offset of inclusive end date

		for (row = 0; row < rowCnt; row++) {
			rowFirst = row * colCnt;
			rowLast = rowFirst + colCnt - 1;

			// intersect segment's offset range with the row's
			segFirst = Math.max(rowFirst, first);
			segLast = Math.min(rowLast, last);

			// deal with in-between indices
			segFirst = Math.ceil(segFirst); // in-between starts round to next cell
			segLast = Math.floor(segLast); // in-between ends round to prev cell

			if (segFirst <= segLast) { // was there any intersection with the current row?

				// must be matching integers to be the segment's start/end
				isStart = segFirst === first;
				isEnd = segLast === last;

				// translate offsets to be relative to start-of-row
				segFirst -= rowFirst;
				segLast -= rowFirst;

				seg = { row: row, isStart: isStart, isEnd: isEnd };
				if (isRTL) {
					seg.leftCol = colCnt - segLast - 1;
					seg.rightCol = colCnt - segFirst - 1;
				}
				else {
					seg.leftCol = segFirst;
					seg.rightCol = segLast;
				}
				segs.push(seg);
			}
		}

		return segs;
	},

	// Given a date, returns its chronolocial cell-offset from the first cell of the grid.
	// If the date lies between cells (because of hiddenDays), returns a floating-point value between offsets.
	// If before the first offset, returns a negative number.
	// If after the last offset, returns an offset past the last cell offset.
	// Only works for *start* dates of cells. Will not work for exclusive end dates for cells.
	dateToCellOffset: function(date) {
		var offsets = this.dayToCellOffsets;
		var day = date.diff(this.start, 'days');

		if (day < 0) {
			return offsets[0] - 1;
		}
		else if (day >= offsets.length) {
			return offsets[offsets.length - 1] + 1;
		}
		else {
			return offsets[day];
		}
	},

	/* Event Drag Visualization
	------------------------------------------------------------------------------------------------------------------*/
	// TODO: move to DayGrid.event, similar to what we did with Grid's drag methods

	// Renders a visual indication of an event or external element being dragged.
	// The dropLocation's end can be null. seg can be null. See Grid::renderDrag for more info.
	renderDrag: function(dropLocation, seg) {
		var opacity;

		// always render a highlight underneath
		this.renderHighlight(
			this.view.calendar.ensureVisibleEventRange(dropLocation) // needs to be a proper range
		);

		// if a segment from the same calendar but another component is being dragged, render a helper event
		if (seg && !seg.el.closest(this.el).length) {

			this.renderRangeHelper(dropLocation, seg);

			opacity = this.view.opt('dragOpacity');
			if (opacity !== undefined) {
				this.helperEls.css('opacity', opacity);
			}

			return true; // a helper has been rendered
		}
	},

	// Unrenders any visual indication of a hovering event
	destroyDrag: function() {
		this.destroyHighlight();
		this.destroyHelper();
	},

	/* Event Resize Visualization
	------------------------------------------------------------------------------------------------------------------*/

	// Renders a visual indication of an event being resized
	renderEventResize: function(range, seg) {
		this.renderHighlight(range);
		this.renderRangeHelper(range, seg);
	},

	// Unrenders a visual indication of an event being resized
	destroyEventResize: function() {
		this.destroyHighlight();
		this.destroyHelper();
	},

	/* Event Helper
	------------------------------------------------------------------------------------------------------------------*/

	// Renders a mock "helper" event. `sourceSeg` is the associated internal segment object. It can be null.
	renderHelper: function(event, sourceSeg) {
		var helperNodes = [];
		var segs = this.eventsToSegs([ event ]);
		var rowStructs;

		segs = this.renderFgSegEls(segs); // assigns each seg's el and returns a subset of segs that were rendered
		rowStructs = this.renderSegRows(segs);

		// inject each new event skeleton into each associated row
		this.rowEls.each(function(row, rowNode) {
			var rowEl = $(rowNode); // the .fc-row
			var skeletonEl = $('

'); // will be absolutely positioned
			var skeletonTop;

			// If there is an original segment, match the top position. Otherwise, put it at the row's top level
			if (sourceSeg && sourceSeg.row === row) {
				skeletonTop = sourceSeg.el.position().top;
			}
			else {
				skeletonTop = rowEl.find('.fc-content-skeleton tbody').position().top;
			}

			skeletonEl.css('top', skeletonTop)
				.find('table')
					.append(rowStructs[row].tbodyEl);

			rowEl.append(skeletonEl);
			helperNodes.push(skeletonEl[0]);
		});

		this.helperEls = $(helperNodes); // array -> jQuery set
	},

	// Unrenders any visual indication of a mock helper event
	destroyHelper: function() {
		if (this.helperEls) {
			this.helperEls.remove();
			this.helperEls = null;
		}
	},

	/* Fill System (highlight, background events, business hours)
	------------------------------------------------------------------------------------------------------------------*/

	fillSegTag: 'td', // override the default tag name

	// Renders a set of rectangles over the given segments of days.
	// Only returns segments that successfully rendered.
	renderFill: function(type, segs, className) {
		var nodes = [];
		var i, seg;
		var skeletonEl;

		segs = this.renderFillSegEls(type, segs); // assignes `.el` to each seg. returns successfully rendered segs

		for (i = 0; i < segs.length; i++) {
			seg = segs[i];
			skeletonEl = this.renderFillRow(type, seg, className);
			this.rowEls.eq(seg.row).append(skeletonEl);
			nodes.push(skeletonEl[0]);
		}

		this.elsByFill[type] = $(nodes);

		return segs;
	},

	// Generates the HTML needed for one row of a fill. Requires the seg's el to be rendered.
	renderFillRow: function(type, seg, className) {
		var colCnt = this.colCnt;
		var startCol = seg.leftCol;
		var endCol = seg.rightCol + 1;
		var skeletonEl;
		var trEl;

		className = className || type.toLowerCase();

		skeletonEl = $(
			'
' +
				'

' +
			'
'
		);
		trEl = skeletonEl.find('tr');

		if (startCol > 0) {
			trEl.append('');
		}

		trEl.append(
			seg.el.attr('colspan', endCol - startCol)
		);

		if (endCol < colCnt) {
			trEl.append('');
		}

		this.bookendCells(trEl, type);

		return skeletonEl;
	}

});
<|endoftext|>


<commit_before>/**
 * Background Script
 * Kassi Share
 */

var contextMenus = chrome.contextMenus || browser.contextMenus || {};

function sendMessageToContentScript(data)
{
    return new Promise((resolve, reject) => {

        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
            const tabValid = tabs && tabs.length > 0;
            if(tabValid) {
                chrome.tabs.sendMessage(tabs[0].id, data, function(response) {
                    // message sent to contentScript
                    resolve(response);
                });
            }
            else {
                reject();
            }
        });

    });
}

// ---------------------

class KodiConfig
{
    constructor()
    {
        this.name = "kodi";
        this.hostName = "localhost";
        this.port = "8080";
        this.username = "";
        this.password = "";
    }

    get _hasCredentials()
    {
        return this.username && this.password;
    }

    get url()
    {
        const credentials = this._hasCredentials ? `${this.username}:${this.password}@` : "";
        return `http://${credentials}${this.hostName}:${this.port}/jsonrpc`;
    }

}

class RPCService
{
    constructor(kodiConf)
    {
        this.kodiConf = kodiConf;
        this.isPending = false;
    }

    send(method, params = null)
    {
        var thisObject = this;
        return new Promise((resolve, reject) => {

            let data = {
                jsonrpc: "2.0",
                method: method,
                id: 1
            };

            if(params)
            {
                data.params = params;
            }

            console.log("<< " + method, data);
            thisObject.isPending = true;

            let strData = JSON.stringify(data);

            let urlRequest = new URLRequest(thisObject.kodiConf.url);
            urlRequest.method = "POST";

            urlRequest.send(strData).then(response => {

                thisObject.isPending = false;

                const data = JSON.parse(response);
                console.log(">> " + method, data);
                if(data.error) {
                    reject(data);
                }
                else {
                    resolve(data);
                }

            }).catch(() => {
                thisObject.isPending = false;
                reject();
            });

        });
    }
}

class Player
{
    constructor(kodiConf)
    {
        this.rpc = new RPCService(kodiConf);
    }

    _clearPlaylist()
    {
        const params = {
            playlistid: 1
        };

        return this.rpc.send("Playlist.Clear", params);
    }

    _addToPlaylist(file)
    {
        const params = {
            playlistid: 1,
            item : {
                file : file
            }
        };

        return this.rpc.send("Playlist.Add", params);
    }

    _playFromPlaylist(position = 0)
    {
        const params = {
            item : {
                playlistid: 1,
                position: position
            }
        };

        return this.rpc.send("Player.Open", params);
    }

    _getActivePlayers()
    {
        const params = {};
        return this.rpc.send("Player.GetActivePlayers", params);
    }

    _queue(file)
    {
        return new Promise((resolve, reject) => {

            if(!file)
            {
                reject();
                return;
            }

            this._addToPlaylist(file)
                .then(response => {

                    const result = response.result;
                    if(result == ResultData.OK) {
                        return this._getActivePlayers();
                    }
                    return reject();
                })
                .then(response => {

                    const result = response.result;
                    // check if no video is playing and start the first video in queue
                    if(result && result.length <= 0) {
                        return this._playFromPlaylist();
                    }
                })
                .then(response => {
                    resolve(response);
                })
                .catch(() => {
                    reject();
                });

        });

    }

    playVideo(file)
    {
        return new Promise((resolve, reject) => {

            console.log("play video, " + file);

            // 1. Clear play list
            // 2. Add to playlist
            // 3. Play first index

            this._clearPlaylist()
                .then(response => {
                    return this._addToPlaylist(file);
                })
                .then(response => {
                    return this._playFromPlaylist();
                })
                .then(response => {

                    resolve(response);

                }).catch(() => {
                    reject();
                });

        });
    }

    queueVideo(file)
    {
        return new Promise((resolve, reject) => {

            console.log("queue file " + file);

            // Player.GetActivePlayers (if empty), Playlist.Clear, Playlist.Add(file), Player.GetActivePlayers (if empty), Player.Open(playlist)
            // Player.GetActivePlayers (if playing), Playlist.Add(file), Player.GetActivePlayers (if playing), do nothing

            this._getActivePlayers()
                .then(response => {

                    const result = response.result;
                    if(result && result.length <= 0)
                    {
                        return this._clearPlaylist();
                    }
                })
                .then(response => {
                    return this._queue(file);
                })
                .then(response => {

                    resolve(response);
                })
                .catch(() => {

                    reject();
                });

        });

    }

    async playAll(files)
    {
        console.log("play all ", files);

        const len = files.length;
        let res;
        try {
            res = await this.playVideo(files[0]);
        } catch(err) {
            return reject(err);
        }

        for (let i = 1; i < len; i++)
        {
            try {
                res = await this.queueVideo(files[i]);
            } catch(err) {
                return reject(err);
            }
        }

        return res;
    }

    async queueAll(files)
    {
        console.log("queue all ", files);

        let res;
        const len = files.length;
        for (let i = 0; i < len; i++)
        {
            try {
                res = await this.queueVideo(files[i]);
            } catch(err) {
                return reject(err);
            }
        }

        return res;
    }
}

class ContextMenu
{
    constructor()
    {
        this.siteFilters = {};
        this._onPlayClick = this._onPlayClick.bind(this);
        this._onQueueClick = this._onQueueClick.bind(this);
        this._onPlayAllClick = this._onPlayAllClick.bind(this);
        this._onQueueAllClick = this._onQueueAllClick.bind(this);
    }

    addFilters(siteName, site, videoFilters, playlistFilters = null)
    {
        this.siteFilters[siteName] = {
            videoFilters: videoFilters,
            playlistFilters: playlistFilters,
            site: site
        };

        this._updateMenuEntries();
    }

    _updateMenuEntries()
    {
        contextMenus.removeAll(() => {
            // callback
        });

        let videoFilters = Object.entries(this.siteFilters)
            .map((item) => {
                return item[1].videoFilters;
            })
            .filter((data) => {
                return data != null;
            });
        videoFilters = [].concat.apply([], videoFilters);

        if(videoFilters && videoFilters.length > 0)
        {
            const playNow = {
                title: "Play",
                contexts:["link"],
                targetUrlPatterns: videoFilters,
                onclick: this._onPlayClick
            };

            const addToQueue = {
                title: "Queue",
                contexts:["link"],
                targetUrlPatterns: videoFilters,
                onclick: this._onQueueClick
            };

            contextMenus.create(playNow);
            contextMenus.create(addToQueue);
        }

        let playListFilters = Object.entries(this.siteFilters)
            .map((item) => {
                return item[1].playlistFilters;
            })
            .filter((data) => {
                return data != null;
            });
        playListFilters = [].concat.apply([], playListFilters);

        if(playListFilters && playListFilters.length > 0)
        {
            const playAll = {
                title: "Play All",
                contexts:["link"],
                targetUrlPatterns: playListFilters,
                onclick: this._onPlayAllClick
            };

            const queueAll = {
                title: "Queue All",
                contexts:["link"],
                targetUrlPatterns: playListFilters,
                onclick: this._onQueueAllClick
            };

            contextMenus.create(playAll);
            contextMenus.create(queueAll);
        }
    }

    _onPlayClick(info, tab)
    {
        let site = this._getSiteFromLinkUrl(info.linkUrl);
        if(site && typeof site["onPlayClick"] === "function")
        {
            site.onPlayClick(info.linkUrl);
        }
    }

    _onQueueClick(info, tab)
    {
        let site = this._getSiteFromLinkUrl(info.linkUrl);
        if(site && typeof site["onQueueClick"] === "function")
        {
            site.onQueueClick(info.linkUrl);
        }
    }

    _onPlayAllClick(info, tab)
    {
        let site = this._getSiteFromLinkUrl(info.linkUrl);
        if(site && typeof site["onPlayAllClick"] === "function")
        {
            site.onPlayAllClick(info.linkUrl);
        }
    }

    _onQueueAllClick(info, tab)
    {
        let site = this._getSiteFromLinkUrl(info.linkUrl);
        if(site && typeof site["onQueueAllClick"] === "function")
        {
            site.onQueueAllClick(info.linkUrl);
        }
    }

    _getSiteFromLinkUrl(linkUrl)
    {
        let site = null;
        if(linkUrl)
        {
            linkUrl = linkUrl.toLowerCase();
            Object.entries(this.siteFilters).some((item, index) =>
            {
                const key = item[0];
                const value = item[1];
                if(linkUrl.indexOf(key) >= 0)
                {
                    site = value.site;
                    return true;
                }
            });
        }
        return site;
    }
}

class URLRequest
{
    constructor(url)
    {
        this._url = url;
        this._contentType = "application/json";
        this._method = "GET";
    }

    set contentType(value)
    {
        this._contentType = value;
    }

    set method(value)
    {
        this._method = value;
    }

    send(data = "")
    {
        //console.log("send url request " + this._url);
        return new Promise((reslove, reject) => {

            let xhr = new XMLHttpRequest();
            xhr.open(this._method, this._url, true);
            xhr.setRequestHeader("Content-type", this._contentType);

            xhr.onreadystatechange = function() {
                //console.log("this.readyState, " + this.readyState + ", " + this.status);
            };

            xhr.onerror = function() {
                reject();
            };

            xhr.onload = function () {
                const isSuccess = (this.status == 200);
                if(isSuccess)
                {
                    reslove(this.responseText);
                }
                else
                {
                    reject();
                }
            };

            xhr.send(data);
        });
    }
}

class AbstractSite
{
    constructor()
    {

    }

    _messagePlayVideo(status)
    {
        const data = {message: "playVideo", status: status};
        sendMessageToContentScript(data);
    }

    _messageQueueVideo(status)
    {
        const data = {message: "queueVideo", status: status};
        sendMessageToContentScript(data);
    }

    _messagePlaylist(status)
    {
        const data = {message: "playList", status: status};
        sendMessageToContentScript(data);
    }

    _messageQueueAll(status)
    {
        const data = {message: "queuePlayList", status: status};
        sendMessageToContentScript(data);
    }

    onPlayClick(url)
    {
        console.log("play click " + url);

        this.getFileFromUrl(url)
            .then(fileUrl => {
                return player.playVideo(fileUrl);
            })
            .then(response => {
                this._messagePlayVideo(ResultData.OK);
            }).catch(response => {
                this._messagePlayVideo(ResultData.ERROR);
            });
    }

    onQueueClick(url)
    {
        console.log("onQueueClick " + url);

        this.getFileFromUrl(url)
            .then(fileUrl => {
                return player.queueVideo(fileUrl);
            })
            .then(response => {
                this._messageQueueVideo(ResultData.OK);
            })
            .catch(response => {
                this._messageQueueVideo(ResultData.ERROR);
            });

    }

    onPlayAllClick(url)
    {
        console.log("play all click " + url);

        this.getPlaylistFromUrl(url)
            .then(fileList => {
                return player.playAll(fileList);
            })
            .then(response => {
                console.log(response);
                this._messagePlaylist(ResultData.OK);
            })
            .catch(response => {
                this._messagePlaylist(ResultData.ERROR);
            });

    }

    onQueueAllClick(url)
    {
        console.log("queue all click " + url);

        this.getPlaylistFromUrl(url)
            .then(fileList => {
                return player.queueAll(fileList);
            })
            .then(response => {
                this._messageQueueAll(ResultData.OK);
            })
            .catch(response => {
                this._messageQueueAll(ResultData.ERROR);
            });
    }

    getFileFromUrl(url)
    {
        return new Promise((resolve, reject) => {
            reject();
        });
    }

    getPlaylistFromUrl(url)
    {
        return new Promise((resolve, reject) => {
            reject();
        });
    }
}

let contextMenu = new ContextMenu();
let kodiConf = new KodiConfig();

let player = new Player(kodiConf);<commit_msg>update<commit_after>/**
 * Background Script
 * Kassi Share
 */

var contextMenus = chrome.contextMenus || browser.contextMenus || {};

function sendMessageToContentScript(data)
{
    return new Promise((resolve, reject) => {

        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
            const tabValid = tabs && tabs.length > 0;
            if(tabValid) {
                chrome.tabs.sendMessage(tabs[0].id, data, function(response) {
                    // message sent to contentScript
                    resolve(response);
                });
            }
            else {
                reject();
            }
        });

    });
}

// ---------------------

class KodiConfig
{
    constructor()
    {
        this.name = "kodi";
        this.hostName = "localhost";
        this.port = "8080";
        this.username = "";
        this.password = "";
    }

    get _hasCredentials()
    {
        return this.username && this.password;
    }

    get url()
    {
        const credentials = this._hasCredentials ? `${this.username}:${this.password}@` : "";
        return `http://${credentials}${this.hostName}:${this.port}/jsonrpc`;
    }

}

class RPCService
{
    constructor(kodiConf)
    {
        this.kodiConf = kodiConf;
        this.isPending = false;
    }

    send(method, params = null)
    {
        var thisObject = this;
        return new Promise((resolve, reject) => {

            let data = {
                jsonrpc: "2.0",
                method: method,
                id: 1
            };

            if(params)
            {
                data.params = params;
            }

            console.log("<< " + method, data);
            thisObject.isPending = true;

            let strData = JSON.stringify(data);

            let urlRequest = new URLRequest(thisObject.kodiConf.url);
            urlRequest.method = "POST";

            urlRequest.send(strData).then(response => {

                thisObject.isPending = false;

                const data = JSON.parse(response);
                console.log(">> " + method, data);
                if(data.error) {
                    reject(data);
                }
                else {
                    resolve(data);
                }

            }).catch(() => {
                thisObject.isPending = false;
                reject();
            });

        });
    }
}

class Player
{
    constructor(kodiConf)
    {
        this.rpc = new RPCService(kodiConf);
    }

    _clearPlaylist()
    {
        const params = {
            playlistid: 1
        };

        return this.rpc.send("Playlist.Clear", params);
    }

    _addToPlaylist(file)
    {
        const params = {
            playlistid: 1,
            item : {
                file : file
            }
        };

        return this.rpc.send("Playlist.Add", params);
    }

    _playFromPlaylist(position = 0)
    {
        const params = {
            item : {
                playlistid: 1,
                position: position
            }
        };

        return this.rpc.send("Player.Open", params);
    }

    _getActivePlayers()
    {
        const params = {};
        return this.rpc.send("Player.GetActivePlayers", params);
    }

    _queue(file)
    {
        return new Promise((resolve, reject) => {

            if(!file)
            {
                reject();
                return;
            }

            this._addToPlaylist(file)
                .then(response => {

                    const result = response.result;
                    if(result == ResultData.OK) {
                        return this._getActivePlayers();
                    }
                    return reject();
                })
                .then(response => {

                    const result = response.result;
                    // check if no video is playing and start the first video in queue
                    if(result && result.length <= 0) {
                        return this._playFromPlaylist();
                    }
                })
                .then(response => {
                    resolve(response);
                })
                .catch(() => {
                    reject();
                });

        });

    }

    playVideo(file)
    {
        return new Promise((resolve, reject) => {

            console.log("play video, " + file);

            // 1. Clear play list
            // 2. Add to playlist
            // 3. Play first index

            this._clearPlaylist()
                .then(response => {
                    return this._addToPlaylist(file);
                })
                .then(response => {
                    return this._playFromPlaylist();
                })
                .then(response => {

                    resolve(response);

                }).catch(() => {
                    reject();
                });

        });
    }

    queueVideo(file)
    {
        return new Promise((resolve, reject) => {

            console.log("queue file " + file);

            // Player.GetActivePlayers (if empty), Playlist.Clear, Playlist.Add(file), Player.GetActivePlayers (if empty), Player.Open(playlist)
            // Player.GetActivePlayers (if playing), Playlist.Add(file), Player.GetActivePlayers (if playing), do nothing

            this._getActivePlayers()
                .then(response => {

                    const result = response.result;
                    if(result && result.length <= 0)
                    {
                        return this._clearPlaylist();
                    }
                })
                .then(response => {
                    return this._queue(file);
                })
                .then(response => {

                    resolve(response);
                })
                .catch(() => {

                    reject();
                });

        });

    }

    async playAll(files)
    {
        console.log("play all ", files);

        const len = files.length;
        let res;
        try {
            res = await this.playVideo(files[0]);
        } catch(err) {
            return reject(err);
        }

        for (let i = 1; i < len; i++)
        {
            try {
                res = await this.queueVideo(files[i]);
            } catch(err) {
                return reject(err);
            }
        }

        return res;
    }

    async queueAll(files)
    {
        console.log("queue all ", files);

        let res;
        const len = files.length;
        for (let i = 0; i < len; i++)
        {
            try {
                res = await this.queueVideo(files[i]);
            } catch(err) {
                return reject(err);
            }
        }

        return res;
    }
}

class ContextMenu
{
    constructor(kodiConfig)
    {
        this.kodiConfig = kodiConfig;
        this.siteFilters = {};
        this._onPlayClick = this._onPlayClick.bind(this);
        this._onQueueClick = this._onQueueClick.bind(this);
        this._onPlayAllClick = this._onPlayAllClick.bind(this);
        this._onQueueAllClick = this._onQueueAllClick.bind(this);
    }

    addFilters(siteName, site, videoFilters, playlistFilters = null)
    {
        this.siteFilters[siteName] = {
            videoFilters: videoFilters,
            playlistFilters: playlistFilters,
            site: site
        };

        this._updateMenuEntries();
    }

    _updateMenuEntries()
    {
        contextMenus.removeAll(() => {
            // callback
        });

        let videoFilters = Object.entries(this.siteFilters)
            .map((item) => {
                return item[1].videoFilters;
            })
            .filter((data) => {
                return data != null;
            });
        videoFilters = [].concat.apply([], videoFilters);

        let playListFilters = Object.entries(this.siteFilters)
            .map((item) => {
                return item[1].playlistFilters;
            })
            .filter((data) => {
                return data != null;
            });
        playListFilters = [].concat.apply([], playListFilters);

        const hasVideo = videoFilters && videoFilters.length > 0;
        const hasPlaylist = playListFilters && playListFilters.length > 0;

        if(hasVideo || hasPlaylist) {

            let filter = videoFilters.concat(playListFilters);
            const kodiName = {
                title: this.kodiConfig.name,
                type: "checkbox",
                checked: true,
                enabled: false,
                contexts:["link"],
                targetUrlPatterns: filter
            };
            contextMenus.create(kodiName);

            const separator = {
                type: "separator",
                contexts:["link"],
                targetUrlPatterns: filter
            };
            contextMenus.create(separator);
        }

        if(hasVideo) {
            const playNow = {
                title: "Play",
                contexts:["link"],
                targetUrlPatterns: videoFilters,
                onclick: this._onPlayClick
            };

            const addToQueue = {
                title: "Queue",
                contexts:["link"],
                targetUrlPatterns: videoFilters,
                onclick: this._onQueueClick
            };

            contextMenus.create(playNow);
            contextMenus.create(addToQueue);
        }

        if(hasPlaylist) {
            const playAll = {
                title: "Play All",
                contexts:["link"],
                targetUrlPatterns: playListFilters,
                onclick: this._onPlayAllClick
            };

            const queueAll = {
                title: "Queue All",
                contexts:["link"],
                targetUrlPatterns: playListFilters,
                onclick: this._onQueueAllClick
            };

            contextMenus.create(playAll);
            contextMenus.create(queueAll);
        }
    }

    _onPlayClick(info, tab)
    {
        let site = this._getSiteFromLinkUrl(info.linkUrl);
        if(site && typeof site["onPlayClick"] === "function")
        {
            site.onPlayClick(info.linkUrl);
        }
    }

    _onQueueClick(info, tab)
    {
        let site = this._getSiteFromLinkUrl(info.linkUrl);
        if(site && typeof site["onQueueClick"] === "function")
        {
            site.onQueueClick(info.linkUrl);
        }
    }

    _onPlayAllClick(info, tab)
    {
        let site = this._getSiteFromLinkUrl(info.linkUrl);
        if(site && typeof site["onPlayAllClick"] === "function")
        {
            site.onPlayAllClick(info.linkUrl);
        }
    }

    _onQueueAllClick(info, tab)
    {
        let site = this._getSiteFromLinkUrl(info.linkUrl);
        if(site && typeof site["onQueueAllClick"] === "function")
        {
            site.onQueueAllClick(info.linkUrl);
        }
    }

    _getSiteFromLinkUrl(linkUrl)
    {
        let site = null;
        if(linkUrl)
        {
            linkUrl = linkUrl.toLowerCase();
            Object.entries(this.siteFilters).some((item, index) =>
            {
                const key = item[0];
                const value = item[1];
                if(linkUrl.indexOf(key) >= 0)
                {
                    site = value.site;
                    return true;
                }
            });
        }
        return site;
    }
}

class URLRequest
{
    constructor(url)
    {
        this._url = url;
        this._contentType = "application/json";
        this._method = "GET";
    }

    set contentType(value)
    {
        this._contentType = value;
    }

    set method(value)
    {
        this._method = value;
    }

    send(data = "")
    {
        //console.log("send url request " + this._url);
        return new Promise((reslove, reject) => {

            let xhr = new XMLHttpRequest();
            xhr.open(this._method, this._url, true);
            xhr.setRequestHeader("Content-type", this._contentType);

            xhr.onreadystatechange = function() {
                //console.log("this.readyState, " + this.readyState + ", " + this.status);
            };

            xhr.onerror = function() {
                reject();
            };

            xhr.onload = function () {
                const isSuccess = (this.status == 200);
                if(isSuccess)
                {
                    reslove(this.responseText);
                }
                else
                {
                    reject();
                }
            };

            xhr.send(data);
        });
    }
}

class AbstractSite
{
    constructor()
    {

    }

    _messagePlayVideo(status)
    {
        const data = {message: "playVideo", status: status};
        sendMessageToContentScript(data);
    }

    _messageQueueVideo(status)
    {
        const data = {message: "queueVideo", status: status};
        sendMessageToContentScript(data);
    }

    _messagePlaylist(status)
    {
        const data = {message: "playList", status: status};
        sendMessageToContentScript(data);
    }

    _messageQueueAll(status)
    {
        const data = {message: "queuePlayList", status: status};
        sendMessageToContentScript(data);
    }

    onPlayClick(url)
    {
        console.log("play click " + url);

        this.getFileFromUrl(url)
            .then(fileUrl => {
                return player.playVideo(fileUrl);
            })
            .then(response => {
                this._messagePlayVideo(ResultData.OK);
            }).catch(response => {
                this._messagePlayVideo(ResultData.ERROR);
            });
    }

    onQueueClick(url)
    {
        console.log("onQueueClick " + url);

        this.getFileFromUrl(url)
            .then(fileUrl => {
                return player.queueVideo(fileUrl);
            })
            .then(response => {
                this._messageQueueVideo(ResultData.OK);
            })
            .catch(response => {
                this._messageQueueVideo(ResultData.ERROR);
            });

    }

    onPlayAllClick(url)
    {
        console.log("play all click " + url);

        this.getPlaylistFromUrl(url)
            .then(fileList => {
                return player.playAll(fileList);
            })
            .then(response => {
                console.log(response);
                this._messagePlaylist(ResultData.OK);
            })
            .catch(response => {
                this._messagePlaylist(ResultData.ERROR);
            });

    }

    onQueueAllClick(url)
    {
        console.log("queue all click " + url);

        this.getPlaylistFromUrl(url)
            .then(fileList => {
                return player.queueAll(fileList);
            })
            .then(response => {
                this._messageQueueAll(ResultData.OK);
            })
            .catch(response => {
                this._messageQueueAll(ResultData.ERROR);
            });
    }

    getFileFromUrl(url)
    {
        return new Promise((resolve, reject) => {
            reject();
        });
    }

    getPlaylistFromUrl(url)
    {
        return new Promise((resolve, reject) => {
            reject();
        });
    }
}

let kodiConf = new KodiConfig();

let contextMenu = new ContextMenu(kodiConf);
let player = new Player(kodiConf);<|endoftext|>


<commit_before>Gerrit.install(function(self) {
/*
   Gerrit.on('showchange', function (c) {
       alert("At least something");
   });
   Gerrit.onAction('change', 'reviewers', function (c) {
       alert("On reviewer added");
   });
*/

(function(send) {

    XMLHttpRequest.prototype.send = function(data) {

        // in this case I'm injecting an access token (eg. accessToken) in the request headers before it gets sent
        //if(accessToken) this.setRequestHeader('x-access-token', accessToken);

        send.call(this, data);
    };

})(XMLHttpRequest.prototype.send);
/*
   (function(open) {
       XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
           if (url.search(/changes\/\d+\/reviewers/) != -1
                   && this.readyState == 4
                   && this.status == 200) {
               if (confirm("The reviewer is busy; add him regardless?") != true) {
                   return;
               }
           }
           open.call(this, method, url, async, user, pass);
       };
   })(XMLHttpRequest.prototype.open);
*/

/*
   (function(open) {
       XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
           this.addEventListener("readystatechange", function() {
               if (url.search(/changes\/\d+\/reviewers/) != -1
                       && this.readyState == 4
                       && this.status == 200) {
                   if (confirm("The reviewer is busy; add him regardless?") != true) {
                       this.status = 500;
                       return;
                   }
               }
           }, false);
           open.call(this, method, url, async, user, pass);
       };
   })(XMLHttpRequest.prototype.open);
   */
});
<commit_msg>Got the basic fake busy reviewer working<commit_after>Gerrit.install(function(self) {
/*
   Gerrit.on('showchange', function (c) {
       alert("At least something");
   });
   Gerrit.onAction('change', 'reviewers', function (c) {
       alert("On reviewer added");
   });
*/

(function(open) {
   XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
       this.url = url;
       open.call(this, method, url, async, user, pass);
   };
})(XMLHttpRequest.prototype.open);

(function(send) {
    XMLHttpRequest.prototype.send = function(data) {
        this.fakeReturn = false;
        if (data != null && data.search(/\{\"reviewer\":\".*\"\}/) != -1) {
            if (confirm("The reviewer is busy; add him regardless?") != true) {
                this.fakeReturn = true;
                data = '';
            }
        }
        // in this case I'm injecting an access token (eg. accessToken) in the request headers before it gets sent
        //if(accessToken) this.setRequestHeader('x-access-token', accessToken);

        send.call(this, data);
    };

})(XMLHttpRequest.prototype.send);

(function() {
    // create XMLHttpRequest proxy object
    var oldXMLHttpRequest = XMLHttpRequest;

    // define constructor for my proxy object
    XMLHttpRequest = function() {
        var actual = new oldXMLHttpRequest();
        var self = this;

        this.onreadystatechange = null;

        // this is the actual handler on the real XMLHttpRequest object
        actual.onreadystatechange = function() {
            self.status = actual.status;
            if (this.readyState == 4) {
                // actual.responseText is the ajax result

                // add your own code here to read the real ajax result
                // from actual.responseText and then put whatever result you want
                // the caller to see in self.responseText
                // this next line of code is a dummy line to be replaced
                if (this.fakeReturn) {
                    self.status = 200;
                }
                //self.responseText = '{"msg": "Hello"}';
            }
            if (self.onreadystatechange) {
                return self.onreadystatechange();
            }
        };

        // add all proxy getters
        ["statusText", "responseType", "response",
         "readyState", "responseXML", "upload", "responseText"].forEach(function(item) {
            Object.defineProperty(self, item, {
                get: function() {return actual[item];}
            });
        });

        // add all proxy getters/setters
        ["ontimeout, timeout", "withCredentials", "onload", "onerror", "onprogress"].forEach(function(item) {
            Object.defineProperty(self, item, {
                get: function() {return actual[item];},
                set: function(val) {actual[item] = val;}
            });
        });

        // add all pure proxy pass-through methods
        ["addEventListener", "send", "open", "abort", "getAllResponseHeaders",
         "getResponseHeader", "overrideMimeType", "setRequestHeader"].forEach(function(item) {
            Object.defineProperty(self, item, {
                value: function() {return actual[item].apply(actual, arguments);}
            });
        });
    }
})();

/*
(function(send) {

    XMLHttpRequest.prototype.send = function(data) {

        if (data != null && data.search(/\{\"reviewer\":\".*\"\}/) != -1) {
            if (confirm("The reviewer is busy; add him regardless?") != true) {
                //return;
                data = '{"reviewer": "boo"}';
            }
        }
        // in this case I'm injecting an access token (eg. accessToken) in the request headers before it gets sent
        //if(accessToken) this.setRequestHeader('x-access-token', accessToken);

        send.call(this, data);
    };

})(XMLHttpRequest.prototype.send);

   (function(open) {
       XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {

                   var oldReady;
                   if (async) {
                       oldReady = this.onreadystatechange;
                       // override onReadyStateChange
                       this.onreadystatechange = function() {
                           if (this.readyState == 4) {
                               // this.responseText is the ajax result
                               // create a dummay ajax object so we can modify responseText
                               var self = this;
                               var dummy = {};
                               ["statusText", "status", "readyState", "responseType", "responseText"].forEach(function(item) {
                                   dummy[item] = self[item];
                               });
                               if (url.search(/changes\/\d+\/reviewers/) != -1) {
                                    dummy.status = 200;
                               }
                               if (oldReady) {
                                    return oldReady.call(dummy);
                               }
                           } else {
                               // call original onreadystatechange handler
                               if (oldReady) {
                                    return oldReady.call(arguments);
                               }
                           }
                       }
                   }

           if (method == "POST" && url.search(/changes\/\d+\/reviewers/) != -1) {
                url = Gerrit.url();
           }
           open.call(this, method, url, async, user, pass);
       };
   })(XMLHttpRequest.prototype.open);
*/
/*
   (function(open) {
       XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
           if (url.search(/changes\/\d+\/reviewers/) != -1
                   && this.readyState == 4
                   && this.status == 200) {
               if (confirm("The reviewer is busy; add him regardless?") != true) {
                   return;
               }
           }
           open.call(this, method, url, async, user, pass);
       };
   })(XMLHttpRequest.prototype.open);

*/
/*
   (function(open) {
       XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
           this.addEventListener("readystatechange", function() {
               if (url.search(/changes\/\d+\/reviewers/) != -1
                       && this.readyState == 4
                       && this.status == 200) {

                    //alert("State change to 4");
                    this.status = 500;
               }
           }, false);
           open.call(this, method, url, async, user, pass);
       };
   })(XMLHttpRequest.prototype.open);
   */
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name OTR for Facebook
// @include http://*.facebook.com
// @include https://*.facebook.com
// @require lib/jquery.js
// @require lib/dep/bigint.js
// @require lib/dep/crypto.js
// @require lib/dep/eventemitter.js
// @require lib/dep/salsa20.js
// @require lib/otr.js
// ==/UserScript==

var DSA = require('otr').DSA;
var OTR = require('otr').OTR;

// execute callback when the page is ready:
$(document).ready(function() {
  console.log("OTR for Facebook loaded.");
  console.log($('.headerTinymanName').text());

  var elemChatTabs = $('#ChatTabsPagelet .fbDockChatTabFlyout');
  console.log(elemChatTabs.size());
  $(elemChatTabs).on('focus', 'textarea', function(e){
    console.log('textarea has focus.');
  });
})

<commit_msg>Wait for chattabs before running<commit_after>// ==UserScript==
// @name OTR for Facebook
// @include http://*.facebook.com
// @include https://*.facebook.com
// @require lib/jquery.js
// @require lib/dep/bigint.js
// @require lib/dep/crypto.js
// @require lib/dep/eventemitter.js
// @require lib/dep/salsa20.js
// @require lib/otr.js
// ==/UserScript==

console.log("OTR for Facebook loaded.");

// execute callback when the page is ready:
$('#ChatTabsPagelet').ready(function() {
  console.log("doc ready.");
  console.log($('.headerTinymanName').text());

  var elemChatTabs = $('#ChatTabsPagelet .fbDockChatTabFlyout');
  console.log(elemChatTabs.size());
  $(elemChatTabs).on('focus', 'textarea', function(e){
    console.log('textarea has focus.');
  });
});
<|endoftext|>


<commit_before>(function () {
  $(document).foundation();

  smoothScroll.init({
    speed: 500,
    offset: 50
  });

  $('section.place .row.embed').on('click', function () {
    $(this).find('iframe').css('pointer-events', 'auto');
  });
})();
<commit_msg>Fix issue with map activating<commit_after>(function () {
  $(document).foundation();

  smoothScroll.init({
    speed: 500,
    offset: 50
  });

  $('section.place .embed').on('click', function () {
    $(this).find('iframe').css('pointer-events', 'auto');
  });
})();
<|endoftext|>


<commit_before>/**
 * Copyright Ben Stones, 2016. All rights reserved.
 *
 * @author Ben Stones (ben@b3ns.com)
 *
 * Do not use in your own projects without prior permission.
 */

$(document).ready(function() {
    var alphabet = {
        a: {
            english: 'Alpha',
            deutsch: 'Anton'
        },

        b: {
            english: 'Bravo',
            deutsch: 'Berta'
        },

        c: {
            english: 'Charlie',
            deutsch: 'Cäsar'
        },

        d: {
            english: 'Delta',
            deutsch: 'Dora'
        },

        e: {
            english: 'Echo',
            deutsch: 'Emil'
        },

        f: {
            english: 'Foxtrot',
            deutsch: 'Friedrich'
        },

        g: {
            english: 'Golf',
            deutsch: 'Gustav'
        },

        h: {
            english: 'Hotel',
            deutsch: 'Heinrich'
        },

        i: {
            english: 'Indigo',
            deutsch: 'Ida'
        },

        j: {
            english: 'Juliet',
            deutsch: 'Julius'
        },

        k: {
            english: 'Kilo',
            deutsch: 'Kaufmann'
        },

        l: {
            english: 'Lima',
            deutsch: 'Ludwig'
        },

        m: {
            english: 'Martha',
            deutsch: 'Mike'
        },

        n: {
            english: 'November',
            deutsch: 'Nordpol'
        },

        o: {
            english: 'Oscar',
            deutsch: 'Otto'
        },

        p: {
            english: 'Papa',
            deutsch: 'Paula'
        },

        q: {
            english: 'Quebec',
            deutsch: 'Quelle'
        },

        r: {
            english: 'Romeo',
            deutsch: 'Richard'
        },

        s: {
            english: 'Sierra',
            deutsch: 'Siegfried'
        },

        t: {
            english: 'Tango',
            deutsch: 'Theodor'
        },

        u: {
            english: 'Uniform',
            deutsch: 'Ulrich'
        },

        v: {
            english: 'Victor',
            deutsch: 'Viktor'
        },

        w: {
            english: 'Whiskey',
            deutsch: 'Wilhelm'
        },

        x: {
            english: 'X-Ray',
            deutsch: 'Xanthippe'
        },

        y: {
            english: 'Yankee',
            deutsch: 'Ypsilon'
        },

        z: {
            english: 'Zulu',
            deutsch: 'Zeppelin'
        },

        0: {
            english: 'Zero',
            deutsch: 'Null'
        },

        1: {
            english: 'One',
            deutsch: 'Eins'
        },

        2: {
            english: 'Two',
            deutsch: 'Zwei'
        },

        3: {
            english: 'Three',
            deutsch: 'Drei'
        },

        4: {
            english: 'Four',
            deutsch: 'Vier'
        },

        5: {
            english: 'Fivex',
            deutsch: 'Fünf'
        },

        6: {
            english: 'Six',
            deutsch: 'Sechs'
        },

        7: {
            english: 'Seven',
            deutsch: 'Sieben'
        },

        8: {
            english: 'Eight',
            deutsch: 'Acht'
        },

        9: {
            english: 'Nine',
            deutsch: 'Neun'
        }
    };

    var defaultLanguage = 'english';

    $('form').submit(function(e) {
        e.preventDefault();
    });

    $('form input').keyup(function(e) {
        if (e.keyCode != 16) {
            var characters = $(this).val().toLowerCase().split('');

            var phonetic = '';

            var result = $('div#result');

            for (i = 0; i < characters.length; i++) {
                if (typeof alphabet[characters[i]][defaultLanguage] !== 'undefined' && alphabet[characters[i]][defaultLanguage]) {
                    phonetic += alphabet[characters[i]][defaultLanguage] + ', ';
                }
            }

            if (characters.length) {
                result.html('
' + phonetic.substring(0, phonetic.length - 2) + '
');
            } else {
                result.html(result.attr('data-default'));
            }
        }
    });
});
<commit_msg>Preparation for Animal support. (Why not?)<commit_after>/**
 * Copyright Ben Stones, 2016. All rights reserved.
 *
 * @author Ben Stones (ben@b3ns.com)
 *
 * Do not use in your own projects without prior permission.
 */

$(document).ready(function() {
    var alphabet = {
        a: {
            english: 'Alpha',
            deutsch: 'Anton',
            animals: 'Anaconda'
        },

        b: {
            english: 'Bravo',
            deutsch: 'Berta',
            animals: 'Bluebird',
        },

        c: {
            english: 'Charlie',
            deutsch: 'Cäsar',
            animals: 'Chameleon'
        },

        d: {
            english: 'Delta',
            deutsch: 'Dora',
            animals: 'Dog'
        },

        e: {
            english: 'Echo',
            deutsch: 'Emil',
            animals: 'Elephant'
        },

        f: {
            english: 'Foxtrot',
            deutsch: 'Friedrich',
            animals: 'Ferret'
        },

        g: {
            english: 'Golf',
            deutsch: 'Gustav',
            animals: 'Gnu'
        },

        h: {
            english: 'Hotel',
            deutsch: 'Heinrich',
            animals: 'Hamster'
        },

        i: {
            english: 'Indigo',
            deutsch: 'Ida',
            animals: 'Ibex'
        },

        j: {
            english: 'Juliet',
            deutsch: 'Julius',
            animals: 'Kangaroo'
        },

        k: {
            english: 'Kilo',
            deutsch: 'Kaufmann',
            animals: 'Kitten'
        },

        l: {
            english: 'Lima',
            deutsch: 'Ludwig',
            animals: 'Ladybug'
        },

        m: {
            english: 'Martha',
            deutsch: 'Mike',
            animals: 'Mouse'
        },

        n: {
            english: 'November',
            deutsch: 'Nordpol',
            animals: 'Nautilus'
        },

        o: {
            english: 'Oscar',
            deutsch: 'Otto',
            animals: 'Orca'
        },

        p: {
            english: 'Papa',
            deutsch: 'Paula',
            animals: 'Pigeon'
        },

        q: {
            english: 'Quebec',
            deutsch: 'Quelle',
            animals: 'Quetzal'
        },

        r: {
            english: 'Romeo',
            deutsch: 'Richard',
            animals: 'Rabbit'
        },

        s: {
            english: 'Sierra',
            deutsch: 'Siegfried',
            animals: 'Squirrel'
        },

        t: {
            english: 'Tango',
            deutsch: 'Theodor',
            animals: 'Tuna'
        },

        u: {
            english: 'Uniform',
            deutsch: 'Ulrich',
            animals: 'Urchin'
        },

        v: {
            english: 'Victor',
            deutsch: 'Viktor',
            animals: 'Viper'
        },

        w: {
            english: 'Whiskey',
            deutsch: 'Wilhelm',
            animals: 'Wasp'
        },

        x: {
            english: 'X-Ray',
            deutsch: 'Xanthippe',
            animals: 'Xerus'
        },

        y: {
            english: 'Yankee',
            deutsch: 'Ypsilon',
            animals: 'Yak'
        },

        z: {
            english: 'Zulu',
            deutsch: 'Zeppelin',
            animals: 'Zebra'
        },

        0: {
            english: 'Zero',
            deutsch: 'Null',
            animals: 'Zero'
        },

        1: {
            english: 'One',
            deutsch: 'Eins',
            animals: 'One'
        },

        2: {
            english: 'Two',
            deutsch: 'Zwei',
            animals: 'Two'
        },

        3: {
            english: 'Three',
            deutsch: 'Drei',
            animals: 'Three'
        },

        4: {
            english: 'Four',
            deutsch: 'Vier',
            animals: 'Four'
        },

        5: {
            english: 'Five',
            deutsch: 'Fünf',
            animals: 'Five'
        },

        6: {
            english: 'Six',
            deutsch: 'Sechs',
            animals: 'Six'
        },

        7: {
            english: 'Seven',
            deutsch: 'Sieben',
            animals: 'Seven'
        },

        8: {
            english: 'Eight',
            deutsch: 'Acht',
            animals: 'Eight'
        },

        9: {
            english: 'Nine',
            deutsch: 'Neun',
            animals: 'Nine'
        }
    };

    var defaultLanguage = 'english';

    $('form').submit(function(e) {
        e.preventDefault();
    });

    $('form input').keyup(function(e) {
        if (e.keyCode != 16) {
            var characters = $(this).val().toLowerCase().split('');

            var phonetic = '';

            var result = $('div#result');

            for (i = 0; i < characters.length; i++) {
                if (typeof alphabet[characters[i]][defaultLanguage] !== 'undefined' && alphabet[characters[i]][defaultLanguage]) {
                    phonetic += alphabet[characters[i]][defaultLanguage] + ', ';
                }
            }

            if (characters.length) {
                result.html('
' + phonetic.substring(0, phonetic.length - 2) + '
');
            } else {
                result.html(result.attr('data-default'));
            }
        }
    });
});
<|endoftext|>


<commit_before>import moment from 'moment';
import _ from 'lodash';
import { logger } from './config';

class WakeLight {
  constructor() {
    this._changeObservers = [];
    // default alarms - 7am and 3pm
    this._alarms = [{
      duration: 60,
      hour: 7,
      id: 0,
      minute: 0,
      name: 'morning',
    }, {
      duration: 60,
      hour: 15,
      id: 1,
      minute: 15,
      name: 'afternoon',
    }];
  }

  /**
   * Event subscriber
   * @method on
   * @param {String}    event     the name of the event
   * @param {Function}  handler   the event handler
   */
  on(event, handler) {
    switch (event) {
      case 'change':
        this._changeObservers.push(handler);
        break;
      default:
        throw Error('event not available');
    }
  }

  /**
   * Change Event emitter
   * @method handleChange
   */
  handleChange(...args) {
    this._changeObservers.forEach(handler => {
      handler(...args);
    });
  }

  /**
   * add or update wakelight alarms. calling this method
   * will restart the alarms _timer and immediately check
   * if the alarms should be active
   * @method  addAlarms
   * @param   {Object}    An object where each key contains an alarm
   */
  addAlarms(alarms) {
    logger.debug('updating alarms');
    this._alarms = alarms;
  }

  /**
   * Check if the wakelight alarm should be active
   * @method checkAlarms
   * @param   [Object]    timeRef   optional momentjs time reference to check the alarm(s) against.
   *                                If no value is provided the system time is used.
   */
  checkAlarms(time) {
    if (!this._alarms) {
      throw Error('alarms not set =/');
    }

    // get current time reference
    const now = time || moment();
    logger.debug('time is');
    logger.debug(now);

    // look for an active alarm
    const enableAlarm = _.find(this._alarms, alarm => {
      const { hour: alarmHour,
        minute: alarmMinute } = alarm;

      // create time reference for the alarm enable
      const alarmEnable = moment()
        .hour(alarmHour)
        .minute(alarmMinute);

      return now.isSame(alarmEnable, 'minute');
    });

    // look for an active alarm
    const disableAlarm = _.find(this._alarms, alarm => {
      const { hour: alarmHour,
        minute: alarmMinute,
        duration: alarmDuration } = alarm;

      // create time reference for the alarm disable
      const alarmDisable = moment()
        .hour(alarmHour)
        .minute(alarmMinute)
        .add(alarmDuration, 'minutes');

      return now.isSame(alarmDisable, 'minute');
    });

    if (enableAlarm) {
      this.handleChange(true, now.format('dddd hh:mm:ss a'), enableAlarm.name);
    }

    if (disableAlarm) {
      this.handleChange(false, now.format('dddd hh:mm:ss a'), disableAlarm.name);
    }
  }

  /**
   * start the wakelight time poller
   * @method run
   */
  run() {
    logger.debug('running wakelight');
    if (!this._alarms) {
      logger.error('alarms not set');
      return;
    }
    this.checkAlarms();

    /*
     * set an interval to check the time and see if the alarm
     * should be activated
     */
    this._timer = setInterval(() => {
      this.checkAlarms();
    }, 60000);
  }

  /**
   * stop the wakelight time poller
   * @method stop
   */
  stop() {
    clearTimeout(this._timer);
    this._timer = null;
  }

  /**
   * Restart the wakelight time poller
   * @method restart
   */
  restart() {
    this.stop();
    this.run();
  }
}

export default WakeLight;
<commit_msg>updated logging statement<commit_after>import moment from 'moment';
import _ from 'lodash';
import { logger } from './config';

class WakeLight {
  constructor() {
    this._changeObservers = [];
    // default alarms - 7am and 3pm
    this._alarms = [{
      duration: 60,
      hour: 7,
      id: 0,
      minute: 0,
      name: 'morning',
    }, {
      duration: 60,
      hour: 15,
      id: 1,
      minute: 15,
      name: 'afternoon',
    }];
  }

  /**
   * Event subscriber
   * @method on
   * @param {String}    event     the name of the event
   * @param {Function}  handler   the event handler
   */
  on(event, handler) {
    switch (event) {
      case 'change':
        this._changeObservers.push(handler);
        break;
      default:
        throw Error('event not available');
    }
  }

  /**
   * Change Event emitter
   * @method handleChange
   */
  handleChange(...args) {
    this._changeObservers.forEach(handler => {
      handler(...args);
    });
  }

  /**
   * add or update wakelight alarms. calling this method
   * will restart the alarms _timer and immediately check
   * if the alarms should be active
   * @method  addAlarms
   * @param   {Object}    An object where each key contains an alarm
   */
  addAlarms(alarms) {
    logger.debug('updating alarms');
    this._alarms = alarms;
  }

  /**
   * Check if the wakelight alarm should be active
   * @method checkAlarms
   * @param   [Object]    timeRef   optional momentjs time reference to check the alarm(s) against.
   *                                If no value is provided the system time is used.
   */
  checkAlarms(time) {
    if (!this._alarms) {
      throw Error('alarms not set =/');
    }

    const format = 'dddd, MMMM Do YYYY, h:mm:ss a';

    // get current time reference
    const now = time || moment();
    logger.debug(`Time is ${now.format(format)}`);

    // look for an active alarm
    const enableAlarm = _.find(this._alarms, alarm => {
      const { hour: alarmHour,
        minute: alarmMinute } = alarm;

      // create time reference for the alarm enable
      const alarmEnable = moment()
        .hour(alarmHour)
        .minute(alarmMinute);

      return now.isSame(alarmEnable, 'minute');
    });

    // look for an active alarm
    const disableAlarm = _.find(this._alarms, alarm => {
      const { hour: alarmHour,
        minute: alarmMinute,
        duration: alarmDuration } = alarm;

      // create time reference for the alarm disable
      const alarmDisable = moment()
        .hour(alarmHour)
        .minute(alarmMinute)
        .add(alarmDuration, 'minutes');

      return now.isSame(alarmDisable, 'minute');
    });

    if (enableAlarm) {
      this.handleChange(true, now.format('dddd hh:mm:ss a'), enableAlarm.name);
    }

    if (disableAlarm) {
      this.handleChange(false, now.format('dddd hh:mm:ss a'), disableAlarm.name);
    }
  }

  /**
   * start the wakelight time poller
   * @method run
   */
  run() {
    logger.debug('running wakelight');
    if (!this._alarms) {
      logger.error('alarms not set');
      return;
    }
    this.checkAlarms();

    /*
     * set an interval to check the time and see if the alarm
     * should be activated
     */
    this._timer = setInterval(() => {
      this.checkAlarms();
    }, 60000);
  }

  /**
   * stop the wakelight time poller
   * @method stop
   */
  stop() {
    clearTimeout(this._timer);
    this._timer = null;
  }

  /**
   * Restart the wakelight time poller
   * @method restart
   */
  restart() {
    this.stop();
    this.run();
  }
}

export default WakeLight;
<|endoftext|>


<commit_before>
;(function() {
	var console = window.console = window.console || {};
	if (console._whistleConsole) {
		return;
	}
	console._whistleConsole = true;
	var JSON = window.JSON || patchJSON();
	function patchJSON() {
	    var JSON = {};
		var rx_one = /^[\],:{}\s]*$/,
	        rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
	        rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
	        rx_four = /(?:^|:|,)(?:\s*\[)+/g,
	        rx_escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
	        rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;

	    function f(n) {
	        return n < 10
	            ? '0' + n
	            : n;
	    }

	    function this_value() {
	        return this.valueOf();
	    }

	    if (typeof Date.prototype.toJSON !== 'function') {

	        Date.prototype.toJSON = function () {

	            return isFinite(this.valueOf())
	                ? this.getUTCFullYear() + '-' +
	                        f(this.getUTCMonth() + 1) + '-' +
	                        f(this.getUTCDate()) + 'T' +
	                        f(this.getUTCHours()) + ':' +
	                        f(this.getUTCMinutes()) + ':' +
	                        f(this.getUTCSeconds()) + 'Z'
	                : null;
	        };

	        Boolean.prototype.toJSON = this_value;
	        Number.prototype.toJSON = this_value;
	        String.prototype.toJSON = this_value;
	    }

	    var gap,
	        indent,
	        meta,
	        rep;

	    function quote(string) {

	        rx_escapable.lastIndex = 0;
	        return rx_escapable.test(string)
	            ? '"' + string.replace(rx_escapable, function (a) {
	                var c = meta[a];
	                return typeof c === 'string'
	                    ? c
	                    : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
	            }) + '"'
	            : '"' + string + '"';
	    }

	    function str(key, holder) {

	        var i,
	            k,
	            v,
	            length,
	            mind = gap,
	            partial,
	            value = holder[key];

	        if (value && typeof value === 'object' &&
	                typeof value.toJSON === 'function') {
	            value = value.toJSON(key);
	        }

	        if (typeof rep === 'function') {
	            value = rep.call(holder, key, value);
	        }

	        switch (typeof value) {
	        case 'string':
	            return quote(value);

	        case 'number':

	            return isFinite(value)
	                ? String(value)
	                : 'null';

	        case 'boolean':
	        case 'null':

	            return String(value);

	        case 'object':

	            if (!value) {
	                return 'null';
	            }

	            gap += indent;
	            partial = [];

	            if (Object.prototype.toString.apply(value) === '[object Array]') {

	                length = value.length;
	                for (i = 0; i < length; i += 1) {
	                    partial[i] = str(i, value) || 'null';
	                }

	                v = partial.length === 0
	                    ? '[]'
	                    : gap
	                        ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
	                        : '[' + partial.join(',') + ']';
	                gap = mind;
	                return v;
	            }

	            if (rep && typeof rep === 'object') {
	                length = rep.length;
	                for (i = 0; i < length; i += 1) {
	                    if (typeof rep[i] === 'string') {
	                        k = rep[i];
	                        v = str(k, value);
	                        if (v) {
	                            partial.push(quote(k) + (
	                                gap
	                                    ? ': '
	                                    : ':'
	                            ) + v);
	                        }
	                    }
	                }
	            } else {

	                for (k in value) {
	                    if (Object.prototype.hasOwnProperty.call(value, k)) {
	                        v = str(k, value);
	                        if (v) {
	                            partial.push(quote(k) + (
	                                gap
	                                    ? ': '
	                                    : ':'
	                            ) + v);
	                        }
	                    }
	                }
	            }

	            v = partial.length === 0
	                ? '{}'
	                : gap
	                    ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
	                    : '{' + partial.join(',') + '}';
	            gap = mind;
	            return v;
	        }
	    }

	    if (typeof JSON.stringify !== 'function') {
	        meta = {
	            '\b': '\\b',
	            '\t': '\\t',
	            '\n': '\\n',
	            '\f': '\\f',
	            '\r': '\\r',
	            '"': '\\"',
	            '\\': '\\\\'
	        };
	        JSON.stringify = function (value, replacer, space) {

	            var i;
	            gap = '';
	            indent = '';

	            if (typeof space === 'number') {
	                for (i = 0; i < space; i += 1) {
	                    indent += ' ';
	                }

	            } else if (typeof space === 'string') {
	                indent = space;
	            }

	            rep = replacer;
	            if (replacer && typeof replacer !== 'function' &&
	                    (typeof replacer !== 'object' ||
	                    typeof replacer.length !== 'number')) {
	                throw new Error('JSON.stringify');
	            }

	            return str('', {'': value});
	        };
	    }

	    return JSON;
	}

	function stringify(obj) {
		if (obj == null) {
			return '';
		}

		if (!obj) {
			return obj + '';
		}
		var type = typeof obj;
		if (type == 'string') {
			return obj;
		}

		if (obj instanceof Error) {
			return obj.stack || obj.message;
		}

		if (type == 'function') {
			return obj.toString();
		}

		return JSON.stringify(obj, null, '\t');
	}

	function addLog(level, text) {
		var img = new Image();
		var timer;
		img.src ='$LOG_CGI?level=' + level + '&text=' + encodeURIComponent(text);
		var preventGC = function() {
			img.onload = img.onerror = null;
			clearTimeout(timer);
		};
		img.onload = img.onerror = preventGC;
		timer = setTimeout(preventGC, 3000);
	}

	var levels = ['fatal', 'error', 'warn', 'info', 'debug', 'log'];
	var noop = function() {};
	for (var i = 0, len = levels.length; i < len; i++) {
		var level = levels[i];
		var fn = console[level] || noop;
		(function(level) {
			console[level] = function() {
				var result = [];
				for (var i = 0, len = arguments.length; i < len; i++) {
					result[i] = stringify(arguments[i]);
				}
				addLog(level, result.join(' '));
				fn.apply(this, arguments);
			};
		})(level);
	}

	window.onerror = function(message, filename, lineno, colno, error) {
		var pageInfo = '\r\nPage Url: ' + location.href + '\r\nUser Agent: ' + navigator.userAgent;
		if (error) {
			console.error((error.stack || error.message) + pageInfo);
		} else {
			console.error('Error: ' + message + '(' + filename
					+ ':' + lineno + ':' + (colno || 0) + ')' + pageInfo);
		}
	};

})();
<commit_msg>refactor: Don\'t execute script if not exists window or image object<commit_after>
;(function() {
  if (typeof window === 'undefined' || typeof Image === 'undefined') {
    return;
  }
	var console = window.console = window.console || {};
	if (console._whistleConsole) {
		return;
	}
	console._whistleConsole = true;
	var JSON = window.JSON || patchJSON();
	function patchJSON() {
	    var JSON = {};
		var rx_one = /^[\],:{}\s]*$/,
	        rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
	        rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
	        rx_four = /(?:^|:|,)(?:\s*\[)+/g,
	        rx_escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
	        rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;

	    function f(n) {
	        return n < 10
	            ? '0' + n
	            : n;
	    }

	    function this_value() {
	        return this.valueOf();
	    }

	    if (typeof Date.prototype.toJSON !== 'function') {

	        Date.prototype.toJSON = function () {

	            return isFinite(this.valueOf())
	                ? this.getUTCFullYear() + '-' +
	                        f(this.getUTCMonth() + 1) + '-' +
	                        f(this.getUTCDate()) + 'T' +
	                        f(this.getUTCHours()) + ':' +
	                        f(this.getUTCMinutes()) + ':' +
	                        f(this.getUTCSeconds()) + 'Z'
	                : null;
	        };

	        Boolean.prototype.toJSON = this_value;
	        Number.prototype.toJSON = this_value;
	        String.prototype.toJSON = this_value;
	    }

	    var gap,
	        indent,
	        meta,
	        rep;

	    function quote(string) {

	        rx_escapable.lastIndex = 0;
	        return rx_escapable.test(string)
	            ? '"' + string.replace(rx_escapable, function (a) {
	                var c = meta[a];
	                return typeof c === 'string'
	                    ? c
	                    : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
	            }) + '"'
	            : '"' + string + '"';
	    }

	    function str(key, holder) {

	        var i,
	            k,
	            v,
	            length,
	            mind = gap,
	            partial,
	            value = holder[key];

	        if (value && typeof value === 'object' &&
	                typeof value.toJSON === 'function') {
	            value = value.toJSON(key);
	        }

	        if (typeof rep === 'function') {
	            value = rep.call(holder, key, value);
	        }

	        switch (typeof value) {
	        case 'string':
	            return quote(value);

	        case 'number':

	            return isFinite(value)
	                ? String(value)
	                : 'null';

	        case 'boolean':
	        case 'null':

	            return String(value);

	        case 'object':

	            if (!value) {
	                return 'null';
	            }

	            gap += indent;
	            partial = [];

	            if (Object.prototype.toString.apply(value) === '[object Array]') {

	                length = value.length;
	                for (i = 0; i < length; i += 1) {
	                    partial[i] = str(i, value) || 'null';
	                }

	                v = partial.length === 0
	                    ? '[]'
	                    : gap
	                        ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
	                        : '[' + partial.join(',') + ']';
	                gap = mind;
	                return v;
	            }

	            if (rep && typeof rep === 'object') {
	                length = rep.length;
	                for (i = 0; i < length; i += 1) {
	                    if (typeof rep[i] === 'string') {
	                        k = rep[i];
	                        v = str(k, value);
	                        if (v) {
	                            partial.push(quote(k) + (
	                                gap
	                                    ? ': '
	                                    : ':'
	                            ) + v);
	                        }
	                    }
	                }
	            } else {

	                for (k in value) {
	                    if (Object.prototype.hasOwnProperty.call(value, k)) {
	                        v = str(k, value);
	                        if (v) {
	                            partial.push(quote(k) + (
	                                gap
	                                    ? ': '
	                                    : ':'
	                            ) + v);
	                        }
	                    }
	                }
	            }

	            v = partial.length === 0
	                ? '{}'
	                : gap
	                    ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
	                    : '{' + partial.join(',') + '}';
	            gap = mind;
	            return v;
	        }
	    }

	    if (typeof JSON.stringify !== 'function') {
	        meta = {
	            '\b': '\\b',
	            '\t': '\\t',
	            '\n': '\\n',
	            '\f': '\\f',
	            '\r': '\\r',
	            '"': '\\"',
	            '\\': '\\\\'
	        };
	        JSON.stringify = function (value, replacer, space) {

	            var i;
	            gap = '';
	            indent = '';

	            if (typeof space === 'number') {
	                for (i = 0; i < space; i += 1) {
	                    indent += ' ';
	                }

	            } else if (typeof space === 'string') {
	                indent = space;
	            }

	            rep = replacer;
	            if (replacer && typeof replacer !== 'function' &&
	                    (typeof replacer !== 'object' ||
	                    typeof replacer.length !== 'number')) {
	                throw new Error('JSON.stringify');
	            }

	            return str('', {'': value});
	        };
	    }

	    return JSON;
	}

	function stringify(obj) {
		if (obj == null) {
			return '';
		}

		if (!obj) {
			return obj + '';
		}
		var type = typeof obj;
		if (type == 'string') {
			return obj;
		}

		if (obj instanceof Error) {
			return obj.stack || obj.message;
		}

		if (type == 'function') {
			return obj.toString();
		}

		return JSON.stringify(obj, null, '\t');
	}

	function addLog(level, text) {
		var img = new Image();
		var timer;
		img.src ='$LOG_CGI?level=' + level + '&text=' + encodeURIComponent(text);
		var preventGC = function() {
			img.onload = img.onerror = null;
			clearTimeout(timer);
		};
		img.onload = img.onerror = preventGC;
		timer = setTimeout(preventGC, 3000);
	}

	var levels = ['fatal', 'error', 'warn', 'info', 'debug', 'log'];
	var noop = function() {};
	for (var i = 0, len = levels.length; i < len; i++) {
		var level = levels[i];
		var fn = console[level] || noop;
		(function(level) {
			console[level] = function() {
				var result = [];
				for (var i = 0, len = arguments.length; i < len; i++) {
					result[i] = stringify(arguments[i]);
				}
				addLog(level, result.join(' '));
				fn.apply(this, arguments);
			};
		})(level);
	}

	window.onerror = function(message, filename, lineno, colno, error) {
		var pageInfo = '\r\nPage Url: ' + location.href + '\r\nUser Agent: ' + navigator.userAgent;
		if (error) {
			console.error((error.stack || error.message) + pageInfo);
		} else {
			console.error('Error: ' + message + '(' + filename
					+ ':' + lineno + ':' + (colno || 0) + ')' + pageInfo);
		}
	};

})();
<|endoftext|>