<commit_before>mi2JS.comp.add('base/MultiCheck', 'base/Group', '',

// component initializer function that defines constructor and adds methods to the prototype
function(proto, superProto, comp, superComp){

	var $ = mi2JS;

	proto.itemTpl = {tag:'B'};

	proto.construct = function(el, tpl, parent){
		this.items = [];
		superProto.construct.call(this, el, tpl, parent);

		if(this.attr('single-value')) this.outFormat = function(value){
			return value[0];
		}

		this.listen(el,'click');
	};

	proto.on_click = function(evt){
		var target = this.item(evt.target);
		if(target){
			if(this.attr('single-value')){
				this.selectedIs(target.data('id'));
			}
		}
	};

	proto.setConfig = function(data){
		// NodeWrapper is cheap, so we discard the old ones
		this.innerHTML = '';
		this.items = {};
		// allow key:value object or array of {id:'',text:''} objects
		var isArray = data instanceof Array;
		for(var p in data){
			var id = isArray ? data[p].id : p;
			var item = $.add(this.el, this.itemTpl);
			item.data('id', id);
			item.setText(   isArray ? data[p].text  : data[p]);
			this.items[id] = item;
		}
	};

	proto.setValue = function(value){
		this.selectedIs(value);
	};

	proto.getValue = function(value){
		var ret = this.forEachGetArray(function(item){
			if(item.isSelected()) return item.data('id');
		});
		return $.format(ret, this.outFormat);
	};

});
<commit_msg>base/MultiCheck<commit_after>mi2JS.comp.add('base/MultiCheck', 'base/Group', '',

// component initializer function that defines constructor and adds methods to the prototype
function(proto, superProto, comp, superComp){

	var $ = mi2JS;

	proto.itemTpl = {tag:'B'};

	proto.construct = function(el, tpl, parent){
		this.items = {};
		superProto.construct.call(this, el, tpl, parent);

		if(this.attr('single-value')) this.outFormat = function(value){
			return value[0];
		}

		this.listen(el,'click');
	};

	proto.on_click = function(evt){
		var target = this.item(evt.target);
		if(target){
			if(this.attr('single-value')){
				this.selectedIs(target.data('id'));
			}else{
				target.setSelected(!target.isSelected());
			}
		}
	};

	proto.setConfig = function(data){
		// NodeWrapper is cheap, so we discard the old ones
		this.innerHTML = '';
		this.items = {};
		// allow key:value object or array of {id:'',text:''} objects
		var isArray = data instanceof Array;
		for(var p in data){
			var id = isArray ? data[p].id : p;
			var item = $.add(this.el, this.itemTpl);
			item.data('id', id);
			item.setText(   isArray ? data[p].text  : data[p]);
			this.items[id] = item;
		}
	};

	proto.setValue = function(value){
		this.selectedIs(value);
	};

	proto.getValue = function(value){
		var ret = this.forEachGetArray(function(item){
			if(item.isSelected()) return item.data('id');
		});
		return $.format(ret, this.outFormat);
	};

});
<|endoftext|>


<commit_before>
setDefaults({
	weekMode: 'fixed'
});

function BasicView(element, calendar, viewName) {
	var t = this;

	// exports
	t.renderBasic = renderBasic;
	t.setHeight = setHeight;
	t.setWidth = setWidth;
	t.renderDayOverlay = renderDayOverlay;
	t.defaultSelectionEnd = defaultSelectionEnd;
	t.renderSelection = renderSelection;
	t.clearSelection = clearSelection;
	t.reportDayClick = reportDayClick; // for selection (kinda hacky)
	t.dragStart = dragStart;
	t.dragStop = dragStop;
	t.defaultEventEnd = defaultEventEnd;
	t.getHoverListener = function() { return hoverListener };
	t.colContentLeft = colContentLeft;
	t.colContentRight = colContentRight;
	t.dayOfWeekCol = dayOfWeekCol;
	t.dateCell = dateCell;
	t.cellDate = cellDate;
	t.cellIsAllDay = function() { return true };
	t.allDayRow = allDayRow;
	t.allDayBounds = allDayBounds;
	t.getRowCnt = function() { return rowCnt };
	t.getColCnt = function() { return colCnt };
	t.getColWidth = function() { return colWidth };
	t.getDaySegmentContainer = function() { return daySegmentContainer };

	// imports
	View.call(t, element, calendar, viewName);
	OverlayManager.call(t);
	SelectionManager.call(t);
	BasicEventRenderer.call(t);
	var opt = t.opt;
	var trigger = t.trigger;
	var clearEvents = t.clearEvents;
	var renderOverlay = t.renderOverlay;
	var clearOverlays = t.clearOverlays;
	var daySelectionMousedown = t.daySelectionMousedown;
	var formatDate = calendar.formatDate;

	// locals

	var table;
	var head;
	var headCells;
	var body;
	var bodyRows;
	var bodyCells;
	var bodyFirstCells;
	var bodyCellTopInners;
	var daySegmentContainer;

	var viewWidth;
	var viewHeight;
	var colWidth;
	var weekNumberWidth;

	var rowCnt, colCnt;
	var coordinateGrid;
	var hoverListener;
	var colContentPositions;

	var rtl, dis, dit;
	var firstDay;
	var nwe; // no weekends? a 0 or 1 for easy computations
	var tm;
	var colFormat;
	var showWeekNumbers;
	var weekNumberTitle;
	var weekNumberFormat;

	/* Rendering
	------------------------------------------------------------*/

	disableTextSelection(element.addClass('fc-grid'));

	function renderBasic(r, c, showNumbers) {
		rowCnt = r;
		colCnt = c;
		updateOptions();
		var firstTime = !body;
		if (firstTime) {
			buildEventContainer();
		}else{
			clearEvents();
		}
		buildTable(showNumbers);
	}

	function updateOptions() {
		rtl = opt('isRTL');
		if (rtl) {
			dis = -1;
			dit = colCnt - 1;
		}else{
			dis = 1;
			dit = 0;
		}
		firstDay = opt('firstDay');
		nwe = opt('weekends') ? 0 : 1;
		tm = opt('theme') ? 'ui' : 'fc';
		colFormat = opt('columnFormat');

		// week # options. (TODO: bad, logic also in other views)
		showWeekNumbers = opt('weekNumbers');
		weekNumberTitle = opt('weekNumberTitle');
		if (opt('weekNumberCalculation') != 'iso') {
			weekNumberFormat = "w";
		}
		else {
			weekNumberFormat = "W";
		}
	}

	function buildEventContainer() {
		daySegmentContainer =
			$("
")
				.appendTo(element);
	}

	function buildTable(showNumbers) {
		var html = '';
		var i, j;
		var headerClass = tm + "-widget-header";
		var contentClass = tm + "-widget-content";
		var month = t.start.getMonth();
		var today = clearTime(new Date());
		var cellDate; // not to be confused with local function. TODO: better names
		var cellClasses;
		var cell;

		html += "
" +
		        "
" +
		        "
";

		if (showWeekNumbers) {
			html += "";
		}

		for (i=0; i<colCnt; i++) {
			html += "";
		}

		html += "
" +
		        "
" +
		        "
";

		for (i=0; i<rowCnt; i++) {
			html += "
";

			if (showWeekNumbers) {
				html += "" +
				        "
" +
				        "	";
			}

			for (j=0; j<colCnt; j++) {
				cellDate = _cellDate(i, j); // a little confusing. cellDate is local variable. _cellDate is private function

				cellClasses = [
					'fc-day',
					'fc-' + dayIDs[cellDate.getDay()],
					contentClass
				];
				if (cellDate.getMonth() != month) {
					cellClasses.push('fc-other-month');
				}
				if (+cellDate == +today) {
					cellClasses.push('fc-today');
					cellClasses.push(tm + '-state-highlight');
				}

				html += "" +
				        "
";
				if (showNumbers) {
					html += "
" + cellDate.getDate() + "
";
				}
				html += "
" +
				        "

" +
				        "
" +
				        "
" +
				        "	";
			}

			html += "
";
		}
		html += "
" +
		        "
";

		lockHeight(); // the unlock happens later, in setHeight()...
		if (table) {
			table.remove();
		}
		table = $(html).appendTo(element);

		head = table.find('thead');
		headCells = head.find('.fc-day-header');
		body = table.find('tbody');
		bodyRows = body.find('tr');
		bodyCells = body.find('.fc-day');
		bodyFirstCells = bodyRows.find('td:first-child');
		bodyCellTopInners = bodyRows.eq(0).find('.fc-day-content > div');

		markFirstLast(head.add(head.find('tr'))); // marks first+last tr/th's
		markFirstLast(bodyRows); // marks first+last td's
		bodyRows.eq(0).addClass('fc-first');
		bodyRows.filter(':last').addClass('fc-last');

		if (showWeekNumbers) {
			head.find('.fc-week-number').text(weekNumberTitle);
		}

		headCells.each(function(i, _cell) {
			var date = indexDate(i);
			$(_cell).text(formatDate(date, colFormat));
		});

		if (showWeekNumbers) {
			body.find('.fc-week-number > div').each(function(i, _cell) {
				var weekStart = _cellDate(i, 0);
				$(_cell).text(formatDate(weekStart, weekNumberFormat));
			});
		}

		bodyCells.each(function(i, _cell) {
			var date = indexDate(i);
			trigger('dayRender', t, date, $(_cell));
		});

		dayBind(bodyCells);
	}

	function setHeight(height) {
		viewHeight = height;

		var bodyHeight = viewHeight - head.height();
		var rowHeight;
		var rowHeightLast;
		var cell;

		if (opt('weekMode') == 'variable') {
			rowHeight = rowHeightLast = Math.floor(bodyHeight / (rowCnt==1 ? 2 : 6));
		}else{
			rowHeight = Math.floor(bodyHeight / rowCnt);
			rowHeightLast = bodyHeight - rowHeight * (rowCnt-1);
		}

		bodyFirstCells.each(function(i, _cell) {
			if (i < rowCnt) {
				cell = $(_cell);
				setMinHeight(
					cell.find('> div'),
					(i==rowCnt-1 ? rowHeightLast : rowHeight) - vsides(cell)
				);
			}
		});

		unlockHeight();
	}

	function setWidth(width) {
		viewWidth = width;
		colContentPositions.clear();

		weekNumberWidth = 0;
		if (showWeekNumbers) {
			weekNumberWidth = head.find('th.fc-week-number').outerWidth();
		}

		colWidth = Math.floor((viewWidth - weekNumberWidth) / colCnt);
		setOuterWidth(headCells.slice(0, -1), colWidth);
	}

	/* Day clicking and binding
	-----------------------------------------------------------*/

	function dayBind(days) {
		days.click(dayClick)
			.mousedown(daySelectionMousedown);
	}

	function dayClick(ev) {
		if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
			var date = parseISO8601($(this).data('date'));
			trigger('dayClick', this, date, true, ev);
		}
	}

	/* Semi-transparent Overlay Helpers
	------------------------------------------------------*/

	function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
		if (refreshCoordinateGrid) {
			coordinateGrid.build();
		}
		var rowStart = cloneDate(t.visStart);
		var rowEnd = addDays(cloneDate(rowStart), colCnt);
		for (var i=0; i<rowCnt; i++) {
			var stretchStart = new Date(Math.max(rowStart, overlayStart));
			var stretchEnd = new Date(Math.min(rowEnd, overlayEnd));
			if (stretchStart < stretchEnd) {
				var colStart, colEnd;
				if (rtl) {
					colStart = dayDiff(stretchEnd, rowStart)*dis+dit+1;
					colEnd = dayDiff(stretchStart, rowStart)*dis+dit+1;
				}else{
					colStart = dayDiff(stretchStart, rowStart);
					colEnd = dayDiff(stretchEnd, rowStart);
				}
				dayBind(
					renderCellOverlay(i, colStart, i, colEnd-1)
				);
			}
			addDays(rowStart, 7);
			addDays(rowEnd, 7);
		}
	}

	function renderCellOverlay(row0, col0, row1, col1) { // row1,col1 is inclusive
		var rect = coordinateGrid.rect(row0, col0, row1, col1, element);
		return renderOverlay(rect, element);
	}

	/* Selection
	-----------------------------------------------------------------------*/

	function defaultSelectionEnd(startDate, allDay) {
		return cloneDate(startDate);
	}

	function renderSelection(startDate, endDate, allDay) {
		renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); // rebuild every time???
	}

	function clearSelection() {
		clearOverlays();
	}

	function reportDayClick(date, allDay, ev) {
		var cell = dateCell(date);
		var _element = bodyCells[cell.row*colCnt + cell.col];
		trigger('dayClick', _element, date, allDay, ev);
	}

	/* External Dragging
	-----------------------------------------------------------------------*/

	function dragStart(_dragElement, ev, ui) {
		hoverListener.start(function(cell) {
			clearOverlays();
			if (cell) {
				renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
			}
		}, ev);
	}

	function dragStop(_dragElement, ev, ui) {
		var cell = hoverListener.stop();
		clearOverlays();
		if (cell) {
			var d = cellDate(cell);
			trigger('drop', _dragElement, d, true, ev, ui);
		}
	}

	/* Utilities
	--------------------------------------------------------*/

	function defaultEventEnd(event) {
		return cloneDate(event.start);
	}

	coordinateGrid = new CoordinateGrid(function(rows, cols) {
		var e, n, p;
		headCells.each(function(i, _e) {
			e = $(_e);
			n = e.offset().left;
			if (i) {
				p[1] = n;
			}
			p = [n];
			cols[i] = p;
		});
		p[1] = n + e.outerWidth();
		bodyRows.each(function(i, _e) {
			if (i < rowCnt) {
				e = $(_e);
				n = e.offset().top;
				if (i) {
					p[1] = n;
				}
				p = [n];
				rows[i] = p;
			}
		});
		p[1] = n + e.outerHeight();
	});

	hoverListener = new HoverListener(coordinateGrid);

	colContentPositions = new HorizontalPositionCache(function(col) {
		return bodyCellTopInners.eq(col);
	});

	function colContentLeft(col) {
		return colContentPositions.left(col);
	}

	function colContentRight(col) {
		return colContentPositions.right(col);
	}

	function dateCell(date) {
		return {
			row: Math.floor(dayDiff(date, t.visStart) / 7),
			col: dayOfWeekCol(date.getDay())
		};
	}

	function cellDate(cell) {
		return _cellDate(cell.row, cell.col);
	}

	function _cellDate(row, col) {
		return addDays(cloneDate(t.visStart), row*7 + col*dis+dit);
		// what about weekends in middle of week?
	}

	function indexDate(index) {
		return _cellDate(Math.floor(index/colCnt), index%colCnt);
	}

	function dayOfWeekCol(dayOfWeek) {
		return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt) * dis + dit;
	}

	function allDayRow(i) {
		return bodyRows.eq(i);
	}

	function allDayBounds(i) {
		var left = 0;
		if (showWeekNumbers) {
			left += weekNumberWidth;
		}
		return {
			left: left,
			right: viewWidth
		};
	}

	// makes sure height doesn't collapse while we destroy/render new cells
	// (this causes a bad end-user scrollbar jump)
	// TODO: generalize this for all view rendering. (also in Calendar.js)

	function lockHeight() {
		setMinHeight(element, element.height());
	}

	function unlockHeight() {
		setMinHeight(element, 1);
	}

}
<commit_msg>fix header classNames bug in basic views<commit_after>
setDefaults({
	weekMode: 'fixed'
});

function BasicView(element, calendar, viewName) {
	var t = this;

	// exports
	t.renderBasic = renderBasic;
	t.setHeight = setHeight;
	t.setWidth = setWidth;
	t.renderDayOverlay = renderDayOverlay;
	t.defaultSelectionEnd = defaultSelectionEnd;
	t.renderSelection = renderSelection;
	t.clearSelection = clearSelection;
	t.reportDayClick = reportDayClick; // for selection (kinda hacky)
	t.dragStart = dragStart;
	t.dragStop = dragStop;
	t.defaultEventEnd = defaultEventEnd;
	t.getHoverListener = function() { return hoverListener };
	t.colContentLeft = colContentLeft;
	t.colContentRight = colContentRight;
	t.dayOfWeekCol = dayOfWeekCol;
	t.dateCell = dateCell;
	t.cellDate = cellDate;
	t.cellIsAllDay = function() { return true };
	t.allDayRow = allDayRow;
	t.allDayBounds = allDayBounds;
	t.getRowCnt = function() { return rowCnt };
	t.getColCnt = function() { return colCnt };
	t.getColWidth = function() { return colWidth };
	t.getDaySegmentContainer = function() { return daySegmentContainer };

	// imports
	View.call(t, element, calendar, viewName);
	OverlayManager.call(t);
	SelectionManager.call(t);
	BasicEventRenderer.call(t);
	var opt = t.opt;
	var trigger = t.trigger;
	var clearEvents = t.clearEvents;
	var renderOverlay = t.renderOverlay;
	var clearOverlays = t.clearOverlays;
	var daySelectionMousedown = t.daySelectionMousedown;
	var formatDate = calendar.formatDate;

	// locals

	var table;
	var head;
	var headCells;
	var body;
	var bodyRows;
	var bodyCells;
	var bodyFirstCells;
	var bodyCellTopInners;
	var daySegmentContainer;

	var viewWidth;
	var viewHeight;
	var colWidth;
	var weekNumberWidth;

	var rowCnt, colCnt;
	var coordinateGrid;
	var hoverListener;
	var colContentPositions;

	var rtl, dis, dit;
	var firstDay;
	var nwe; // no weekends? a 0 or 1 for easy computations
	var tm;
	var colFormat;
	var showWeekNumbers;
	var weekNumberTitle;
	var weekNumberFormat;

	/* Rendering
	------------------------------------------------------------*/

	disableTextSelection(element.addClass('fc-grid'));

	function renderBasic(r, c, showNumbers) {
		rowCnt = r;
		colCnt = c;
		updateOptions();
		var firstTime = !body;
		if (firstTime) {
			buildEventContainer();
		}else{
			clearEvents();
		}
		buildTable(showNumbers);
	}

	function updateOptions() {
		rtl = opt('isRTL');
		if (rtl) {
			dis = -1;
			dit = colCnt - 1;
		}else{
			dis = 1;
			dit = 0;
		}
		firstDay = opt('firstDay');
		nwe = opt('weekends') ? 0 : 1;
		tm = opt('theme') ? 'ui' : 'fc';
		colFormat = opt('columnFormat');

		// week # options. (TODO: bad, logic also in other views)
		showWeekNumbers = opt('weekNumbers');
		weekNumberTitle = opt('weekNumberTitle');
		if (opt('weekNumberCalculation') != 'iso') {
			weekNumberFormat = "w";
		}
		else {
			weekNumberFormat = "W";
		}
	}

	function buildEventContainer() {
		daySegmentContainer =
			$("
")
				.appendTo(element);
	}

	function buildTable(showNumbers) {
		var html = '';
		var i, j;
		var headerClass = tm + "-widget-header";
		var contentClass = tm + "-widget-content";
		var month = t.start.getMonth();
		var today = clearTime(new Date());
		var cellDate; // not to be confused with local function. TODO: better names
		var cellClasses;
		var cell;

		html += "
" +
		        "
" +
		        "
";

		if (showWeekNumbers) {
			html += "";
		}

		for (i=0; i<colCnt; i++) {
			cellDate = _cellDate(0, i); // a little confusing. cellDate is local variable. _cellDate is private function
			html += "";
		}

		html += "
" +
		        "
" +
		        "
";

		for (i=0; i<rowCnt; i++) {
			html += "
";

			if (showWeekNumbers) {
				html += "" +
				        "
" +
				        "	";
			}

			for (j=0; j<colCnt; j++) {
				cellDate = _cellDate(i, j); // a little confusing. cellDate is local variable. _cellDate is private function

				cellClasses = [
					'fc-day',
					'fc-' + dayIDs[cellDate.getDay()],
					contentClass
				];
				if (cellDate.getMonth() != month) {
					cellClasses.push('fc-other-month');
				}
				if (+cellDate == +today) {
					cellClasses.push('fc-today');
					cellClasses.push(tm + '-state-highlight');
				}

				html += "" +
				        "
";
				if (showNumbers) {
					html += "
" + cellDate.getDate() + "
";
				}
				html += "
" +
				        "

" +
				        "
" +
				        "
" +
				        "	";
			}

			html += "
";
		}
		html += "
" +
		        "
";

		lockHeight(); // the unlock happens later, in setHeight()...
		if (table) {
			table.remove();
		}
		table = $(html).appendTo(element);

		head = table.find('thead');
		headCells = head.find('.fc-day-header');
		body = table.find('tbody');
		bodyRows = body.find('tr');
		bodyCells = body.find('.fc-day');
		bodyFirstCells = bodyRows.find('td:first-child');
		bodyCellTopInners = bodyRows.eq(0).find('.fc-day-content > div');

		markFirstLast(head.add(head.find('tr'))); // marks first+last tr/th's
		markFirstLast(bodyRows); // marks first+last td's
		bodyRows.eq(0).addClass('fc-first');
		bodyRows.filter(':last').addClass('fc-last');

		if (showWeekNumbers) {
			head.find('.fc-week-number').text(weekNumberTitle);
		}

		headCells.each(function(i, _cell) {
			var date = indexDate(i);
			$(_cell).text(formatDate(date, colFormat));
		});

		if (showWeekNumbers) {
			body.find('.fc-week-number > div').each(function(i, _cell) {
				var weekStart = _cellDate(i, 0);
				$(_cell).text(formatDate(weekStart, weekNumberFormat));
			});
		}

		bodyCells.each(function(i, _cell) {
			var date = indexDate(i);
			trigger('dayRender', t, date, $(_cell));
		});

		dayBind(bodyCells);
	}

	function setHeight(height) {
		viewHeight = height;

		var bodyHeight = viewHeight - head.height();
		var rowHeight;
		var rowHeightLast;
		var cell;

		if (opt('weekMode') == 'variable') {
			rowHeight = rowHeightLast = Math.floor(bodyHeight / (rowCnt==1 ? 2 : 6));
		}else{
			rowHeight = Math.floor(bodyHeight / rowCnt);
			rowHeightLast = bodyHeight - rowHeight * (rowCnt-1);
		}

		bodyFirstCells.each(function(i, _cell) {
			if (i < rowCnt) {
				cell = $(_cell);
				setMinHeight(
					cell.find('> div'),
					(i==rowCnt-1 ? rowHeightLast : rowHeight) - vsides(cell)
				);
			}
		});

		unlockHeight();
	}

	function setWidth(width) {
		viewWidth = width;
		colContentPositions.clear();

		weekNumberWidth = 0;
		if (showWeekNumbers) {
			weekNumberWidth = head.find('th.fc-week-number').outerWidth();
		}

		colWidth = Math.floor((viewWidth - weekNumberWidth) / colCnt);
		setOuterWidth(headCells.slice(0, -1), colWidth);
	}

	/* Day clicking and binding
	-----------------------------------------------------------*/

	function dayBind(days) {
		days.click(dayClick)
			.mousedown(daySelectionMousedown);
	}

	function dayClick(ev) {
		if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
			var date = parseISO8601($(this).data('date'));
			trigger('dayClick', this, date, true, ev);
		}
	}

	/* Semi-transparent Overlay Helpers
	------------------------------------------------------*/

	function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
		if (refreshCoordinateGrid) {
			coordinateGrid.build();
		}
		var rowStart = cloneDate(t.visStart);
		var rowEnd = addDays(cloneDate(rowStart), colCnt);
		for (var i=0; i<rowCnt; i++) {
			var stretchStart = new Date(Math.max(rowStart, overlayStart));
			var stretchEnd = new Date(Math.min(rowEnd, overlayEnd));
			if (stretchStart < stretchEnd) {
				var colStart, colEnd;
				if (rtl) {
					colStart = dayDiff(stretchEnd, rowStart)*dis+dit+1;
					colEnd = dayDiff(stretchStart, rowStart)*dis+dit+1;
				}else{
					colStart = dayDiff(stretchStart, rowStart);
					colEnd = dayDiff(stretchEnd, rowStart);
				}
				dayBind(
					renderCellOverlay(i, colStart, i, colEnd-1)
				);
			}
			addDays(rowStart, 7);
			addDays(rowEnd, 7);
		}
	}

	function renderCellOverlay(row0, col0, row1, col1) { // row1,col1 is inclusive
		var rect = coordinateGrid.rect(row0, col0, row1, col1, element);
		return renderOverlay(rect, element);
	}

	/* Selection
	-----------------------------------------------------------------------*/

	function defaultSelectionEnd(startDate, allDay) {
		return cloneDate(startDate);
	}

	function renderSelection(startDate, endDate, allDay) {
		renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); // rebuild every time???
	}

	function clearSelection() {
		clearOverlays();
	}

	function reportDayClick(date, allDay, ev) {
		var cell = dateCell(date);
		var _element = bodyCells[cell.row*colCnt + cell.col];
		trigger('dayClick', _element, date, allDay, ev);
	}

	/* External Dragging
	-----------------------------------------------------------------------*/

	function dragStart(_dragElement, ev, ui) {
		hoverListener.start(function(cell) {
			clearOverlays();
			if (cell) {
				renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
			}
		}, ev);
	}

	function dragStop(_dragElement, ev, ui) {
		var cell = hoverListener.stop();
		clearOverlays();
		if (cell) {
			var d = cellDate(cell);
			trigger('drop', _dragElement, d, true, ev, ui);
		}
	}

	/* Utilities
	--------------------------------------------------------*/

	function defaultEventEnd(event) {
		return cloneDate(event.start);
	}

	coordinateGrid = new CoordinateGrid(function(rows, cols) {
		var e, n, p;
		headCells.each(function(i, _e) {
			e = $(_e);
			n = e.offset().left;
			if (i) {
				p[1] = n;
			}
			p = [n];
			cols[i] = p;
		});
		p[1] = n + e.outerWidth();
		bodyRows.each(function(i, _e) {
			if (i < rowCnt) {
				e = $(_e);
				n = e.offset().top;
				if (i) {
					p[1] = n;
				}
				p = [n];
				rows[i] = p;
			}
		});
		p[1] = n + e.outerHeight();
	});

	hoverListener = new HoverListener(coordinateGrid);

	colContentPositions = new HorizontalPositionCache(function(col) {
		return bodyCellTopInners.eq(col);
	});

	function colContentLeft(col) {
		return colContentPositions.left(col);
	}

	function colContentRight(col) {
		return colContentPositions.right(col);
	}

	function dateCell(date) {
		return {
			row: Math.floor(dayDiff(date, t.visStart) / 7),
			col: dayOfWeekCol(date.getDay())
		};
	}

	function cellDate(cell) {
		return _cellDate(cell.row, cell.col);
	}

	function _cellDate(row, col) {
		return addDays(cloneDate(t.visStart), row*7 + col*dis+dit);
		// what about weekends in middle of week?
	}

	function indexDate(index) {
		return _cellDate(Math.floor(index/colCnt), index%colCnt);
	}

	function dayOfWeekCol(dayOfWeek) {
		return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt) * dis + dit;
	}

	function allDayRow(i) {
		return bodyRows.eq(i);
	}

	function allDayBounds(i) {
		var left = 0;
		if (showWeekNumbers) {
			left += weekNumberWidth;
		}
		return {
			left: left,
			right: viewWidth
		};
	}

	// makes sure height doesn't collapse while we destroy/render new cells
	// (this causes a bad end-user scrollbar jump)
	// TODO: generalize this for all view rendering. (also in Calendar.js)

	function lockHeight() {
		setMinHeight(element, element.height());
	}

	function unlockHeight() {
		setMinHeight(element, 1);
	}

}
<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved. See LICENSE file for details.
 */

var Rectangle = this.Rectangle = Base.extend({
	beans: true,

	initialize: function(arg0, arg1, arg2, arg3) {
		if (arguments.length == 1) {
			// Use 0 as defaults, in case we're reading from a Point or Size
			this.x = arg0.x || 0;
			this.y = arg0.y || 0;
			this.width = arg0.width || 0;
			this.height = arg0.height || 0;
		} else if (arguments.length == 2) {
			if (arg1.x !== undefined) {
				// new Rectangle(point1, point2)
				var point1 = Point.read(arguments, 0, 1);
				var point2 = Point.read(arguments, 1, 1);
				this.x = point1.x;
				this.y = point1.y;
				this.width = point2.x - point1.x;
				this.height = point2.y - point1.y;
				if (this.width < 0) {
					this.x = point2.x;
					this.width = -this.width;
				}
				if (this.height < 0) {
					this.y = point2.y;
					this.height = -this.height;
				}
			} else {
				// new Rectangle(point, size)
				var point = Point.read(arguments, 0, 1);
				var size = Size.read(arguments, 1, 1);
				this.x = point.x;
				this.y = point.y;
				this.width = size.width;
				this.height = size.height;
			}
		} else if (arguments.length == 4) {
			// new Rectangle(x, y, width, height)
			this.x = arg0;
			this.y = arg1;
			this.width = arg2;
			this.height = arg3;
		} else {
			// new Rectangle()
			this.x = this.y = this.width = this.height = 0;
		}
	},

	set: function(x, y, width, height) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		return this;
	},

	getPoint: function() {
		return Point.create(this.x, this.y);
	},

	setPoint: function() {
		var point = Point.read(arguments);
		this.x = point.x;
		this.y = point.y;
		return this;
	},

	getSize: function() {
		return Size.create(this.width, this.height);
	},

	setSize: function() {
		var size = Size.read(arguments);
		this.width = size.width;
		this.height = size.height;
		return this;
	},

	getLeft: function() {
		return this.x;
	},

	setLeft: function(left) {
		// right should not move
		this.width -= left - this.x;
		this.x = left;
		return this;
	},

	getTop: function() {
		return this.y;
	},

	setTop: function(top) {
		this.height -= top - this.y;
		this.y = top;
		return this;
	},

	getRight: function() {
		return this.x + this.width;
	},

	setRight: function(right) {
		this.width = right - this.x;
		return this;
	},

	getBottom: function() {
		return this.y + this.height;
	},

	setBottom: function(bottom) {
		this.height = bottom - this.y;
		return this;
	},

	getCenterX: function() {
		return this.x + this.width * 0.5;
	},

	setCenterX: function(x) {
		this.x = x - this.width * 0.5;
		return this;
	},

	getCenterY: function() {
		return this.y + this.height * 0.5;
	},

	setCenterY: function(y) {
		this.y = y - this.height * 0.5;
		return this;
	},

	getCenter: function() {
		return Point.create(this.getCenterX(), this.getCenterY());
	},

	setCenter: function() {
		var pt = Point.read(arguments);
		return this.setCenterX(pt.x).setCenterY(pt.y);
	},

	clone: function() {
		return new Rectangle(this);
	},

	equals: function() {
		var rect = Rectangle.read(arguments);
		return this.x == rect.x && this.y == rect.y
				&& this.width == rect.width && this.height == rect.height;
	},

	isEmpty: function() {
		return this.width == 0 || this.height == 0;
	},

	contains: function(rect) {
		if (rect.width !== undefined) {
			return rect.x >= this.x && rect.y >= this.y
					&& rect.x + rect.width <= this.x + this.width
					&& rect.y + rect.height <= this.y + this.height;
		} else {
			var point = Point.read(arguments);
			return point.x >= this.x && point.y >= this.y
					&& point.x <= this.x + this.width
					&& point.y <= this.y + this.height;
		}
	},

	intersects: function() {
		var rect = Rectangle.read(arguments);
		return rect.x + rect.width > this.x
				&& rect.y + rect.height > this.y
				&& rect.x < this.x + this.width
				&& rect.y < this.y + this.height;
	},

	intersect: function() {
		var rect = Rectangle.read(arguments);
			x1 = Math.max(this.x, rect.x),
			y1 = Math.max(this.y, rect.y),
			x2 = Math.min(this.x + this.width, rect.x + rect.width),
			y2 = Math.min(this.y + this.height, rect.y + rect.height);
		return Rectangle.create(x1, y1, x2 - x1, y2 - y1);
	},

	unite: function() {
		var rect = Rectangle.read(arguments),
			x1 = Math.min(this.x, rect.x),
			y1 = Math.min(this.y, rect.y),
			x2 = Math.max(this.x + this.width, rect.x + rect.width),
			y2 = Math.max(this.y + this.height, rect.y + rect.height);
		return Rectangle.create(x1, y1, x2 - x1, y2 - y1);
	},

	include: function() {
		var point = Point.read(arguments),
			x1 = Math.min(this.x, point.x),
			y1 = Math.min(this.y, point.y),
			x2 = Math.max(this.x + this.width, point.x),
			y2 = Math.max(this.y + this.height, point.y);
		return Rectangle.create(x1, y1, x2 - x1, y2 - y1);
	},

	toString: function() {
		return '{ x: ' + this.x
				+ ', y: ' + this.y
				+ ', width: ' + this.width
				+ ', height: ' + this.height
				+ ' }';
	},

	statics: {
		// See Point.create()
		create: function(x, y, width, height) {
			return new Rectangle(Rectangle.dont).set(x, y, width, height);
		}
	}
}, new function() {
	var keys = [
		['Top', 'Left'], ['Top', 'Right'],
		['Bottom', 'Left'], ['Bottom', 'Right'],
		['Left', 'Center'], ['Top', 'Center'],
		['Right', 'Center'], ['Bottom', 'Center']
	];

	return Base.each(keys,
		function(parts, index) {
			var key = parts.join('');
			// find out if the first of the pair is an x or y property,
			// by checking the first character for [R]ight or [L]eft;
			var xFirst = /^[RL]/.test(key);
			// Rename Center to CenterX or CenterY:
			if (index >= 4) parts[1] += xFirst ? 'Y' : 'X';
			var xIndex = xFirst ? 0 : 1,
				yIndex = xFirst ? 1 : 0,
				getX = 'get' + parts[xIndex],
				getY = 'get' + parts[yIndex],
				setX = 'set' + parts[xIndex],
				setY = 'set' + parts[yIndex];
			this['get' + key] = function() {
				return Point.create(this[getX](), this[getY]());
			};
			this['set' + key] = function(value) {
				var pt = Point.read(arguments);
				return this[setX](pt.x) // Note: chaining here!
						   [setY](pt.y);
			};
		}, { beans: true });
});
<commit_msg>Simplify new getter / setter generation code a bit.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved. See LICENSE file for details.
 */

var Rectangle = this.Rectangle = Base.extend({
	beans: true,

	initialize: function(arg0, arg1, arg2, arg3) {
		if (arguments.length == 1) {
			// Use 0 as defaults, in case we're reading from a Point or Size
			this.x = arg0.x || 0;
			this.y = arg0.y || 0;
			this.width = arg0.width || 0;
			this.height = arg0.height || 0;
		} else if (arguments.length == 2) {
			if (arg1.x !== undefined) {
				// new Rectangle(point1, point2)
				var point1 = Point.read(arguments, 0, 1);
				var point2 = Point.read(arguments, 1, 1);
				this.x = point1.x;
				this.y = point1.y;
				this.width = point2.x - point1.x;
				this.height = point2.y - point1.y;
				if (this.width < 0) {
					this.x = point2.x;
					this.width = -this.width;
				}
				if (this.height < 0) {
					this.y = point2.y;
					this.height = -this.height;
				}
			} else {
				// new Rectangle(point, size)
				var point = Point.read(arguments, 0, 1);
				var size = Size.read(arguments, 1, 1);
				this.x = point.x;
				this.y = point.y;
				this.width = size.width;
				this.height = size.height;
			}
		} else if (arguments.length == 4) {
			// new Rectangle(x, y, width, height)
			this.x = arg0;
			this.y = arg1;
			this.width = arg2;
			this.height = arg3;
		} else {
			// new Rectangle()
			this.x = this.y = this.width = this.height = 0;
		}
	},

	set: function(x, y, width, height) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		return this;
	},

	getPoint: function() {
		return Point.create(this.x, this.y);
	},

	setPoint: function() {
		var point = Point.read(arguments);
		this.x = point.x;
		this.y = point.y;
		return this;
	},

	getSize: function() {
		return Size.create(this.width, this.height);
	},

	setSize: function() {
		var size = Size.read(arguments);
		this.width = size.width;
		this.height = size.height;
		return this;
	},

	getLeft: function() {
		return this.x;
	},

	setLeft: function(left) {
		// right should not move
		this.width -= left - this.x;
		this.x = left;
		return this;
	},

	getTop: function() {
		return this.y;
	},

	setTop: function(top) {
		this.height -= top - this.y;
		this.y = top;
		return this;
	},

	getRight: function() {
		return this.x + this.width;
	},

	setRight: function(right) {
		this.width = right - this.x;
		return this;
	},

	getBottom: function() {
		return this.y + this.height;
	},

	setBottom: function(bottom) {
		this.height = bottom - this.y;
		return this;
	},

	getCenterX: function() {
		return this.x + this.width * 0.5;
	},

	setCenterX: function(x) {
		this.x = x - this.width * 0.5;
		return this;
	},

	getCenterY: function() {
		return this.y + this.height * 0.5;
	},

	setCenterY: function(y) {
		this.y = y - this.height * 0.5;
		return this;
	},

	getCenter: function() {
		return Point.create(this.getCenterX(), this.getCenterY());
	},

	setCenter: function() {
		var pt = Point.read(arguments);
		return this.setCenterX(pt.x).setCenterY(pt.y);
	},

	clone: function() {
		return new Rectangle(this);
	},

	equals: function() {
		var rect = Rectangle.read(arguments);
		return this.x == rect.x && this.y == rect.y
				&& this.width == rect.width && this.height == rect.height;
	},

	isEmpty: function() {
		return this.width == 0 || this.height == 0;
	},

	contains: function(rect) {
		if (rect.width !== undefined) {
			return rect.x >= this.x && rect.y >= this.y
					&& rect.x + rect.width <= this.x + this.width
					&& rect.y + rect.height <= this.y + this.height;
		} else {
			var point = Point.read(arguments);
			return point.x >= this.x && point.y >= this.y
					&& point.x <= this.x + this.width
					&& point.y <= this.y + this.height;
		}
	},

	intersects: function() {
		var rect = Rectangle.read(arguments);
		return rect.x + rect.width > this.x
				&& rect.y + rect.height > this.y
				&& rect.x < this.x + this.width
				&& rect.y < this.y + this.height;
	},

	intersect: function() {
		var rect = Rectangle.read(arguments);
			x1 = Math.max(this.x, rect.x),
			y1 = Math.max(this.y, rect.y),
			x2 = Math.min(this.x + this.width, rect.x + rect.width),
			y2 = Math.min(this.y + this.height, rect.y + rect.height);
		return Rectangle.create(x1, y1, x2 - x1, y2 - y1);
	},

	unite: function() {
		var rect = Rectangle.read(arguments),
			x1 = Math.min(this.x, rect.x),
			y1 = Math.min(this.y, rect.y),
			x2 = Math.max(this.x + this.width, rect.x + rect.width),
			y2 = Math.max(this.y + this.height, rect.y + rect.height);
		return Rectangle.create(x1, y1, x2 - x1, y2 - y1);
	},

	include: function() {
		var point = Point.read(arguments),
			x1 = Math.min(this.x, point.x),
			y1 = Math.min(this.y, point.y),
			x2 = Math.max(this.x + this.width, point.x),
			y2 = Math.max(this.y + this.height, point.y);
		return Rectangle.create(x1, y1, x2 - x1, y2 - y1);
	},

	toString: function() {
		return '{ x: ' + this.x
				+ ', y: ' + this.y
				+ ', width: ' + this.width
				+ ', height: ' + this.height
				+ ' }';
	},

	statics: {
		// See Point.create()
		create: function(x, y, width, height) {
			return new Rectangle(Rectangle.dont).set(x, y, width, height);
		}
	}
}, new function() {
	var keys = [
		['Top', 'Left'], ['Top', 'Right'],
		['Bottom', 'Left'], ['Bottom', 'Right'],
		['Left', 'Center'], ['Top', 'Center'],
		['Right', 'Center'], ['Bottom', 'Center']
	];

	return Base.each(keys,
		function(parts, index) {
			var key = parts.join('');
			// find out if the first of the pair is an x or y property,
			// by checking the first character for [R]ight or [L]eft;
			var xFirst = /^[RL]/.test(key);
			// Rename Center to CenterX or CenterY:
			if (index >= 4)
				parts[1] += xFirst ? 'Y' : 'X';
			var x = parts[xFirst ? 0 : 1],
				y = parts[xFirst ? 1 : 0],
				getX = 'get' + x,
				getY = 'get' + y,
				setX = 'set' + x,
				setY = 'set' + y;
			this['get' + key] = function() {
				return Point.create(this[getX](), this[getY]());
			};
			this['set' + key] = function(value) {
				var pt = Point.read(arguments);
				return this[setX](pt.x)[setY](pt.y); // Note: chaining here!
			};
		}, { beans: true });
});
<|endoftext|>


<commit_before>
 /**
  * @see ./demo/defile/window.html
  * @namespace basis.ui.window
  */

  var namespace = this.path;

  //
  // import names
  //

  var Class = basis.Class;
  var arrayFrom = basis.array.from;
  var resolveValue = require('basis.data').resolveValue;
  var DOM = require('basis.dom');
  var cssom = require('basis.cssom');
  var createEvent = require('basis.event').create;
  var Node = require('basis.ui').Node;
  var ButtonPanel = require('basis.ui.button').ButtonPanel;
  var MoveableElement = require('basis.dragdrop').MoveableElement;

  //
  // definitions
  //

  var dict = require('basis.l10n').dictionary(__filename);

  var templates = require('basis.template').define(namespace, {
    Blocker: resource('./templates/window/Blocker.tmpl'),
    Window: resource('./templates/window/Window.tmpl'),
    TitleButton: resource('./templates/window/TitleButton.tmpl'),
    ButtonPanel: resource('./templates/window/ButtonPanel.tmpl'),
    windowManager: resource('./templates/window/windowManager.tmpl')
  });

  //
  // main part
  //

 /**
  * @class
  */
  var Blocker = Class(Node, {
    className: namespace + '.Blocker',

    template: templates.Blocker,

    captureElement: null,
    capture: function(element, zIndex){
      this.captureElement = DOM.get(element || document.body);
      if (this.captureElement)
      {
        DOM.insert(this.captureElement, this.element);
        this.element.style.zIndex = zIndex || 1000;
      }
    },
    release: function(){
      if (this.captureElement)
      {
        if (this.element.parentNode == this.captureElement)
          DOM.remove(this.element);

        this.captureElement = null;
      }
    },

    destroy: function(){
      this.release();

      Node.prototype.destroy.call(this);
    }
  });

  //
  //  Window
  //

  var DD_HANDLER = {
    start: function(){
      this.autocenter = false;
    }
  };

 /**
  * @class
  */
  var Window = Class(Node, {
    className: namespace + '.Window',

    emit_open: createEvent('open'),
    emit_close: createEvent('close'),

    closeOnEscape: true,
    autocenter: true,
    autocenter_: false,
    modal: false,
    closed: true,
    moveable: true,
    zIndex: 0,

    visible: false,
    visibleRA_: null,

    dde: null,

    title: dict.token('emptyTitle'),

    template: templates.Window,
    binding: {
      title: 'title',
      moveable: 'moveable',
      titleButtons: 'satellite:'
    },
    action: {
      close: function(){
        this.close();
      },
      mousedown: function(){
        this.activate();
      },
      keydown: function(event){
        switch (event.key)
        {
          case event.KEY.ESCAPE:
            if (this.closeOnEscape)
              this.close();
            break;

          case event.KEY.ENTER:
            if (event.sender.tagName != 'TEXTAREA')
              event.die();
            break;
        }
      }
    },

    buttonPanelClass: ButtonPanel.subclass({
      className: namespace + '.ButtonPanel',

      template: templates.ButtonPanel,
      listen: {
        owner: {
          select: function(){
            if (this.firstChild)
              this.firstChild.focus();
          }
        }
      }
    }),

    satellite: {
      titleButtons: {
        existsIf: function(owner){
          return !owner.titleButton || owner.titleButton.close !== false;
        },
        satelliteClass: Node.subclass({
          className: namespace + '.TitleButton',

          template: templates.TitleButton,
          action: {
            close: function(){
              this.owner.close();
            }
          }
        })
      }
    },

    init: function(){
      Node.prototype.init.call(this);

      // make window moveable
      if (this.moveable)
        this.dde = new MoveableElement({
          fixRight: false,
          fixBottom: false,
          handler: {
            context: this,
            callbacks: DD_HANDLER
          }
        });

      // common buttons
      var commonButtons = basis.object.iterate(basis.object.slice(this, ['buttonOk', 'buttonCancel']), function(key, button){
        return {
          name: key == 'buttonOk' ? 'ok' : 'cancel',
          caption: button.caption || button.title || button
        };
      }, this);

      // buttons
      var buttons = arrayFrom(this.buttons).concat(commonButtons).map(function(button){
        return basis.object.complete({
          click: (button.click || this.close).bind(this)
        }, button);
      }, this);

      // build button panel
      if (buttons.length)
        this.buttonPanel = new this.buttonPanelClass({
          owner: this,
          childNodes: buttons
        });

      if (this.autocenter !== false)
      {
        this.autocenter = true;
        this.autocenter_ = true;
      }

      /** @deprecated 1.4 */
      /** @cut */ if (this.closed !== true)
      /** @cut */   basis.dev.warn(namespace + '.Window: `closed` property can\'t be set on create and deprecated (use `visible` instead)');
      /** @cut */ basis.dev.warnPropertyAccess(this, 'closed', true, namespace + '.Window: `closed` property is deprecated, use `visible` instead');

      var visible = this.visible;

      this.visible = false;
      this.closed = true;

      if (visible)
      {
        // it's a hack to propertly add to window stack, when visible on init
        // TODO: find out better solution
        this.element = document.createComment('');

        // no custom code, as `open` event emit is desired behaviour
        this.setVisible(visible);
      }
    },
    setTitle: function(title){
      this.title = title;
      this.updateBind('title');
    },
    templateSync: function(){
      var style;
      if (!this.autocenter && this.element.nodeType == 1)
        style = basis.object.slice(this.element.style, ['left', 'top', 'margin']);

      Node.prototype.templateSync.call(this);

      if (this.element.nodeType == 1)
      {
        if (style)
          cssom.setStyle(this.element, style);

        if (this.dde)
          this.dde.setElement(this.element, this.tmpl.ddtrigger || (this.tmpl.title && this.tmpl.title.parentNode) || this.element);

        if (this.buttonPanel)
          DOM.insert(this.tmpl.content || this.element, this.buttonPanel.element);
      }

      this.realign();
    },
    setZIndex: function(zIndex){
      this.zIndex = zIndex;
      if (this.tmpl && this.element.style)
        this.element.style.zIndex = zIndex;
    },
    realign: function(){
      this.setZIndex(this.zIndex);
      if (this.tmpl && this.autocenter)
      {
        cssom.setStyle(this.element,
          this.element.offsetWidth
            ? {
                left: '50%',
                top: '50%',
                marginLeft: -this.element.offsetWidth / 2 + 'px',
                marginTop: -this.element.offsetHeight / 2 + 'px'
              }
            : {
                left: 0,
                top: 0,
                margin: 0
              }
        );
      }
    },
    activate: function(){
      this.select();
    },
    setVisible: function(visible){
      visible = !!resolveValue(this, this.setVisible, visible, 'visibleRA_');

      if (this.visible !== visible)
      {
        if (visible)
        {
          windowManager.appendChild(this);

          this.visible = true;
          this.closed = false;

          this.realign();

          this.emit_open();
        }
        else
        {
          windowManager.removeChild(this);

          this.visible = false;
          this.closed = true;

          this.autocenter = this.autocenter_;

          this.emit_close();
        }
      }

      if (visible)
        this.realign();
    },
    open: function(){
      this.setVisible(true);
    },
    close: function(){
      this.setVisible(false);
    },
    destroy: function(){
      // NOTE: no resolveValue required, as on this.setVisible(false)
      // resolve adapter will be destroyed
      this.close();

      if (this.dde)
      {
        this.dde.destroy();
        this.dde = null;
      }

      if (this.buttonPanel)
      {
        this.buttonPanel.destroy();
        this.buttonPanel = null;
      }

      Node.prototype.destroy.call(this);
    }
  });

  //
  // Window manager
  //

  var windowManager = new Node({
    template: templates.windowManager,
    blocker: basis.fn.lazyInit(function(){
      return new Blocker();
    }),

    selection: true,
    listen: {
      selection: {
        itemsChanged: function(selection){
          var selected = selection.pick();
          var lastWin = this.lastChild;

          if (selected)
          {
            if (selected.parentNode == this && selected != lastWin)
            {
              // put selected on top
              this.insertBefore(selected);
              this.emit_childNodesModified({});
            }
          }
          else
          {
            if (lastWin)
              selection.add([lastWin]);
          }
        }
      }
    },
    handler: {
      childNodesModified: function(){
        var modalIndex = -1;

        if (this.lastChild)
        {
          for (var i = 0, node; node = this.childNodes[i]; i++)
          {
            node.setZIndex(2001 + i * 2);

            if (node.modal)
              modalIndex = i;
          }

          this.lastChild.select();
        }

        if (modalIndex != -1)
          this.blocker().capture(this.element, 2000 + modalIndex * 2);
        else
          this.blocker().release();
      }
    }
  });

  basis.doc.body.ready(function(body){
    body.insertBefore(windowManager.element, body.firstChild);
    for (var node = windowManager.firstChild; node; node = node.nextSibling)
      node.realign();
  });

  //
  // export names
  //

  module.exports = {
    Window: Window,
    Blocker: Blocker,
    getWindowTopZIndex: function(){
      return windowManager.childNodes.length * 2 + 2001;
    }
  };
<commit_msg>basis.ui.window: take in account ddelement reference to decide what element to drag<commit_after>
 /**
  * @see ./demo/defile/window.html
  * @namespace basis.ui.window
  */

  var namespace = this.path;

  //
  // import names
  //

  var Class = basis.Class;
  var arrayFrom = basis.array.from;
  var resolveValue = require('basis.data').resolveValue;
  var DOM = require('basis.dom');
  var cssom = require('basis.cssom');
  var createEvent = require('basis.event').create;
  var Node = require('basis.ui').Node;
  var ButtonPanel = require('basis.ui.button').ButtonPanel;
  var MoveableElement = require('basis.dragdrop').MoveableElement;

  //
  // definitions
  //

  var dict = require('basis.l10n').dictionary(__filename);

  var templates = require('basis.template').define(namespace, {
    Blocker: resource('./templates/window/Blocker.tmpl'),
    Window: resource('./templates/window/Window.tmpl'),
    TitleButton: resource('./templates/window/TitleButton.tmpl'),
    ButtonPanel: resource('./templates/window/ButtonPanel.tmpl'),
    windowManager: resource('./templates/window/windowManager.tmpl')
  });

  //
  // main part
  //

 /**
  * @class
  */
  var Blocker = Class(Node, {
    className: namespace + '.Blocker',

    template: templates.Blocker,

    captureElement: null,
    capture: function(element, zIndex){
      this.captureElement = DOM.get(element || document.body);
      if (this.captureElement)
      {
        DOM.insert(this.captureElement, this.element);
        this.element.style.zIndex = zIndex || 1000;
      }
    },
    release: function(){
      if (this.captureElement)
      {
        if (this.element.parentNode == this.captureElement)
          DOM.remove(this.element);

        this.captureElement = null;
      }
    },

    destroy: function(){
      this.release();

      Node.prototype.destroy.call(this);
    }
  });

  //
  //  Window
  //

  var DD_HANDLER = {
    start: function(){
      this.autocenter = false;
    }
  };

 /**
  * @class
  */
  var Window = Class(Node, {
    className: namespace + '.Window',

    emit_open: createEvent('open'),
    emit_close: createEvent('close'),

    closeOnEscape: true,
    autocenter: true,
    autocenter_: false,
    modal: false,
    closed: true,
    moveable: true,
    zIndex: 0,

    visible: false,
    visibleRA_: null,

    dde: null,

    title: dict.token('emptyTitle'),

    template: templates.Window,
    binding: {
      title: 'title',
      moveable: 'moveable',
      titleButtons: 'satellite:'
    },
    action: {
      close: function(){
        this.close();
      },
      mousedown: function(){
        this.activate();
      },
      keydown: function(event){
        switch (event.key)
        {
          case event.KEY.ESCAPE:
            if (this.closeOnEscape)
              this.close();
            break;

          case event.KEY.ENTER:
            if (event.sender.tagName != 'TEXTAREA')
              event.die();
            break;
        }
      }
    },

    buttonPanelClass: ButtonPanel.subclass({
      className: namespace + '.ButtonPanel',

      template: templates.ButtonPanel,
      listen: {
        owner: {
          select: function(){
            if (this.firstChild)
              this.firstChild.focus();
          }
        }
      }
    }),

    satellite: {
      titleButtons: {
        existsIf: function(owner){
          return !owner.titleButton || owner.titleButton.close !== false;
        },
        satelliteClass: Node.subclass({
          className: namespace + '.TitleButton',

          template: templates.TitleButton,
          action: {
            close: function(){
              this.owner.close();
            }
          }
        })
      }
    },

    init: function(){
      Node.prototype.init.call(this);

      // make window moveable
      if (this.moveable)
        this.dde = new MoveableElement({
          fixRight: false,
          fixBottom: false,
          handler: {
            context: this,
            callbacks: DD_HANDLER
          }
        });

      // common buttons
      var commonButtons = basis.object.iterate(basis.object.slice(this, ['buttonOk', 'buttonCancel']), function(key, button){
        return {
          name: key == 'buttonOk' ? 'ok' : 'cancel',
          caption: button.caption || button.title || button
        };
      }, this);

      // buttons
      var buttons = arrayFrom(this.buttons).concat(commonButtons).map(function(button){
        return basis.object.complete({
          click: (button.click || this.close).bind(this)
        }, button);
      }, this);

      // build button panel
      if (buttons.length)
        this.buttonPanel = new this.buttonPanelClass({
          owner: this,
          childNodes: buttons
        });

      if (this.autocenter !== false)
      {
        this.autocenter = true;
        this.autocenter_ = true;
      }

      /** @deprecated 1.4 */
      /** @cut */ if (this.closed !== true)
      /** @cut */   basis.dev.warn(namespace + '.Window: `closed` property can\'t be set on create and deprecated (use `visible` instead)');
      /** @cut */ basis.dev.warnPropertyAccess(this, 'closed', true, namespace + '.Window: `closed` property is deprecated, use `visible` instead');

      var visible = this.visible;

      this.visible = false;
      this.closed = true;

      if (visible)
      {
        // it's a hack to propertly add to window stack, when visible on init
        // TODO: find out better solution
        this.element = document.createComment('');

        // no custom code, as `open` event emit is desired behaviour
        this.setVisible(visible);
      }
    },
    setTitle: function(title){
      this.title = title;
      this.updateBind('title');
    },
    templateSync: function(){
      var style;
      if (!this.autocenter && this.element.nodeType == 1)
        style = basis.object.slice(this.element.style, ['left', 'top', 'margin']);

      Node.prototype.templateSync.call(this);

      if (this.element.nodeType == 1)
      {
        if (style)
          cssom.setStyle(this.element, style);

        if (this.dde)
          this.dde.setElement(this.tmpl.ddelement || this.element, this.tmpl.ddtrigger || (this.tmpl.title && this.tmpl.title.parentNode) || this.element);

        if (this.buttonPanel)
          DOM.insert(this.tmpl.content || this.element, this.buttonPanel.element);
      }

      this.realign();
    },
    setZIndex: function(zIndex){
      this.zIndex = zIndex;
      if (this.tmpl && this.element.style)
        this.element.style.zIndex = zIndex;
    },
    realign: function(){
      this.setZIndex(this.zIndex);
      if (this.tmpl && this.autocenter)
      {
        cssom.setStyle(this.element,
          this.element.offsetWidth
            ? {
                left: '50%',
                top: '50%',
                marginLeft: -this.element.offsetWidth / 2 + 'px',
                marginTop: -this.element.offsetHeight / 2 + 'px'
              }
            : {
                left: 0,
                top: 0,
                margin: 0
              }
        );
      }
    },
    activate: function(){
      this.select();
    },
    setVisible: function(visible){
      visible = !!resolveValue(this, this.setVisible, visible, 'visibleRA_');

      if (this.visible !== visible)
      {
        if (visible)
        {
          windowManager.appendChild(this);

          this.visible = true;
          this.closed = false;

          this.realign();

          this.emit_open();
        }
        else
        {
          windowManager.removeChild(this);

          this.visible = false;
          this.closed = true;

          this.autocenter = this.autocenter_;

          this.emit_close();
        }
      }

      if (visible)
        this.realign();
    },
    open: function(){
      this.setVisible(true);
    },
    close: function(){
      this.setVisible(false);
    },
    destroy: function(){
      // NOTE: no resolveValue required, as on this.setVisible(false)
      // resolve adapter will be destroyed
      this.close();

      if (this.dde)
      {
        this.dde.destroy();
        this.dde = null;
      }

      if (this.buttonPanel)
      {
        this.buttonPanel.destroy();
        this.buttonPanel = null;
      }

      Node.prototype.destroy.call(this);
    }
  });

  //
  // Window manager
  //

  var windowManager = new Node({
    template: templates.windowManager,
    blocker: basis.fn.lazyInit(function(){
      return new Blocker();
    }),

    selection: true,
    listen: {
      selection: {
        itemsChanged: function(selection){
          var selected = selection.pick();
          var lastWin = this.lastChild;

          if (selected)
          {
            if (selected.parentNode == this && selected != lastWin)
            {
              // put selected on top
              this.insertBefore(selected);
              this.emit_childNodesModified({});
            }
          }
          else
          {
            if (lastWin)
              selection.add([lastWin]);
          }
        }
      }
    },
    handler: {
      childNodesModified: function(){
        var modalIndex = -1;

        if (this.lastChild)
        {
          for (var i = 0, node; node = this.childNodes[i]; i++)
          {
            node.setZIndex(2001 + i * 2);

            if (node.modal)
              modalIndex = i;
          }

          this.lastChild.select();
        }

        if (modalIndex != -1)
          this.blocker().capture(this.element, 2000 + modalIndex * 2);
        else
          this.blocker().release();
      }
    }
  });

  basis.doc.body.ready(function(body){
    body.insertBefore(windowManager.element, body.firstChild);
    for (var node = windowManager.firstChild; node; node = node.nextSibling)
      node.realign();
  });

  //
  // export names
  //

  module.exports = {
    Window: Window,
    Blocker: Blocker,
    getWindowTopZIndex: function(){
      return windowManager.childNodes.length * 2 + 2001;
    }
  };
<|endoftext|>


<commit_before>/*
* carebot-tracker.js is library that checks if an element is visible on the page
* and reports it to pym.js.
* Check out the readme at README.md for usage.
*/

/*globals define, attachEvent, addEventListener: true */
/* global module */

(function(factory) {
    if (typeof define === 'function' && define.amd) {
        define(factory);
    } else if (typeof module !== 'undefined' && module.exports) {
        module.exports = factory();
    } else {
        window.CarebotTracker = factory.call(this);
    }
})(function() {
    var lib = {};

    /**
     * Timer
     * @param {Function} callback Called every time a new time bucket is reached
     *
     * Interface:
     *
     */
    lib.Timer = function(callback) {
        // From https://github.com/nprapps/elections16/blob/master/www/js/app.js#L298-L335
        var startTime;
        var previousTotalSeconds = 0;
        var previousBucket;
        var alerter;

        function getTimeBucket(seconds) {
            var minutes, timeBucket;
            if (seconds < 60) {
                var tensOfSeconds = Math.floor(seconds / 10) * 10;
                timeBucket = tensOfSeconds.toString() + 's';
            } else if (seconds >=60 && seconds < 300) {
                minutes = Math.floor(seconds / 60);
                timeBucket = minutes.toString() + 'm';
            } else {
                minutes = Math.floor(seconds / 60);
                var fivesOfMinutes = Math.floor(minutes / 5) * 5;
                timeBucket = fivesOfMinutes.toString() + 'm';
            }

            return timeBucket;
        }

        function getSecondsSince(startTime) {
            if (!startTime) {
                return 0;
            }

            var currentTime = new Date();
            var totalTime = Math.abs(currentTime - startTime);
            var seconds = Math.floor(totalTime/1000);
            return seconds;
        }

        function calculateTimeBucket(startTime) {
            var totalTime = getSecondsSince(startTime) + previousTotalSeconds;
            var timeBucket = getTimeBucket(totalTime);

            return {
                bucket: timeBucket,
                seconds: totalTime
            };
        }

        function check() {
            return calculateTimeBucket(startTime);
        }

        function reportBucket() {
            var results = calculateTimeBucket(startTime);
            if (results.bucket !== previousBucket) {
                callback(results);
                previousBucket = results.bucket;
            }
        }

        function start() {
            startTime = new Date();

            if (callback) {
                alerter = setInterval(reportBucket, 10000);
            }
        }

        function pause() {
            previousTotalSeconds = getSecondsSince(startTime) + previousTotalSeconds;
            clearInterval(alerter);
            startTime = undefined;
        }

        return {
            start: start,
            pause: pause,
            check: check
        };
    }.bind(this);

    /**
     * Tracks how long an element is visible.
     *
     * @class Parent
     * @param {String} id The id of the element the tracker will watch.
     * @param {Function} callback Will be called on every new time bucket.
     * @param {Object} config Configuration to override the default settings.
     */
    lib.VisibilityTracker = function(id, callback, config) {
        var WAIT_TO_ENSURE_SCROLLING_IS_DONE = 50; //

        var el = document.getElementById(id);
        var isVisible = false;
        var timeout;

        var timer = new lib.Timer(callback);

        // Ensure a config object
        config = (config || {});

        function isElementInViewport(el) {
            // Adapted from http://stackoverflow.com/a/15203639/117014
            //
            // Returns true only if the WHOLE element is in the viewport
            var rect     = el.getBoundingClientRect();
            var vWidth   = window.innerWidth || document.documentElement.clientWidth;
            var vHeight  = window.innerHeight || document.documentElement.clientHeight;

            // Core tests: are all sides of the rectangle in the viewport?
            var leftIsOffScreen = rect.left < 0;
            var rightIsOffScreen = rect.right > vWidth;
            var bottomIsOffScreen = rect.bottom > vHeight;
            var topIsOffScreen = rect.top < 0;

            // These are not necessary, but kept if we want to track partial visibility.
            /*
            var leftSideIsToRightOfWindow = rect.left > vWidth;
            var rightSideIsToLeftOfWindow = rect.right < 0;
            var topIsBelowVisibleWindow = rect.top > vHeight;
            var botomIsAboveVisibleWindow = rect.bottom < 0;
            */

            if (leftIsOffScreen  ||
                rightIsOffScreen ||
                topIsOffScreen   ||
                bottomIsOffScreen) {
                return false;
            }

            return true;
        }

        function checkIfVisible () {
            var newVisibility = isElementInViewport(el);

            if (isVisible && !newVisibility) {
                timer.pause();
            }

            if (!isVisible && newVisibility) {
                timer.start();
            }

            isVisible = newVisibility;
            return newVisibility;
        }

        function handler() {
            // Only register a new event every 1/10 of a second
            // That way we don't record an absurd number of events
            if (timeout) {
                window.clearTimeout(timeout);
            }
            timeout = window.setTimeout(checkIfVisible, WAIT_TO_ENSURE_SCROLLING_IS_DONE);
        }

        // Listen to different window movement events
        if (window.addEventListener) {
            addEventListener('DOMContentLoaded', handler, false);
            addEventListener('load', handler, false);
            addEventListener('scroll', handler, false);
            addEventListener('resize', handler, false);
        } else if (window.attachEvent)  {
            attachEvent('onDOMContentLoaded', handler); // IE9+ :(
            attachEvent('onload', handler);
            attachEvent('onscroll', handler);
            attachEvent('onresize', handler);
        }
    };

    return lib;
});
<commit_msg>Check if visible on startup<commit_after>/*
* carebot-tracker.js is library that checks if an element is visible on the page
* and reports it to pym.js.
* Check out the readme at README.md for usage.
*/

/*globals define, attachEvent, addEventListener: true */
/* global module */

(function(factory) {
    if (typeof define === 'function' && define.amd) {
        define(factory);
    } else if (typeof module !== 'undefined' && module.exports) {
        module.exports = factory();
    } else {
        window.CarebotTracker = factory.call(this);
    }
})(function() {
    var lib = {};

    /**
     * Timer
     * @param {Function} callback Called every time a new time bucket is reached
     *
     * Interface:
     *
     */
    lib.Timer = function(callback) {
        // From https://github.com/nprapps/elections16/blob/master/www/js/app.js#L298-L335
        var startTime;
        var previousTotalSeconds = 0;
        var previousBucket;
        var alerter;

        function getTimeBucket(seconds) {
            var minutes, timeBucket;
            if (seconds < 60) {
                var tensOfSeconds = Math.floor(seconds / 10) * 10;
                timeBucket = tensOfSeconds.toString() + 's';
            } else if (seconds >=60 && seconds < 300) {
                minutes = Math.floor(seconds / 60);
                timeBucket = minutes.toString() + 'm';
            } else {
                minutes = Math.floor(seconds / 60);
                var fivesOfMinutes = Math.floor(minutes / 5) * 5;
                timeBucket = fivesOfMinutes.toString() + 'm';
            }

            return timeBucket;
        }

        function getSecondsSince(startTime) {
            if (!startTime) {
                return 0;
            }

            var currentTime = new Date();
            var totalTime = Math.abs(currentTime - startTime);
            var seconds = Math.floor(totalTime/1000);
            return seconds;
        }

        function calculateTimeBucket(startTime) {
            var totalTime = getSecondsSince(startTime) + previousTotalSeconds;
            var timeBucket = getTimeBucket(totalTime);

            return {
                bucket: timeBucket,
                seconds: totalTime
            };
        }

        function check() {
            return calculateTimeBucket(startTime);
        }

        function reportBucket() {
            var results = calculateTimeBucket(startTime);
            if (results.bucket !== previousBucket) {
                callback(results);
                previousBucket = results.bucket;
            }
        }

        function start() {
            startTime = new Date();

            if (callback) {
                alerter = setInterval(reportBucket, 10000);
            }
        }

        function pause() {
            previousTotalSeconds = getSecondsSince(startTime) + previousTotalSeconds;
            clearInterval(alerter);
            startTime = undefined;
        }

        return {
            start: start,
            pause: pause,
            check: check
        };
    }.bind(this);

    /**
     * Tracks how long an element is visible.
     *
     * @class Parent
     * @param {String} id The id of the element the tracker will watch.
     * @param {Function} callback Will be called on every new time bucket.
     * @param {Object} config Configuration to override the default settings.
     */
    lib.VisibilityTracker = function(id, callback, config) {
        var WAIT_TO_ENSURE_SCROLLING_IS_DONE = 50; //

        var el = document.getElementById(id);
        var isVisible = false;
        var timeout;

        var timer = new lib.Timer(callback);

        // Ensure a config object
        config = (config || {});

        function isElementInViewport(el) {
            // Adapted from http://stackoverflow.com/a/15203639/117014
            //
            // Returns true only if the WHOLE element is in the viewport
            var rect     = el.getBoundingClientRect();
            var vWidth   = window.innerWidth || document.documentElement.clientWidth;
            var vHeight  = window.innerHeight || document.documentElement.clientHeight;

            // Core tests: are all sides of the rectangle in the viewport?
            var leftIsOffScreen = rect.left < 0;
            var rightIsOffScreen = rect.right > vWidth;
            var bottomIsOffScreen = rect.bottom > vHeight;
            var topIsOffScreen = rect.top < 0;

            // These are not necessary, but kept if we want to track partial visibility.
            /*
            var leftSideIsToRightOfWindow = rect.left > vWidth;
            var rightSideIsToLeftOfWindow = rect.right < 0;
            var topIsBelowVisibleWindow = rect.top > vHeight;
            var botomIsAboveVisibleWindow = rect.bottom < 0;
            */

            if (leftIsOffScreen  ||
                rightIsOffScreen ||
                topIsOffScreen   ||
                bottomIsOffScreen) {
                return false;
            }

            return true;
        }

        function checkIfVisible () {
            var newVisibility = isElementInViewport(el);

            if (isVisible && !newVisibility) {
                timer.pause();
            }

            if (!isVisible && newVisibility) {
                timer.start();
            }

            isVisible = newVisibility;
            return newVisibility;
        }

        function handler() {
            // Only register a new event every 1/10 of a second
            // That way we don't record an absurd number of events
            if (timeout) {
                window.clearTimeout(timeout);
            }
            timeout = window.setTimeout(checkIfVisible, WAIT_TO_ENSURE_SCROLLING_IS_DONE);
        }

        // Listen to different window movement events
        if (window.addEventListener) {
            addEventListener('DOMContentLoaded', handler, false);
            addEventListener('load', handler, false);
            addEventListener('scroll', handler, false);
            addEventListener('resize', handler, false);
        } else if (window.attachEvent)  {
            attachEvent('onDOMContentLoaded', handler); // IE9+ :(
            attachEvent('onload', handler);
            attachEvent('onscroll', handler);
            attachEvent('onresize', handler);
        }

        checkIfVisible();
    };

    return lib;
});
<|endoftext|>


<commit_before>import {
  PropertyColor,
  PropertyEnum,
  PropertyString,
  PropertyNumber,
  PropertyLengthMeasure,
  PropertyToggle,
  PropertyCheckbox,
  PropertyHidden,
  PropertyReadOnly
} from './properties/export';

import {UNIT_CENTIMETER} from '../constants';

export default class Catalog {

  constructor(unit = UNIT_CENTIMETER) {
    this.elements = {};
    this.categories = {root: {name: 'root', label: '/', elements: [], categories: []}};
    this.propertyTypes = {};
    this.unit = unit;

    this.registerMultiplePropertyType([
      ['color', PropertyColor, PropertyColor],
      ['enum', PropertyEnum, PropertyEnum],
      ['string', PropertyString, PropertyString],
      ['number', PropertyNumber, PropertyNumber],
      ['length-measure', PropertyLengthMeasure, PropertyLengthMeasure],
      ['toggle', PropertyToggle, PropertyToggle],
      ['checkbox', PropertyCheckbox, PropertyCheckbox],
      ['hidden', PropertyHidden, PropertyHidden],
      ['read-only', PropertyReadOnly, PropertyReadOnly]
    ]);
  }

  getElement(type) {
    if (this.hasElement(type)) {
      return this.elements[type];
    }
    throw new Error(`Element ${type} does not exist in catalog`);
  }

  getCategory(categoryName) {
    if (this.hasCategory(categoryName)) {
      return this.categories[categoryName];
    }
    throw new Error(`Category ${categoryName} does not exist in catalog`);
  }

  getPropertyType(type) {
    if (this.propertyTypes.hasOwnProperty(type)) {
      return this.propertyTypes[type];
    }
    throw new Error(`Element ${type} does not exist in catalog`);
  }

  registerElement(json) {
    json.properties = json.properties || {};
    if (this.validateElement(json)) {
      this.elements[json.name] = json;
      this.categories.root.elements.push(this.elements[json.name]);
    }
  }

  registerMultipleElements(elementArray) {
    elementArray.forEach(el => this.registerElement(el));
  }

  registerPropertyType(type, Viewer, Editor) {
    this.propertyTypes[type] = {type, Viewer, Editor};
  }

  registerMultiplePropertyType(propertyTypeArray) {
    propertyTypeArray.forEach(el => this.registerPropertyType(...el));
  }

  validateElement(json) {
    if (!json.hasOwnProperty('name')) throw new Error('Element not valid');

    let name = json.name;
    if (!json.hasOwnProperty('prototype')) throw new Error(`Element ${name} doesn't have prototype`);

    if (!json.hasOwnProperty('info')) throw new Error(`Element ${name} doesn't have info`);
    if (!json.info.hasOwnProperty('tag')) throw new Error(`Element ${name} doesn't have tag`);
    if (!json.info.hasOwnProperty('group')) throw new Error(`Element ${name} doesn't have group`);
    if (!json.info.hasOwnProperty('description')) throw new Error(`Element ${name} doesn't have description`);
    if (!json.info.hasOwnProperty('image')) throw new Error(`Element ${name} doesn't have image`);

    if (!json.hasOwnProperty('render2D')) throw new Error(`Element ${name} doesn't have render2D handler`);
    if (!json.hasOwnProperty('render3D')) throw new Error(`Element ${name} doesn't have render3D handler`);
    if (!json.hasOwnProperty('properties')) throw new Error(`Element ${name} doesn't have properties`);

    for (let propertyName in json.properties) {
      let propertyConfigs = json.properties[propertyName];
      if (!propertyConfigs.hasOwnProperty('type')) throw new Error(`Element ${name}, Property ${propertyName} doesn't have type`);
      if (!propertyConfigs.hasOwnProperty('defaultValue')) throw new Error(`Element ${name}, Property ${propertyName} doesn't have defaultValue`);
    }

    return true;
  }

  hasElement(type) {
    return this.elements.hasOwnProperty(type);
  }

  /** @description Register a new category
   *  @param {string} name Name of category
   *  @param {string} label Label of category
   *  @param {array} [childs] Category's childs
   *  @return {object} Registered category
  */
  registerCategory(name, label, childs) {
    if (this.validateCategory(name, label)) {
      this.categories[name] = {name, label, categories: [], elements: []};
      this.categories.root.categories.push(this.categories[name]);

      if(childs && childs.length) {
        childs.forEach( el => this.addToCategory( name, el ) );
      }

      return this.categories[name];
    }
    return null;
  }

  addToCategory(name, child) {
    if (this.hasElement(child.name)) {
      this.categories[name].elements.push(child);
      this.categories.root.elements.splice(this.categories.root.elements.indexOf(child), 1);
    } else if (this.hasCategory(child.name)) {
      this.categories[name].categories.push(child);
      this.categories.root.categories.splice(this.categories.root.categories.indexOf(child), 1);
    } else {
      throw new Error(`child ${child} is either category nor element`);
    }
  }

  categoryHasElement(categoryName, elementName) {
    return this.hasCategory(categoryName) && this.categories[categoryName].elements.some(el => el.name === elementName);
  }

  validateCategory(name, label) {
    if (!name) {
      throw new Error('Category has undefined name');
    }
    if (name === '') {
      throw new Error('Category has empty name');
    }
    if (this.hasCategory(name)) {
      throw new Error('Category has already been registered');
    }

    return true;
  }

  hasCategory(categoryName) {
    return this.categories.hasOwnProperty(categoryName);
  }

}
<commit_msg>added catalog's functions documentation<commit_after>import {
  PropertyColor,
  PropertyEnum,
  PropertyString,
  PropertyNumber,
  PropertyLengthMeasure,
  PropertyToggle,
  PropertyCheckbox,
  PropertyHidden,
  PropertyReadOnly
} from './properties/export';

import {UNIT_CENTIMETER} from '../constants';

export default class Catalog {

  constructor(unit = UNIT_CENTIMETER) {
    this.elements = {};
    this.categories = {root: {name: 'root', label: '/', elements: [], categories: []}};
    this.propertyTypes = {};
    this.unit = unit;

    this.registerMultiplePropertyType([
      ['color', PropertyColor, PropertyColor],
      ['enum', PropertyEnum, PropertyEnum],
      ['string', PropertyString, PropertyString],
      ['number', PropertyNumber, PropertyNumber],
      ['length-measure', PropertyLengthMeasure, PropertyLengthMeasure],
      ['toggle', PropertyToggle, PropertyToggle],
      ['checkbox', PropertyCheckbox, PropertyCheckbox],
      ['hidden', PropertyHidden, PropertyHidden],
      ['read-only', PropertyReadOnly, PropertyReadOnly]
    ]);
  }

  /** @description Get catalog's element
   *  @param {string} type Element's type
   *  @return {?object} Element
  */
  getElement(type) {
    if (this.hasElement(type)) {
      return this.elements[type];
    }
    throw new Error(`Element ${type} does not exist in catalog`);
  }

  /** @description Get catalog category
   *  @param {string} categoryName Name of category
   *  @return {object} Category
  */
  getCategory(categoryName) {
    if (this.hasCategory(categoryName)) {
      return this.categories[categoryName];
    }
    throw new Error(`Category ${categoryName} does not exist in catalog`);
  }

  /** @description Return type of a specfied property
   *  @param {string} type Property type
   *  @return {?object} Property
  */
  getPropertyType(type) {
    if (this.propertyTypes.hasOwnProperty(type)) {
      return this.propertyTypes[type];
    }
    throw new Error(`Element ${type} does not exist in catalog`);
  }

  /** @description Register a new element
   *  @param {object} json Element structure
   *  @return {void}
  */
  registerElement(json) {
    json.properties = json.properties || {};
    if (this.validateElement(json)) {
      this.elements[json.name] = json;
      this.categories.root.elements.push(this.elements[json.name]);
    }
  }

  /** @description Register multiple elements
   *  @param {array} [elementArray] Array of elements
   *  @return {void}
  */
  registerMultipleElements(elementArray) {
    elementArray.forEach(el => this.registerElement(el));
  }

  /** @description Register a new property
   *  @param {string} type Type of property
   *  @param {object} Viewer Property viewer component
   *  @param {object} Editor Property editor component
   *  @return {void}
  */
  registerPropertyType(type, Viewer, Editor) {
    this.propertyTypes[type] = {type, Viewer, Editor};
  }

  /** @description Register multiple property
   *  @param {array} propertyTypeArray Array of properties
   *  @return {void}
  */
  registerMultiplePropertyType(propertyTypeArray) {
    propertyTypeArray.forEach(el => this.registerPropertyType(...el));
  }

  /** @description Validate an element
   *  @param {object} json Element's structure
   *  @return {?boolean}
  */
  validateElement(json) {
    if (!json.hasOwnProperty('name')) throw new Error('Element not valid');

    let name = json.name;
    if (!json.hasOwnProperty('prototype')) throw new Error(`Element ${name} doesn't have prototype`);

    if (!json.hasOwnProperty('info')) throw new Error(`Element ${name} doesn't have info`);
    if (!json.info.hasOwnProperty('tag')) throw new Error(`Element ${name} doesn't have tag`);
    if (!json.info.hasOwnProperty('group')) throw new Error(`Element ${name} doesn't have group`);
    if (!json.info.hasOwnProperty('description')) throw new Error(`Element ${name} doesn't have description`);
    if (!json.info.hasOwnProperty('image')) throw new Error(`Element ${name} doesn't have image`);

    if (!json.hasOwnProperty('render2D')) throw new Error(`Element ${name} doesn't have render2D handler`);
    if (!json.hasOwnProperty('render3D')) throw new Error(`Element ${name} doesn't have render3D handler`);
    if (!json.hasOwnProperty('properties')) throw new Error(`Element ${name} doesn't have properties`);

    for (let propertyName in json.properties) {
      let propertyConfigs = json.properties[propertyName];
      if (!propertyConfigs.hasOwnProperty('type')) throw new Error(`Element ${name}, Property ${propertyName} doesn't have type`);
      if (!propertyConfigs.hasOwnProperty('defaultValue')) throw new Error(`Element ${name}, Property ${propertyName} doesn't have defaultValue`);
    }

    return true;
  }

  /** @description Check if catalog has element
   *  @param {string} type Element's type
   *  @return {boolean}
  */
  hasElement(type) {
    return this.elements.hasOwnProperty(type);
  }

  /** @description Register a new category
   *  @param {string} name Name of category
   *  @param {string} label Label of category
   *  @param {array} [childs] Category's childs
   *  @return {?object} Registered category
  */
  registerCategory(name, label, childs) {
    if (this.validateCategory(name, label)) {
      this.categories[name] = {name, label, categories: [], elements: []};
      this.categories.root.categories.push(this.categories[name]);

      if(childs && childs.length) {
        childs.forEach( el => this.addToCategory( name, el ) );
      }

      return this.categories[name];
    }
    return null;
  }

  /** @description Add an element to the specified category
   *  @param {string} name Name of category
   *  @param {object} child Element's structure
   *  @return {?void}
  */
  addToCategory(name, child) {
    if (this.hasElement(child.name)) {
      this.categories[name].elements.push(child);
      this.categories.root.elements.splice(this.categories.root.elements.indexOf(child), 1);
    } else if (this.hasCategory(child.name)) {
      this.categories[name].categories.push(child);
      this.categories.root.categories.splice(this.categories.root.categories.indexOf(child), 1);
    } else {
      throw new Error(`child ${child} is either category nor element`);
    }
  }

  /** @description Check if category contain element
   *  @param {string} categoryName Name of category
   *  @param {string} elementName Name of element
   *  @return {boolean}
  */
  categoryHasElement(categoryName, elementName) {
    return this.hasCategory(categoryName) && this.categories[categoryName].elements.some(el => el.name === elementName);
  }

  /** @description Validate a category
   *  @param {string} name Name of category
   *  @param {string} label Label of category
   *  @return {?boolean}
  */
  validateCategory(name, label) {
    if (!name) {
      throw new Error('Category has undefined name');
    }
    if (name === '') {
      throw new Error('Category has empty name');
    }
    if (this.hasCategory(name)) {
      throw new Error('Category has already been registered');
    }

    return true;
  }

  /** @description Verify if catalog already contain a category with specified name
   *  @param {string} categoryName Name of category
   *  @return {boolean}
  */
  hasCategory(categoryName) {
    return this.categories.hasOwnProperty(categoryName);
  }

}
<|endoftext|>


<commit_before>'use strict';
const isInteger                                       = require('is-integer');
const assign                                          = require('../util/assign');
const { ITERATOR }                                    = require('../protocols');
const toIterator                                      = require('../methods/to-iterator');
const {toArray, filter, map, reduce, cat, uniq, take} = require('../methods');

const baseProto = {
  [ITERATOR]() {
    return this;
  },
  toArray() {
    return toArray(this);
  },
  reduce(reducerFn, init) {
    return reduce(reducerFn, init, this);
  },
  filter(predicate) {
    return filter(predicate, this);
  },
  map(mapper) {
    return map(mapper, this);
  },
  concat(...args) {
    return cat([this].concat(args));
  },
  take(n) {
    return take(n, this);
  },
  uniq() {
    return uniq(this);
  },
  cat() {
    return cat(this);
  }
};

function IteratorIterator(iterator) {
  this._iterIterator = toIterator(iterator);
  this._done = null;
  this._iter = null;
}
assign(IteratorIterator.prototype, baseProto);

IteratorIterator.prototype.next = function() {
  if (this._done) return { value: undefined, done: true };

  const step = this._iter && this._iter.next();
  if (!step || step.done) {
    const iterStep = this._iterIterator.next();
    this._done = iterStep.done;
    this._iter = iterStep.value;
    return this.next();
  }
  return step;
};

function ObjectIterator(obj) {
  this._obj = obj;
  this._keys = Object.keys(obj);
  this._step = -1;
}
assign(ObjectIterator.prototype, baseProto);

ObjectIterator.prototype.next = function() {
  const key = this._keys[++this._step];
  if (this._step >= this._keys.length) return { value: undefined, done: true };
  return { value: [key, this._obj[key]], done: false };
};

function ArrayIterator(arr) {
  this._arr = arr;
  this._i = -1;
}
assign(ArrayIterator.prototype, baseProto);

ArrayIterator.prototype.next = function() {
  return { value: this._arr[++this._i], done: this._i >= this._arr.length };
};

function WrappingIterator(iterator) {
  this._iter = toIterator(iterator);
}
assign(WrappingIterator.prototype, baseProto);

WrappingIterator.prototype.next = function() {
  return this._iter.next();
};

function TakeIterator(length, iterator) {
  if (!isInteger(length) || length < 0) throw new TypeError(`length must be integer >= 0, got ${length}`);
  WrappingIterator.call(this, iterator);
  this._length = length;
  this._i = 0;
}
assign(TakeIterator.prototype, WrappingIterator.prototype);

TakeIterator.prototype.next = function() {
  const nextVal = this._iter.next();
  if (nextVal.done || ++this._i > this._length) return { done: true, value: undefined };
  return { value: nextVal.value, done: false };
};

function SubIterator(base, iters, index) {
  this._base = base;
  this._iters = iters;
  this._index = index;
  this._queue = [];
  // Object.freeze(this);
}

assign(SubIterator.prototype, baseProto);

SubIterator.prototype.next = function() {
  if (!this._queue.length) return this._queue.shift();
  const nextVal = this._base.next();
  const iters = this._iters;
  for (let i = 0; i < iters.length; i++) {
    if (i !== this._index) iters[i]._queue.push(nextVal);
  }
  return nextVal;
};

function MappingIterator(mapFn, iterable, thisArg) {
  this._mapper = (thisArg || thisArg === null) ? mapFn.bind(thisArg) : mapFn;
  this._iter = toIterator(iterable);
}

MappingIterator.prototype.next = function() {
  const nextVal = this._iter.next();
  if (nextVal.done) return nextVal;
  return { value: this._mapper(nextVal.value), done: false };
};

function FilterIterator(predicate, iterable) {
  this._predicate = predicate;
  this._iter = toIterator(iterable);
}

FilterIterator.prototype.next = function() {
  let nextVal = this._iter.next();
  while(!nextVal.done && !this._predicate(nextVal.value)) {
    nextVal = this._iter.next();
  }
  return nextVal;
};

const abs = Math.abs;

function RangeIterator(start, end, step = 1) {
  if (!isInteger(start)) throw new Error(`Parameter 'start' must be an integer, got ${start}`);
  if (!isInteger(end)) throw new Error(`Parameter 'end' must be an integer, got ${end}`);
  this.start = start;
  this.end = end;
  this._distance = end - start;
  this._delta = 0;
  this._step = (end >= start) ? step : -step;
}

RangeIterator.prototype.next = function() {
  const d = this._delta += this.delta;
  return { done: abs(d) > abs(this._distance), value: this.start + d };
};

module.exports = {
  ArrayIterator,
  ObjectIterator,
  WrappingIterator,
  IteratorIterator,
  TakeIterator,
  MappingIterator,
  FilterIterator,
  RangeIterator,
  SubIterator
};

// Object.keys(ctors).forEach(function(key) {
//   assign(ctors[key].prototype)
// });
<commit_msg>Update iterators.js<commit_after>'use strict';
const isInteger                                       = require('is-integer');
const assign                                          = require('../util/assign');
const { ITERATOR }                                    = require('../protocols');
const {toArray, filter, map, reduce, cat, uniq, take, toIterator} = require('../methods');

const baseProto = {
  [ITERATOR]() {
    return this;
  },
  toArray() {
    return toArray(this);
  },
  reduce(reducerFn, init) {
    return reduce(reducerFn, init, this);
  },
  filter(predicate) {
    return filter(predicate, this);
  },
  map(mapper) {
    return map(mapper, this);
  },
  concat(...args) {
    return cat([this].concat(args));
  },
  take(n) {
    return take(n, this);
  },
  uniq() {
    return uniq(this);
  },
  cat() {
    return cat(this);
  }
};

function IteratorIterator(iterator) {
  this._iterIterator = toIterator(iterator);
  this._done = null;
  this._iter = null;
}
assign(IteratorIterator.prototype, baseProto);

IteratorIterator.prototype.next = function() {
  if (this._done) return { value: undefined, done: true };

  const step = this._iter && this._iter.next();
  if (!step || step.done) {
    const iterStep = this._iterIterator.next();
    this._done = iterStep.done;
    this._iter = iterStep.value;
    return this.next();
  }
  return step;
};

function ObjectIterator(obj) {
  this._obj = obj;
  this._keys = Object.keys(obj);
  this._step = -1;
}
assign(ObjectIterator.prototype, baseProto);

ObjectIterator.prototype.next = function() {
  const key = this._keys[++this._step];
  if (this._step >= this._keys.length) return { value: undefined, done: true };
  return { value: [key, this._obj[key]], done: false };
};

function ArrayIterator(arr) {
  this._arr = arr;
  this._i = -1;
}
assign(ArrayIterator.prototype, baseProto);

ArrayIterator.prototype.next = function() {
  return { value: this._arr[++this._i], done: this._i >= this._arr.length };
};

function WrappingIterator(iterator) {
  this._iter = toIterator(iterator);
}
assign(WrappingIterator.prototype, baseProto);

WrappingIterator.prototype.next = function() {
  return this._iter.next();
};

function TakeIterator(length, iterator) {
  if (!isInteger(length) || length < 0) throw new TypeError(`length must be integer >= 0, got ${length}`);
  WrappingIterator.call(this, iterator);
  this._length = length;
  this._i = 0;
}
assign(TakeIterator.prototype, WrappingIterator.prototype);

TakeIterator.prototype.next = function() {
  const nextVal = this._iter.next();
  if (nextVal.done || ++this._i > this._length) return { done: true, value: undefined };
  return { value: nextVal.value, done: false };
};

function SubIterator(base, iters, index) {
  this._base = base;
  this._iters = iters;
  this._index = index;
  this._queue = [];
  // Object.freeze(this);
}

assign(SubIterator.prototype, baseProto);

SubIterator.prototype.next = function() {
  if (!this._queue.length) return this._queue.shift();
  const nextVal = this._base.next();
  const iters = this._iters;
  for (let i = 0; i < iters.length; i++) {
    if (i !== this._index) iters[i]._queue.push(nextVal);
  }
  return nextVal;
};

function MappingIterator(mapFn, iterable, thisArg) {
  this._mapper = (thisArg || thisArg === null) ? mapFn.bind(thisArg) : mapFn;
  this._iter = toIterator(iterable);
}

MappingIterator.prototype.next = function() {
  const nextVal = this._iter.next();
  if (nextVal.done) return nextVal;
  return { value: this._mapper(nextVal.value), done: false };
};

function FilterIterator(predicate, iterable) {
  this._predicate = predicate;
  this._iter = toIterator(iterable);
}

FilterIterator.prototype.next = function() {
  let nextVal = this._iter.next();
  while(!nextVal.done && !this._predicate(nextVal.value)) {
    nextVal = this._iter.next();
  }
  return nextVal;
};

const abs = Math.abs;

function RangeIterator(start, end, step = 1) {
  if (!isInteger(start)) throw new Error(`Parameter 'start' must be an integer, got ${start}`);
  if (!isInteger(end)) throw new Error(`Parameter 'end' must be an integer, got ${end}`);
  this.start = start;
  this.end = end;
  this._distance = end - start;
  this._delta = 0;
  this._step = (end >= start) ? step : -step;
}

RangeIterator.prototype.next = function() {
  const d = this._delta += this.delta;
  return { done: abs(d) > abs(this._distance), value: this.start + d };
};

module.exports = {
  ArrayIterator,
  ObjectIterator,
  WrappingIterator,
  IteratorIterator,
  TakeIterator,
  MappingIterator,
  FilterIterator,
  RangeIterator,
  SubIterator
};

// Object.keys(ctors).forEach(function(key) {
//   assign(ctors[key].prototype)
// });
<|endoftext|>


<commit_before>const FeedSelector = require('../structs/FeedSelector.js')
const MenuUtils = require('../structs/MenuUtils.js')
const Translator = require('../structs/Translator.js')
const Profile = require('../structs/db/Profile.js')
const Feed = require('../structs/db/Feed.js')
const getConfig = require('../config.js').get
const createLogger = require('../util/logger/create.js')

module.exports = async (message, command) => {
  const profile = await Profile.get(message.guild.id)
  const guildLocale = profile ? profile.locale : undefined
  const translate = Translator.createLocaleTranslator(guildLocale)
  const feeds = await Feed.getManyBy('guild', message.guild.id)
  const feedSelector = new FeedSelector(message, null, { command, locale: guildLocale, multiSelect: true }, feeds)
  const data = await new MenuUtils.MenuSeries(message, [feedSelector], { locale: guildLocale }).start()
  if (!data) return
  const { selectedFeeds } = data
  const removing = await message.channel.send(translate('commands.remove.removing'))
  const errors = []
  const log = createLogger(message.guild.shard.id)
  let removed = translate('commands.remove.success') + '\n```\n'
  for (const feed of selectedFeeds) {
    const link = feed.url
    try {
      await feed.delete()
      removed += `\n${link}`
      log.info({
        guild: message.guild
      }, `Removed feed ${link}`)
    } catch (err) {
      log.error({
        error: err,
        guild: message.guild
      }, `Failed to remove feed ${link}`)
      errors.push(err)
    }
  }
  const config = getConfig()
  const prefix = profile && profile.prefix ? profile.prefix : config.bot.prefix
  if (errors.length > 0) {
    await removing.edit(translate('commands.remove.internalError'))
  } else {
    await removing.edit(`${removed}\`\`\`\n\n${translate('generics.backupReminder', { prefix })}`)
  }
}
<commit_msg>Remove MenuUtils from remove command<commit_after>const { PromptNode } = require('discord.js-prompts')
const movePrompts = require('./prompts/move/index.js')
const runWithFeedGuild = require('./prompts/runner/runWithFeedsProfile.js')
const Translator = require('../structs/Translator.js')
const Profile = require('../structs/db/Profile.js')
const getConfig = require('../config.js').get
const createLogger = require('../util/logger/create.js')

module.exports = async (message, command) => {
  const profile = await Profile.get(message.guild.id)
  const translate = Translator.createProfileTranslator(profile)
  const selectSourceFeedsNode = new PromptNode(movePrompts.selectSourceFeeds.prompt)
  const data = await runWithFeedGuild(selectSourceFeedsNode, message)
  const { sourceFeeds } = data
  if (!sourceFeeds) {
    return
  }
  const removing = await message.channel.send(translate('commands.remove.removing'))
  const errors = []
  const log = createLogger(message.guild.shard.id)
  let removed = translate('commands.remove.success') + '\n```\n'
  for (const feed of sourceFeeds) {
    const link = feed.url
    try {
      await feed.delete()
      removed += `\n${link}`
      log.info({
        guild: message.guild
      }, `Removed feed ${link}`)
    } catch (err) {
      log.error({
        error: err,
        guild: message.guild
      }, `Failed to remove feed ${link}`)
      errors.push(err)
    }
  }
  const config = getConfig()
  const prefix = profile && profile.prefix ? profile.prefix : config.bot.prefix
  if (errors.length > 0) {
    await removing.edit(translate('commands.remove.internalError'))
  } else {
    await removing.edit(`${removed}\`\`\`\n\n${translate('generics.backupReminder', { prefix })}`)
  }
}
<|endoftext|>