<commit_before>/**
 * jQuery Yii GridView plugin file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright Copyright © 2008-2010 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 * @version $Id$
 */

(function($) {
	var gridSettings = {};

	var methods = {
		/**
		 * yiiGridView set function.
		 * @param options map settings for the grid view. Available options are as follows:
		 * - ajaxUpdate: array, IDs of the containers whose content may be updated by ajax response
		 * - ajaxVar: string, the name of the GET variable indicating the ID of the element triggering the AJAX request
		 * - pagerClass: string, the CSS class for the pager container
		 * - tableClass: string, the CSS class for the table
		 * - selectableRows: integer, the number of rows that can be selected
		 * - updateSelector: string, the selector for choosing which elements can trigger ajax requests
		 * - beforeAjaxUpdate: function, the function to be called before ajax request is sent
		 * - afterAjaxUpdate: function, the function to be called after ajax response is received
		 * - ajaxUpdateError: function, the function to be called if an ajax error occurs
		 * - selectionChanged: function, the function to be called after the row selection is changed
		 * @return object the jQuery object
		 */
		init: function(options) {
			var settings = $.extend( {
					ajaxUpdate: [],
					ajaxVar: 'ajax',
					pagerClass: 'pager',
					loadingClass: 'loading',
					filterClass: 'filters',
					tableClass: 'items',
					selectableRows: 1
					// updateSelector: '#id .pager a, '#id .grid thead th a',
					// beforeAjaxUpdate: function(id) {},
					// afterAjaxUpdate: function(id, data) {},
					// selectionChanged: function(id) {},
					// url: 'ajax request URL'
				}, options || {});

			return this.each(function(){
				var $grid = $(this),
					id = $grid.attr('id');

				settings.tableClass=settings.tableClass.replace(/\s+/g,'.');
				if(settings.updateSelector === undefined)
					settings.updateSelector = '#'+id+' .'+settings.pagerClass.replace(/\s+/g,'.')+' a, #'+id+' .'+settings.tableClass+' thead th a';

				gridSettings[id] = settings;

				if(settings.ajaxUpdate.length > 0) {
					$(document).on('click', settings.updateSelector, function(){
						$('#'+id).yiiGridView('update', {url: $(this).attr('href')});
						return false;
					});
				}

				var inputSelector = '#'+id+' .'+settings.filterClass+' input, '+'#'+id+' .'+settings.filterClass+' select';
				$(document).on('change', inputSelector, function(){
					var data = $(inputSelector).serialize();
					if(settings.pageVar !== undefined)
						data += '&' + settings.pageVar + '=1';
					$('#'+id).yiiGridView('update', {data: data});
				});

				if(settings.selectableRows > 0) {
					$grid.yiiGridView('selectCheckedRows');
					$(document).on('click','#'+id+' .'+settings.tableClass+' > tbody > tr',function(e){
						var $target = $(e.target);
						if($target.closest('td').hasClass('button-column') ||
							(e.target.type === 'checkbox' && !$target.hasClass('select-on-check')))
							return;

						var $currentGrid = $('#'+id),
							$row = $(this),
							isRowSelected = $row.toggleClass('selected').hasClass('selected'),
							$checks = $('input.select-on-check',$currentGrid);

						if(settings.selectableRows === 1) {
							$row.siblings().removeClass('selected');
							$checks.prop('checked',false);
						}
						$('input.select-on-check', $row).prop('checked', isRowSelected);
						$("input.select-on-check-all",$currentGrid).prop('checked', $checks.length === $checks.filter(':checked').length);

						if(settings.selectionChanged !== undefined)
							settings.selectionChanged(id);
					});
					if(settings.selectableRows > 1) {
						$(document).on('click','#'+id+' .select-on-check-all',function(){
							var $currentGrid = $('#'+id),
								$checks = $('input.select-on-check',$currentGrid),
								$checksAll = $('input.select-on-check-all',$currentGrid),
								$rows = $currentGrid.children('.'+settings.tableClass).children('tbody').children();
							if(this.checked){
								$rows.addClass('selected');
								$checks.prop('checked',true);
								$checksAll.prop('checked',true);
							} else {
								$rows.removeClass('selected');
								$checks.prop('checked',false);
								$checksAll.prop('checked',false);
							}
							if(settings.selectionChanged !== undefined)
								settings.selectionChanged(id);
						});
					}
				} else
					$(document).on('click','#'+id+' .select-on-check',false);
			});
		},

		/**
		 * Returns the key value for the specified row
		 * @param row integer the row number (zero-based index)
		 * @return string the key value
		 */
		getKey: function(row) {
			return this.children('.keys span:eq('+row+')').text()
		},

		/**
		 * Returns the URL that generates the grid view content.
		 * @return string the URL that generates the grid view content.
		 */
		getUrl: function() {
			var sUrl = gridSettings[this.attr('id')].url;
			return sUrl || this.children('.keys').attr('title');
		},

		/**
		 * Returns the jQuery collection of the cells in the specified row.
		 * @param row integer the row number (zero-based index)
		 * @return jQuery the jQuery collection of the cells in the specified row.
		 */
		getRow: function(row) {
			var sClass = gridSettings[this.attr('id')].tableClass;
			return this.children('.'+sClass).children('tbody').children('tr:eq('+row+')').children();
		},

		/**
		 * Returns the jQuery collection of the cells in the specified column.
		 * @param column integer the column number (zero-based index)
		 * @return jQuery the jQuery collection of the cells in the specified column.
		 */
		getColumn: function(column) {
			var sClass = gridSettings[this.attr('id')].tableClass;
			return this.children('.'+sClass).children('tbody').children('tr').children('td:nth-child('+(column+1)+')');
		},

		/**
		 * Performs an AJAX-based update of the grid view contents.
		 * @param options map the AJAX request options (see jQuery.ajax API manual). By default,
		 * the URL to be requested is the one that generates the current content of the grid view.
		 * @return object the jQuery object
		 */
		update: function(options) {
			if(options && options.error !== undefined) {
				var customError=options.error;
				delete options.error;
			}

			return this.each(function(){
				var $grid = $(this),
					id=$grid.attr('id'),
					settings = gridSettings[id];
				$grid.addClass(settings.loadingClass);

				options = $.extend({
					type: 'GET',
					url: $grid.yiiGridView('getUrl'),
					success: function(data,status) {
						var $data = $('<div>'+data+'</div>');
						$grid.removeClass(settings.loadingClass)
						$.each(settings.ajaxUpdate, function(i,v) {
							var updateId = '#'+v;
							$(updateId).replaceWith($(updateId,$data));
						});
						if(settings.afterAjaxUpdate !== undefined)
							settings.afterAjaxUpdate(id, data);
						if(settings.selectableRows > 0)
							$('#'+id).yiiGridView('selectCheckedRows');
					},
					error: function(XHR, textStatus, errorThrown) {
						$grid.removeClass(settings.loadingClass);
						if(XHR.readyState == 0 || XHR.status == 0)
							return;
						if(customError !== undefined) {
							var ret = customError(XHR);
							if( ret !== undefined && !ret)
								return;
						}
						var err='';
						switch(textStatus) {
							case 'timeout':
								err='The request timed out!';
								break;
							case 'parsererror':
								err='Parser error!';
								break;
							case 'error':
								if(XHR.status && !/^\s*$/.test(XHR.status))
									err = 'Error ' + XHR.status;
								else
									err = 'Error';
								if(XHR.responseText && !/^\s*$/.test(XHR.responseText))
									err = err + ': ' + XHR.responseText;
								break;
						}

						if(settings.ajaxUpdateError !== undefined)
							settings.ajaxUpdateError(XHR, textStatus, errorThrown,err);
						else if(err)
							alert(err);
					}
				}, options || {});
				if(options.data !== undefined && options.type === 'GET') {
					options.url = $.param.querystring(options.url, options.data);
					options.data = {};
				}

				if(settings.ajaxUpdate !== false) {
					options.url = $.param.querystring(options.url, settings.ajaxVar+'='+id);
					if(settings.beforeAjaxUpdate !== undefined)
						settings.beforeAjaxUpdate(id, options);
					$.ajax(options);
				}
				else {  // non-ajax mode
					if(options.type === 'GET') {
						window.location.href = options.url;
					}
					else {  // POST mode
						var $form = $('<form action="'+options.url+'" method="post"></form>').appendTo('body');
						if(options.data === undefined)
							options.data = {};

						if(options.data.returnUrl === undefined)
							options.data.returnUrl = window.location.href;

						$.each(options.data, function(name,value) {
							$form.append($('<input type="hidden" name="t" value="" />').attr('name',name).val(value));
						});
						$form.submit();
					}
				}
			});
		},

		/**
		 * 1. Selects rows that have checkbox checked (only checkbox that is connected with selecting a row)
		 * 2. Check if "check all" need to be checked/unchecked (all checkboxes)
		 * @return object the jQuery object
		 */
		selectCheckedRows: function() {
			var settings = gridSettings[this.attr('id')];
				table = this.children('.'+settings.tableClass);

			table.children('tbody').find('input.select-on-check').filter(':checked').each(function(){
				$(this).closest('tr').addClass('selected');
			});

			table.children('thead').find('th input').filter('[type="checkbox"]').each(function(){
				var name = this.name.substring(0, this.name.length-4)+'[]', //.. remove '_all' and add '[]''
					$checks = $("input[name='"+name+"']",table);
				this.checked = $checks.length > 0 && $checks.length === $checks.filter(':checked').length;
			});
			return this;
		},

		/**
		 * Returns the key values of the currently selected rows.
		 * @return array the key values of the currently selected rows.
		 */
		getSelection: function() {
			var settings = gridSettings[this.attr('id')],
				keys = this.find('.keys span'),
				selection = [];
			this.children('.'+settings.tableClass).children('tbody').children().each(function(i){
				if($(this).hasClass('selected'))
					selection.push(keys.eq(i).text());
			});
			return selection;
		},

		/**
		 * Returns the key values of the currently checked rows.
		 * @param column_id string the ID of the column
		 * @return array the key values of the currently checked rows.
		 */
		getChecked: function(column_id) {
			var settings = gridSettings[this.attr('id')],
				keys = this.find('.keys span'),
				checked = [];
			if(column_id.substring(column_id.length-2) !== '[]')
				column_id = column_id + '[]';
			this.children('.'+settings.tableClass).children('tbody').children('tr').children('td').children('input[name="'+column_id+'"]').each(function(i){
				if(this.checked)
					checked.push(keys.eq(i).text());
			});
			return checked;
		}
	};

	$.fn.yiiGridView = function (method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		} else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		} else {
			$.error('Method ' + method + ' does not exist on jQuery.yiiGridView');
			return false;
		}
	}

/******************************************************************************
 *** DEPRECATED METHODS
 *** used before Yii 1.1.9
 ******************************************************************************/
	$.fn.yiiGridView.settings = gridSettings;
	/**
	 * Returns the key value for the specified row
	 * @param id string the ID of the grid view container
	 * @param row integer the row number (zero-based index)
	 * @return string the key value
	 */
	$.fn.yiiGridView.getKey = function(id, row) {
		return $('#'+id).yiiGridView('getKey', row);
	};

	/**
	 * Returns the URL that generates the grid view content.
	 * @param id string the ID of the grid view container
	 * @return string the URL that generates the grid view content.
	 */
	$.fn.yiiGridView.getUrl = function(id) {
		return $('#'+id).yiiGridView('getUrl');
	};

	/**
	 * Returns the jQuery collection of the cells in the specified row.
	 * @param id string the ID of the grid view container
	 * @param row integer the row number (zero-based index)
	 * @return jQuery the jQuery collection of the cells in the specified row.
	 */
	$.fn.yiiGridView.getRow = function(id, row) {
		return $('#'+id).yiiGridView('getRow', row);
	};

	/**
	 * Returns the jQuery collection of the cells in the specified column.
	 * @param id string the ID of the grid view container
	 * @param column integer the column number (zero-based index)
	 * @return jQuery the jQuery collection of the cells in the specified column.
	 */
	$.fn.yiiGridView.getColumn = function(id, column) {
		return $('#'+id).yiiGridView('getColumn', column);
	};

	/**
	 * Performs an AJAX-based update of the grid view contents.
	 * @param id string the ID of the grid view container
	 * @param options map the AJAX request options (see jQuery.ajax API manual). By default,
	 * the URL to be requested is the one that generates the current content of the grid view.
	 */
	$.fn.yiiGridView.update = function(id, options) {
		$('#'+id).yiiGridView('update', options);
	};

	/**
	 * 1. Selects rows that have checkbox checked (only checkbox that is connected with selecting a row)
	 * 2. Check if "check all" need to be checked/unchecked (all checkboxes)
	 * @param id string the ID of the grid view container
	 */
	$.fn.yiiGridView.selectCheckedRows = function(id) {
		$('#'+id).yiiGridView('selectCheckedRows');
	};

	/**
	 * Returns the key values of the currently selected rows.
	 * @param id string the ID of the grid view container
	 * @return array the key values of the currently selected rows.
	 */
	$.fn.yiiGridView.getSelection = function(id) {
		return $('#'+id).yiiGridView('getSelection');
	};

	/**
	 * Returns the key values of the currently checked rows.
	 * @param id string the ID of the grid view container
	 * @param column_id string the ID of the column
	 * @return array the key values of the currently checked rows.
	 */
	$.fn.yiiGridView.getChecked = function(id,column_id) {
		return $('#'+id).yiiGridView('getChecked', column_id);
	};
})(jQuery);<commit_msg>(Fixes issue 2997) now returns proper key of row in CGridView<commit_after>/**
 * jQuery Yii GridView plugin file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright Copyright © 2008-2010 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 * @version $Id$
 */

(function($) {
	var gridSettings = {};

	var methods = {
		/**
		 * yiiGridView set function.
		 * @param options map settings for the grid view. Available options are as follows:
		 * - ajaxUpdate: array, IDs of the containers whose content may be updated by ajax response
		 * - ajaxVar: string, the name of the GET variable indicating the ID of the element triggering the AJAX request
		 * - pagerClass: string, the CSS class for the pager container
		 * - tableClass: string, the CSS class for the table
		 * - selectableRows: integer, the number of rows that can be selected
		 * - updateSelector: string, the selector for choosing which elements can trigger ajax requests
		 * - beforeAjaxUpdate: function, the function to be called before ajax request is sent
		 * - afterAjaxUpdate: function, the function to be called after ajax response is received
		 * - ajaxUpdateError: function, the function to be called if an ajax error occurs
		 * - selectionChanged: function, the function to be called after the row selection is changed
		 * @return object the jQuery object
		 */
		init: function(options) {
			var settings = $.extend( {
					ajaxUpdate: [],
					ajaxVar: 'ajax',
					pagerClass: 'pager',
					loadingClass: 'loading',
					filterClass: 'filters',
					tableClass: 'items',
					selectableRows: 1
					// updateSelector: '#id .pager a, '#id .grid thead th a',
					// beforeAjaxUpdate: function(id) {},
					// afterAjaxUpdate: function(id, data) {},
					// selectionChanged: function(id) {},
					// url: 'ajax request URL'
				}, options || {});

			return this.each(function(){
				var $grid = $(this),
					id = $grid.attr('id');

				settings.tableClass=settings.tableClass.replace(/\s+/g,'.');
				if(settings.updateSelector === undefined)
					settings.updateSelector = '#'+id+' .'+settings.pagerClass.replace(/\s+/g,'.')+' a, #'+id+' .'+settings.tableClass+' thead th a';

				gridSettings[id] = settings;

				if(settings.ajaxUpdate.length > 0) {
					$(document).on('click', settings.updateSelector, function(){
						$('#'+id).yiiGridView('update', {url: $(this).attr('href')});
						return false;
					});
				}

				var inputSelector = '#'+id+' .'+settings.filterClass+' input, '+'#'+id+' .'+settings.filterClass+' select';
				$(document).on('change', inputSelector, function(){
					var data = $(inputSelector).serialize();
					if(settings.pageVar !== undefined)
						data += '&' + settings.pageVar + '=1';
					$('#'+id).yiiGridView('update', {data: data});
				});

				if(settings.selectableRows > 0) {
					$grid.yiiGridView('selectCheckedRows');
					$(document).on('click','#'+id+' .'+settings.tableClass+' > tbody > tr',function(e){
						var $target = $(e.target);
						if($target.closest('td').hasClass('button-column') ||
							(e.target.type === 'checkbox' && !$target.hasClass('select-on-check')))
							return;

						var $currentGrid = $('#'+id),
							$row = $(this),
							isRowSelected = $row.toggleClass('selected').hasClass('selected'),
							$checks = $('input.select-on-check',$currentGrid);

						if(settings.selectableRows === 1) {
							$row.siblings().removeClass('selected');
							$checks.prop('checked',false);
						}
						$('input.select-on-check', $row).prop('checked', isRowSelected);
						$("input.select-on-check-all",$currentGrid).prop('checked', $checks.length === $checks.filter(':checked').length);

						if(settings.selectionChanged !== undefined)
							settings.selectionChanged(id);
					});
					if(settings.selectableRows > 1) {
						$(document).on('click','#'+id+' .select-on-check-all',function(){
							var $currentGrid = $('#'+id),
								$checks = $('input.select-on-check',$currentGrid),
								$checksAll = $('input.select-on-check-all',$currentGrid),
								$rows = $currentGrid.children('.'+settings.tableClass).children('tbody').children();
							if(this.checked){
								$rows.addClass('selected');
								$checks.prop('checked',true);
								$checksAll.prop('checked',true);
							} else {
								$rows.removeClass('selected');
								$checks.prop('checked',false);
								$checksAll.prop('checked',false);
							}
							if(settings.selectionChanged !== undefined)
								settings.selectionChanged(id);
						});
					}
				} else
					$(document).on('click','#'+id+' .select-on-check',false);
			});
		},

		/**
		 * Returns the key value for the specified row
		 * @param row integer the row number (zero-based index)
		 * @return string the key value
		 */
		getKey: function(row) {
			return this.children('.keys').children('span').eq(row).text();
		},

		/**
		 * Returns the URL that generates the grid view content.
		 * @return string the URL that generates the grid view content.
		 */
		getUrl: function() {
			var sUrl = gridSettings[this.attr('id')].url;
			return sUrl || this.children('.keys').attr('title');
		},

		/**
		 * Returns the jQuery collection of the cells in the specified row.
		 * @param row integer the row number (zero-based index)
		 * @return jQuery the jQuery collection of the cells in the specified row.
		 */
		getRow: function(row) {
			var sClass = gridSettings[this.attr('id')].tableClass;
			return this.children('.'+sClass).children('tbody').children('tr').eq(row).children();
		},

		/**
		 * Returns the jQuery collection of the cells in the specified column.
		 * @param column integer the column number (zero-based index)
		 * @return jQuery the jQuery collection of the cells in the specified column.
		 */
		getColumn: function(column) {
			var sClass = gridSettings[this.attr('id')].tableClass;
			return this.children('.'+sClass).children('tbody').children('tr').children('td:nth-child('+(column+1)+')');
		},

		/**
		 * Performs an AJAX-based update of the grid view contents.
		 * @param options map the AJAX request options (see jQuery.ajax API manual). By default,
		 * the URL to be requested is the one that generates the current content of the grid view.
		 * @return object the jQuery object
		 */
		update: function(options) {
			if(options && options.error !== undefined) {
				var customError = options.error;
				delete options.error;
			}

			return this.each(function(){
				var $grid = $(this),
					id = $grid.attr('id'),
					settings = gridSettings[id];
				$grid.addClass(settings.loadingClass);

				options = $.extend({
					type: 'GET',
					url: $grid.yiiGridView('getUrl'),
					success: function(data,status) {
						var $data = $('<div>'+data+'</div>');
						$grid.removeClass(settings.loadingClass)
						$.each(settings.ajaxUpdate, function(i,v) {
							var updateId = '#'+v;
							$(updateId).replaceWith($(updateId,$data));
						});
						if(settings.afterAjaxUpdate !== undefined)
							settings.afterAjaxUpdate(id, data);
						if(settings.selectableRows > 0)
							$('#'+id).yiiGridView('selectCheckedRows');
					},
					error: function(XHR, textStatus, errorThrown) {
						$grid.removeClass(settings.loadingClass);
						if(XHR.readyState == 0 || XHR.status == 0)
							return;
						if(customError !== undefined) {
							var ret = customError(XHR);
							if( ret !== undefined && !ret)
								return;
						}
						var err = '';
						switch(textStatus) {
							case 'timeout':
								err = 'The request timed out!';
								break;
							case 'parsererror':
								err = 'Parser error!';
								break;
							case 'error':
								if(XHR.status && !/^\s*$/.test(XHR.status))
									err = 'Error ' + XHR.status;
								else
									err = 'Error';
								if(XHR.responseText && !/^\s*$/.test(XHR.responseText))
									err = err + ': ' + XHR.responseText;
								break;
						}

						if(settings.ajaxUpdateError !== undefined)
							settings.ajaxUpdateError(XHR, textStatus, errorThrown,err);
						else if(err)
							alert(err);
					}
				}, options || {});
				if(options.data !== undefined && options.type === 'GET') {
					options.url = $.param.querystring(options.url, options.data);
					options.data = {};
				}

				if(settings.ajaxUpdate !== false) {
					options.url = $.param.querystring(options.url, settings.ajaxVar+'='+id);
					if(settings.beforeAjaxUpdate !== undefined)
						settings.beforeAjaxUpdate(id, options);
					$.ajax(options);
				}
				else {  // non-ajax mode
					if(options.type === 'GET') {
						window.location.href = options.url;
					}
					else {  // POST mode
						var $form = $('<form action="'+options.url+'" method="post"></form>').appendTo('body');
						if(options.data === undefined)
							options.data = {};

						if(options.data.returnUrl === undefined)
							options.data.returnUrl = window.location.href;

						$.each(options.data, function(name,value) {
							$form.append($('<input type="hidden" name="t" value="" />').attr('name',name).val(value));
						});
						$form.submit();
					}
				}
			});
		},

		/**
		 * 1. Selects rows that have checkbox checked (only checkbox that is connected with selecting a row)
		 * 2. Check if "check all" need to be checked/unchecked (all checkboxes)
		 * @return object the jQuery object
		 */
		selectCheckedRows: function() {
			var settings = gridSettings[this.attr('id')],
				table = this.children('.'+settings.tableClass);

			table.children('tbody').find('input.select-on-check').filter(':checked').each(function(){
				$(this).closest('tr').addClass('selected');
			});

			table.children('thead').find('th input').filter('[type="checkbox"]').each(function(){
				var name = this.name.substring(0, this.name.length-4)+'[]', //.. remove '_all' and add '[]''
					$checks = $("input[name='"+name+"']",table);
				this.checked = $checks.length > 0 && $checks.length === $checks.filter(':checked').length;
			});
			return this;
		},

		/**
		 * Returns the key values of the currently selected rows.
		 * @return array the key values of the currently selected rows.
		 */
		getSelection: function() {
			var settings = gridSettings[this.attr('id')],
				keys = this.find('.keys span'),
				selection = [];
			this.children('.'+settings.tableClass).children('tbody').children().each(function(i){
				if($(this).hasClass('selected'))
					selection.push(keys.eq(i).text());
			});
			return selection;
		},

		/**
		 * Returns the key values of the currently checked rows.
		 * @param column_id string the ID of the column
		 * @return array the key values of the currently checked rows.
		 */
		getChecked: function(column_id) {
			var settings = gridSettings[this.attr('id')],
				keys = this.find('.keys span'),
				checked = [];
			if(column_id.substring(column_id.length-2) !== '[]')
				column_id = column_id + '[]';
			this.children('.'+settings.tableClass).children('tbody').children('tr').children('td').children('input[name="'+column_id+'"]').each(function(i){
				if(this.checked)
					checked.push(keys.eq(i).text());
			});
			return checked;
		}
	};

	$.fn.yiiGridView = function (method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		} else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		} else {
			$.error('Method ' + method + ' does not exist on jQuery.yiiGridView');
			return false;
		}
	}

/******************************************************************************
 *** DEPRECATED METHODS
 *** used before Yii 1.1.9
 ******************************************************************************/
	$.fn.yiiGridView.settings = gridSettings;
	/**
	 * Returns the key value for the specified row
	 * @param id string the ID of the grid view container
	 * @param row integer the row number (zero-based index)
	 * @return string the key value
	 */
	$.fn.yiiGridView.getKey = function(id, row) {
		return $('#'+id).yiiGridView('getKey', row);
	};

	/**
	 * Returns the URL that generates the grid view content.
	 * @param id string the ID of the grid view container
	 * @return string the URL that generates the grid view content.
	 */
	$.fn.yiiGridView.getUrl = function(id) {
		return $('#'+id).yiiGridView('getUrl');
	};

	/**
	 * Returns the jQuery collection of the cells in the specified row.
	 * @param id string the ID of the grid view container
	 * @param row integer the row number (zero-based index)
	 * @return jQuery the jQuery collection of the cells in the specified row.
	 */
	$.fn.yiiGridView.getRow = function(id, row) {
		return $('#'+id).yiiGridView('getRow', row);
	};

	/**
	 * Returns the jQuery collection of the cells in the specified column.
	 * @param id string the ID of the grid view container
	 * @param column integer the column number (zero-based index)
	 * @return jQuery the jQuery collection of the cells in the specified column.
	 */
	$.fn.yiiGridView.getColumn = function(id, column) {
		return $('#'+id).yiiGridView('getColumn', column);
	};

	/**
	 * Performs an AJAX-based update of the grid view contents.
	 * @param id string the ID of the grid view container
	 * @param options map the AJAX request options (see jQuery.ajax API manual). By default,
	 * the URL to be requested is the one that generates the current content of the grid view.
	 */
	$.fn.yiiGridView.update = function(id, options) {
		$('#'+id).yiiGridView('update', options);
	};

	/**
	 * 1. Selects rows that have checkbox checked (only checkbox that is connected with selecting a row)
	 * 2. Check if "check all" need to be checked/unchecked (all checkboxes)
	 * @param id string the ID of the grid view container
	 */
	$.fn.yiiGridView.selectCheckedRows = function(id) {
		$('#'+id).yiiGridView('selectCheckedRows');
	};

	/**
	 * Returns the key values of the currently selected rows.
	 * @param id string the ID of the grid view container
	 * @return array the key values of the currently selected rows.
	 */
	$.fn.yiiGridView.getSelection = function(id) {
		return $('#'+id).yiiGridView('getSelection');
	};

	/**
	 * Returns the key values of the currently checked rows.
	 * @param id string the ID of the grid view container
	 * @param column_id string the ID of the column
	 * @return array the key values of the currently checked rows.
	 */
	$.fn.yiiGridView.getChecked = function(id,column_id) {
		return $('#'+id).yiiGridView('getChecked', column_id);
	};
})(jQuery);<|endoftext|>


<commit_before>(function(root) {

var helper = root.helper

if (typeof require != 'undefined') {
    helper = require('./helper')
}

var Board = function(size, arrangement, captures) {
    this.size = !isNaN(size) ? size : 19
    this.captures = captures ? { '-1': captures['-1'], '1': captures['1'] } : { '-1': 0, '1': 0 }
    this.arrangement = {}
    this.overlays = {}

    // Initialize arrangement
    for (var x = 0; x < this.size; x++) {
        for (var y = 0; y < this.size; y++) {
            this.arrangement[[x, y]] = arrangement ? arrangement[[x, y]] : 0
        }
    }
}

Board.prototype = {
    clone: function() {
        return this.makeMove(0)
    },

    hasVertex: function(vertex) {
        var x = vertex[0], y = vertex[1]
        return 0 <= Math.min(x, y) && Math.max(x, y) < this.size
    },

    clear: function() {
        for (var x = 0; x < this.size; x++) {
            for (var y = 0; y < this.size; y++) {
                this.arrangement[[x, y]] = 0
            }
        }
    },

    getDistance: function(v, w) {
        return Math.abs(v[0] - w[0]) + Math.abs(v[1] - w[1])
    },

    getDistanceToGround: function(vertex) {
        return Math.min(vertex[0] + 1, this.size - vertex[0], vertex[1] + 1, this.size - vertex[1])
    },

    getNeighbors: function(vertex) {
        var self = this
        if (!self.hasVertex(vertex)) return []
        var x = vertex[0], y = vertex[1]

        return [
            [x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]
        ].filter(self.hasVertex.bind(self))
    },

    getNeighborhood: function(vertex) {
        var self = this
        var result = []

        if (!self.hasVertex(vertex)) return result

        for (var x = vertex[0] - 3; x <= vertex[0] + 3; x++) {
            for (var y = vertex[1] - 3; y <= vertex[1] + 3; y++) {
                if (!self.hasVertex([x, y])) continue
                var distance = Math.pow(vertex[0] - x, 2) + Math.pow(vertex[1] - y, 2)
                if (distance <= 10) result.push([x, y])
            }
        }

        return result
    },

    getNeighborhoodStones: function(vertex) {
        var self = this
        var surroundings = self.getNeighborhood(vertex)
        var stones = [vertex]

        while (true) {
            var newstones = surroundings.filter(function(v) {
                return self.arrangement[v] != 0
                    && !stones.some(function(w) { return w[0] == v[0] && w[1] == v[1] })
            })

            if (newstones.length == 0) return stones

            newstones.forEach(function(v) {
                if (stones.some(function(w) { return w[0] == v[0] && w[1] == v[1] })) return

                self.getNeighborhood(v).forEach(function(n) {
                    surroundings.push(n)
                })

                stones.push(v)
            })
        }
    },

    getConnectedComponent: function(vertex, colors, result) {
        if (!this.hasVertex(vertex)) return []
        if (!result) result = [vertex]

        // Recursive depth-first search
        this.getNeighbors(vertex).forEach(function(v) {
            if (colors.indexOf(this.arrangement[v]) == -1) return
            if (result.some(function(w) { return w[0] == v[0] && w[1] == v[1] })) return

            result.push(v)
            this.getConnectedComponent(v, colors, result)
        }.bind(this))

        return result
    },

    getChain: function(vertex) {
        return this.getConnectedComponent(vertex, [this.arrangement[vertex]])
    },

    getLiberties: function(vertex) {
        if (this.arrangement[vertex] == 0) return []

        var self = this
        var chain = self.getChain(vertex)
        var liberties = []

        chain.forEach(function(c) {
            liberties.push.apply(liberties, self.getNeighbors(c).filter(function(n) {
                return self.arrangement[n] == 0
                && !liberties.some(function(v) { return v[0] == n[0] && v[1] == n[1] })
            }))
        })

        return liberties
    },

    getRelatedChains: function(vertex) {
        if (this.arrangement[vertex] == 0) return []

        var area = this.getConnectedComponent(vertex, [this.arrangement[vertex], 0])
        return area.filter(function(v) {
            return this.arrangement[v] == this.arrangement[vertex]
        }, this)
    },

    getAreaMap: function() {
        var self = this
        var map = {}

        for (var i = 0; i < self.size; i++) {
            for (var j = 0; j < self.size; j++) {
                var vertex = [i, j]

                if (vertex in map) continue
                if (self.arrangement[vertex] != 0) {
                    map[vertex] = self.arrangement[vertex]
                    continue
                }

                var chain = self.getChain(vertex)
                var sign = 0
                var indicator = 1

                chain.forEach(function(c) {
                    if (indicator == 0) return

                    self.getNeighbors(c).forEach(function(n) {
                        if (self.arrangement[n] == 0 || indicator == 0) return

                        if (sign == 0) sign = map[n] = self.arrangement[n]
                        else if (sign != self.arrangement[n]) indicator = 0
                    })
                })

                chain.forEach(function(c) {
                    map[c] = sign * indicator
                })
            }
        }

        return map
    },

    getScore: function(areaMap) {
        var score = {}

        score['area_1'] = 0
        score['area_-1'] = 0
        score['territory_1'] = 0
        score['territory_-1'] = 0
        score['captures_1'] = this.captures['1']
        score['captures_-1'] = this.captures['-1']

        for (var vertex in areaMap) {
            var sign = areaMap[vertex]
            if (sign == 0) continue

            score['area_' + sign]++
            if (this.arrangement[vertex] == 0) score['territory_' + sign]++
        }

        return score
    },

    isValid: function() {
        var liberties = {}

        for (var x = 0; x < this.size; x++) {
            for (var y = 0; y < this.size; y++) {
                var vertex = [x, y]
                if (vertex in liberties || this.arrangement[vertex] == 0) continue

                var l = this.getLiberties(vertex).length
                if (l == 0) return false

                this.getChain(vertex).forEach(function(v) {
                    liberties[v] = l
                })
            }
        }

        return true
    },

    makeMove: function(sign, vertex) {
        var move = new Board(this.size, this.arrangement, this.captures)

        if (sign == 0 || !this.hasVertex(vertex)) return move
        if (this.arrangement[vertex] != 0) return null

        sign = sign > 0 ? 1 : -1
        var suicide = true

        // Remove captured stones
        this.getNeighbors(vertex).forEach(function(n) {
            if (move.arrangement[n] != -sign) return

            var ll = this.getLiberties(n)
            if (ll.length != 1) return

            var l = ll[0]
            if (l[0] != vertex[0] || l[1] != vertex[1]) return

            this.getChain(n).forEach(function(c) {
                move.arrangement[c] = 0
                move.captures[sign.toString()]++
            })

            suicide = false;
        }.bind(this))

        move.arrangement[vertex] = sign

        // Detect suicide
        if (suicide) {
            var chain = move.getChain(vertex)

            if (move.getLiberties(vertex).length == 0) {
                chain.forEach(function(c) {
                    move.arrangement[c] = 0
                    move.captures[(-sign).toString()]++
                })
            }
        }

        return move
    },

    getHandicapPlacement: function(count) {
        if (this.size < 6 || count < 2) return []

        var near = this.size >= 13 ? 3 : 2
        var far = this.size - near - 1

        var result = [[near, near], [far, far], [near, far], [far, near]]

        if (this.size % 2 != 0) {
            var middle = (this.size - 1) / 2
            if (count == 5) result.push([middle, middle])
            result.push([near, middle], [far, middle])
            if (count == 7) result.push([middle, middle])
            result.push([middle, near], [middle, far], [middle, middle])
        }

        return result.slice(0, count)
    },

    guessDeadStones: function() {
        var self = this
        var map = self.getAreaMap()
        var done = {}
        var result = []

        for (var i = 0; i < this.size; i++) {
            for (var j = 0; j < this.size; j++) {
                var vertex = [i, j]
                if (map[vertex] != 0 || vertex in done) continue

                var posArea = self.getConnectedComponent(vertex, [0, -1])
                var negArea = self.getConnectedComponent(vertex, [0, 1])
                var posDead = posArea.filter(function(v) { return self.arrangement[v] == -1 })
                var negDead = negArea.filter(function(v) { return self.arrangement[v] == 1 })

                var sign = 0
                var actualArea, actualDead

                var negDiff = negArea.filter(function(y) {
                    return !negDead.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                        && !posArea.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                })

                var posDiff = posArea.filter(function(y) {
                    return !posDead.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                        && !negArea.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                })

                if (negDiff.length <= 1 && negDead.length <= posDead.length) {
                    sign--
                    actualArea = negArea
                    actualDead = negDead
                }

                if (posDiff.length <= 1 && posDead.length <= negDead.length) {
                    sign++
                    actualArea = posArea
                    actualDead = posDead
                }

                if (sign == 0) {
                    actualArea = self.getChain(vertex)
                    actualDead = []
                }

                actualArea.forEach(function(v) {
                    done[v] = 1
                })

                result.push.apply(result, actualDead)
            }
        }

        return result
    },

    getHash: function() {
        return helper.hash(JSON.stringify(this.arrangement))
    }
}

if (typeof module != 'undefined') module.exports = Board
else window.Board = Board

}).call(null, typeof module != 'undefined' ? module : window)
<commit_msg>Board.getSurroundings()<commit_after>(function(root) {

var helper = root.helper

if (typeof require != 'undefined') {
    helper = require('./helper')
}

var Board = function(size, arrangement, captures) {
    this.size = !isNaN(size) ? size : 19
    this.captures = captures ? { '-1': captures['-1'], '1': captures['1'] } : { '-1': 0, '1': 0 }
    this.arrangement = {}
    this.overlays = {}

    // Initialize arrangement
    for (var x = 0; x < this.size; x++) {
        for (var y = 0; y < this.size; y++) {
            this.arrangement[[x, y]] = arrangement ? arrangement[[x, y]] : 0
        }
    }
}

Board.prototype = {
    clone: function() {
        return this.makeMove(0)
    },

    hasVertex: function(vertex) {
        var x = vertex[0], y = vertex[1]
        return 0 <= Math.min(x, y) && Math.max(x, y) < this.size
    },

    clear: function() {
        for (var x = 0; x < this.size; x++) {
            for (var y = 0; y < this.size; y++) {
                this.arrangement[[x, y]] = 0
            }
        }
    },

    getDistance: function(v, w) {
        return Math.abs(v[0] - w[0]) + Math.abs(v[1] - w[1])
    },

    getDistanceToGround: function(vertex) {
        return Math.min(vertex[0] + 1, this.size - vertex[0], vertex[1] + 1, this.size - vertex[1])
    },

    getNeighbors: function(vertex) {
        var self = this
        if (!self.hasVertex(vertex)) return []
        var x = vertex[0], y = vertex[1]

        return [
            [x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]
        ].filter(self.hasVertex.bind(self))
    },

    getNeighborhood: function(vertex) {
        var self = this
        var result = []

        if (!self.hasVertex(vertex)) return result

        for (var x = vertex[0] - 3; x <= vertex[0] + 3; x++) {
            for (var y = vertex[1] - 3; y <= vertex[1] + 3; y++) {
                if (!self.hasVertex([x, y])) continue
                var distance = Math.pow(vertex[0] - x, 2) + Math.pow(vertex[1] - y, 2)
                if (distance <= 10) result.push([x, y])
            }
        }

        return result
    },

    getSurroundings: function(vertex) {
        var self = this
        var result = self.getNeighborhood(vertex)
        var stones = [vertex]

        while (true) {
            var newstones = result.filter(function(v) {
                return self.arrangement[v] != 0
                    && !stones.some(function(w) { return w[0] == v[0] && w[1] == v[1] })
            })

            if (newstones.length == 0) return result

            newstones.forEach(function(v) {
                if (stones.some(function(w) { return w[0] == v[0] && w[1] == v[1] })) return

                self.getNeighborhood(v).forEach(function(n) {
                    if (result.some(function(w) { return w[0] == n[0] && w[1] == n[1] })) return
                    result.push(n)
                })

                stones.push(v)
            })
        }
    },

    getConnectedComponent: function(vertex, colors, result) {
        if (!this.hasVertex(vertex)) return []
        if (!result) result = [vertex]

        // Recursive depth-first search
        this.getNeighbors(vertex).forEach(function(v) {
            if (colors.indexOf(this.arrangement[v]) == -1) return
            if (result.some(function(w) { return w[0] == v[0] && w[1] == v[1] })) return

            result.push(v)
            this.getConnectedComponent(v, colors, result)
        }.bind(this))

        return result
    },

    getChain: function(vertex) {
        return this.getConnectedComponent(vertex, [this.arrangement[vertex]])
    },

    getLiberties: function(vertex) {
        if (this.arrangement[vertex] == 0) return []

        var self = this
        var chain = self.getChain(vertex)
        var liberties = []

        chain.forEach(function(c) {
            liberties.push.apply(liberties, self.getNeighbors(c).filter(function(n) {
                return self.arrangement[n] == 0
                && !liberties.some(function(v) { return v[0] == n[0] && v[1] == n[1] })
            }))
        })

        return liberties
    },

    getRelatedChains: function(vertex) {
        if (this.arrangement[vertex] == 0) return []

        var area = this.getConnectedComponent(vertex, [this.arrangement[vertex], 0])
        return area.filter(function(v) {
            return this.arrangement[v] == this.arrangement[vertex]
        }, this)
    },

    getAreaMap: function() {
        var self = this
        var map = {}

        for (var i = 0; i < self.size; i++) {
            for (var j = 0; j < self.size; j++) {
                var vertex = [i, j]

                if (vertex in map) continue
                if (self.arrangement[vertex] != 0) {
                    map[vertex] = self.arrangement[vertex]
                    continue
                }

                var chain = self.getChain(vertex)
                var sign = 0
                var indicator = 1

                chain.forEach(function(c) {
                    if (indicator == 0) return

                    self.getNeighbors(c).forEach(function(n) {
                        if (self.arrangement[n] == 0 || indicator == 0) return

                        if (sign == 0) sign = map[n] = self.arrangement[n]
                        else if (sign != self.arrangement[n]) indicator = 0
                    })
                })

                chain.forEach(function(c) {
                    map[c] = sign * indicator
                })
            }
        }

        return map
    },

    getScore: function(areaMap) {
        var score = {}

        score['area_1'] = 0
        score['area_-1'] = 0
        score['territory_1'] = 0
        score['territory_-1'] = 0
        score['captures_1'] = this.captures['1']
        score['captures_-1'] = this.captures['-1']

        for (var vertex in areaMap) {
            var sign = areaMap[vertex]
            if (sign == 0) continue

            score['area_' + sign]++
            if (this.arrangement[vertex] == 0) score['territory_' + sign]++
        }

        return score
    },

    isValid: function() {
        var liberties = {}

        for (var x = 0; x < this.size; x++) {
            for (var y = 0; y < this.size; y++) {
                var vertex = [x, y]
                if (vertex in liberties || this.arrangement[vertex] == 0) continue

                var l = this.getLiberties(vertex).length
                if (l == 0) return false

                this.getChain(vertex).forEach(function(v) {
                    liberties[v] = l
                })
            }
        }

        return true
    },

    makeMove: function(sign, vertex) {
        var move = new Board(this.size, this.arrangement, this.captures)

        if (sign == 0 || !this.hasVertex(vertex)) return move
        if (this.arrangement[vertex] != 0) return null

        sign = sign > 0 ? 1 : -1
        var suicide = true

        // Remove captured stones
        this.getNeighbors(vertex).forEach(function(n) {
            if (move.arrangement[n] != -sign) return

            var ll = this.getLiberties(n)
            if (ll.length != 1) return

            var l = ll[0]
            if (l[0] != vertex[0] || l[1] != vertex[1]) return

            this.getChain(n).forEach(function(c) {
                move.arrangement[c] = 0
                move.captures[sign.toString()]++
            })

            suicide = false;
        }.bind(this))

        move.arrangement[vertex] = sign

        // Detect suicide
        if (suicide) {
            var chain = move.getChain(vertex)

            if (move.getLiberties(vertex).length == 0) {
                chain.forEach(function(c) {
                    move.arrangement[c] = 0
                    move.captures[(-sign).toString()]++
                })
            }
        }

        return move
    },

    getHandicapPlacement: function(count) {
        if (this.size < 6 || count < 2) return []

        var near = this.size >= 13 ? 3 : 2
        var far = this.size - near - 1

        var result = [[near, near], [far, far], [near, far], [far, near]]

        if (this.size % 2 != 0) {
            var middle = (this.size - 1) / 2
            if (count == 5) result.push([middle, middle])
            result.push([near, middle], [far, middle])
            if (count == 7) result.push([middle, middle])
            result.push([middle, near], [middle, far], [middle, middle])
        }

        return result.slice(0, count)
    },

    guessDeadStones: function() {
        var self = this
        var map = self.getAreaMap()
        var done = {}
        var result = []

        for (var i = 0; i < this.size; i++) {
            for (var j = 0; j < this.size; j++) {
                var vertex = [i, j]
                if (map[vertex] != 0 || vertex in done) continue

                var posArea = self.getConnectedComponent(vertex, [0, -1])
                var negArea = self.getConnectedComponent(vertex, [0, 1])
                var posDead = posArea.filter(function(v) { return self.arrangement[v] == -1 })
                var negDead = negArea.filter(function(v) { return self.arrangement[v] == 1 })

                var sign = 0
                var actualArea, actualDead

                var negDiff = negArea.filter(function(y) {
                    return !negDead.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                        && !posArea.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                })

                var posDiff = posArea.filter(function(y) {
                    return !posDead.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                        && !negArea.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                })

                if (negDiff.length <= 1 && negDead.length <= posDead.length) {
                    sign--
                    actualArea = negArea
                    actualDead = negDead
                }

                if (posDiff.length <= 1 && posDead.length <= negDead.length) {
                    sign++
                    actualArea = posArea
                    actualDead = posDead
                }

                if (sign == 0) {
                    actualArea = self.getChain(vertex)
                    actualDead = []
                }

                actualArea.forEach(function(v) {
                    done[v] = 1
                })

                result.push.apply(result, actualDead)
            }
        }

        return result
    },

    getHash: function() {
        return helper.hash(JSON.stringify(this.arrangement))
    }
}

if (typeof module != 'undefined') module.exports = Board
else window.Board = Board

}).call(null, typeof module != 'undefined' ? module : window)
<|endoftext|>


<commit_before>'use strict';

var assert = require('assert');
var Resource = require('../src/resource');

var _ = require('lodash');
//var AWS = require('aws-sdk');
//AWS.config.region = 'us-east-1';
//var ASG = P.promisifyAll(new AWS.AutoScaling());

function testCall(res, cb) {
	return res
		.call
		.then(function(data) {
			assert(data.ResponseMetadata.RequestId);
			cb();
		})
		.catch(function(e) {
			assert.fail(e);
			cb();
		});
};

function testConstructor(res) {
	var name = 'test-001';
	var resource = new res.construct(name, {});
	assert.equal(resource.inTemplate, false);
	assert(_.isEqual(resource.templateParams, {}));
	assert.equal(resource.id, name);
	assert.equal(resource.name, 'test001Resource');
	assert(_.isEqual(resource.body, {}));
	assert(resource.block);
}

describe('resource', function () {
	it('buildName should return a unique alphanumeric name', function () {
		var vpc = new Resource.EC2.VPC.construct('resource-001', {});
		assert.equal(vpc.name, 'resource001Resource');
	});

	describe('AWS::AutoScaling::AutoScaling', function() {
		it('AWS::AutoScaling::AutoScaling\'s call method should work', function(done) {
			return testCall(Resource.AutoScaling.AutoScalingGroup, done);
		});

		it('AWS::AutoScaling::AutoScaling\'s constructor method should work', function() {
			testConstructor(Resource.AutoScaling.AutoScalingGroup);
		});
	});

	describe('AWS::AutoScaling::LaunchConfiguration', function() {
		it('AWS::AutoScaling::LaunchConfiguration\'s call method should work', function(done) {
			return testCall(Resource.AutoScaling.LaunchConfiguration, done);
		});

		it('AWS::AutoScaling::AutoScaling\'s constructor method should work', function() {
			testConstructor(Resource.AutoScaling.LaunchConfiguration);
		});
	});

	describe('AWS::AutoScaling::ScalingPolicy', function() {
		it('AWS::AutoScaling::ScalingPolicy\'s call method should work', function(done) {
			return testCall(Resource.AutoScaling.ScalingPolicy, done);
		});

		it('AWS::AutoScaling::ScalingPolicy\'s constructor method should work', function() {
			testConstructor(Resource.AutoScaling.ScalingPolicy);
		});
	});

	describe('AWS::AutoScaling::ScheduledAction', function() {
		it('AWS::AutoScaling::ScheduledAction\'s call method should work', function(done) {
			return testCall(Resource.AutoScaling.ScheduledAction, done);
		});

		it('AWS::AutoScaling::ScheduledAction\'s constructor method should work', function() {
			testConstructor(Resource.AutoScaling.ScheduledAction);
		});
	});

});

<commit_msg>Adding unit tests for resource EC2.<commit_after>'use strict';

var assert = require('assert');
var Resource = require('../src/resource');

var _ = require('lodash');
//var AWS = require('aws-sdk');
//AWS.config.region = 'us-east-1';
//var ASG = P.promisifyAll(new AWS.AutoScaling());

function testCall(res, cb) {
	return res
		.call
		.then(function(data) {
			if(data.ResponseMetadata) {
				assert(data.ResponseMetadata.RequestId);
			} else if(data) {
				assert(data);
			} else {
				assert.fail(data);
			}
			cb();
		})
		.catch(function(e) {
			assert.fail(e);
			cb();
		});
};

function testConstructor(res) {
	var name = 'test-001';
	var resource = new res.construct(name, {});
	assert.equal(resource.inTemplate, false);
	assert(_.isEqual(resource.templateParams, {}));
	assert.equal(resource.id, name);
	assert.equal(resource.name, 'test001Resource');
	assert(_.isEqual(resource.body, {}));
	assert(resource.block);
}

describe('resource', function () {
	it('buildName should return a unique alphanumeric name', function () {
		var vpc = new Resource.EC2.VPC.construct('resource-001', {});
		assert.equal(vpc.name, 'resource001Resource');
	});

	describe('AWS::Autoscaling', function() {

		describe('AWS::AutoScaling::AutoScaling', function() {
			it('AWS::AutoScaling::AutoScaling\'s call method should work', function(done) {
				return testCall(Resource.AutoScaling.AutoScalingGroup, done);
			});

			it('AWS::AutoScaling::AutoScaling\'s constructor method should work', function() {
				testConstructor(Resource.AutoScaling.AutoScalingGroup);
			});
		});

		describe('AWS::AutoScaling::LaunchConfiguration', function() {
			it('AWS::AutoScaling::LaunchConfiguration\'s call method should work', function(done) {
				return testCall(Resource.AutoScaling.LaunchConfiguration, done);
			});

			it('AWS::AutoScaling::AutoScaling\'s constructor method should work', function() {
				testConstructor(Resource.AutoScaling.LaunchConfiguration);
			});
		});

		describe('AWS::AutoScaling::ScalingPolicy', function() {
			it('AWS::AutoScaling::ScalingPolicy\'s call method should work', function(done) {
				return testCall(Resource.AutoScaling.ScalingPolicy, done);
			});

			it('AWS::AutoScaling::ScalingPolicy\'s constructor method should work', function() {
				testConstructor(Resource.AutoScaling.ScalingPolicy);
			});
		});

		describe('AWS::AutoScaling::ScheduledAction', function() {
			it('AWS::AutoScaling::ScheduledAction\'s call method should work', function(done) {
				return testCall(Resource.AutoScaling.ScheduledAction, done);
			});

			it('AWS::AutoScaling::ScheduledAction\'s constructor method should work', function() {
				testConstructor(Resource.AutoScaling.ScheduledAction);
			});
		});
	});

	describe('AWS::EC2', function() {

		describe('AWS::EC2::CustomerGateway', function() {
			it('AWS::EC2::CustomerGateway\'s call method should work', function(done) {
				return testCall(Resource.EC2.CustomerGateway, done);
			});

			it('AWS::EC2::\'s constructor method should work', function() {
				testConstructor(Resource.EC2.CustomerGateway);
			});
		});

		describe('AWS::EC2::DHCPOptions', function() {
			it('AWS::EC2::DHCPOptions\'s call method should work', function(done) {
				return testCall(Resource.EC2.DHCPOptions, done);
			});

			it('AWS::EC2::DHCPOptions\'s constructor method should work', function() {
				testConstructor(Resource.EC2.DHCPOptions);
			});
		});

		describe('AWS::EC2::EIP', function() {
			it('AWS::EC2::EIP\'s call method should work', function(done) {
				return testCall(Resource.EC2.EIP, done);
			});

			it('AWS::EC2::EIP\'s constructor method should work', function() {
				testConstructor(Resource.EC2.EIP);
			});
		});

		describe('AWS::EC2::EIPAssociation', function() {
			//it('AWS::EC2::EIPAssociation\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.EIPAssociation, done);
			//});

			it('AWS::EC2::EIPAssociation\'s constructor method should work', function() {
				testConstructor(Resource.EC2.EIPAssociation);
			});
		});

		describe('AWS::EC2::Instance', function() {
			it('AWS::EC2::Instance\'s call method should work', function(done) {
				return testCall(Resource.EC2.Instance, done);
			});

			it('AWS::EC2::Instance\'s constructor method should work', function() {
				testConstructor(Resource.EC2.Instance);
			});
		});

		describe('AWS::EC2::InternetGateway', function() {
			it('AWS::EC2::InternetGateway\'s call method should work', function(done) {
				return testCall(Resource.EC2.InternetGateway, done);
			});

			it('AWS::EC2::InternetGateway\'s constructor method should work', function() {
				testConstructor(Resource.EC2.InternetGateway);
			});
		});

		describe('AWS::EC2::NetworkAcl', function() {
			it('AWS::EC2::NetworkAcl\'s call method should work', function(done) {
				return testCall(Resource.EC2.NetworkAcl, done);
			});

			it('AWS::EC2::NetworkAcl\'s constructor method should work', function() {
				testConstructor(Resource.EC2.NetworkAcl);
			});
		});

		describe('AWS::EC2::NetworkAclEntry', function() {
			//it('AWS::EC2::NetworkAclEntry\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.NetworkAclEntry, done);
			//});

			it('AWS::EC2::NetworkAclEntry\'s constructor method should work', function() {
				testConstructor(Resource.EC2.NetworkAclEntry);
			});
		});

		describe('AWS::EC2::NetworkInterface', function() {
			it('AWS::EC2::NetworkInterface\'s call method should work', function(done) {
				return testCall(Resource.EC2.NetworkInterface, done);
			});

			it('AWS::EC2::NetworkInterface\'s constructor method should work', function() {
				testConstructor(Resource.EC2.NetworkInterface);
			});
		});

		describe('AWS::EC2::NetworkInterfaceAttachment', function() {
			//it('AWS::EC2::NetworkInterfaceAttachment\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.NetworkInterfaceAttachment, done);
			//});

			it('AWS::EC2::NetworkInterfaceAttachment\'s constructor method should work', function() {
				testConstructor(Resource.EC2.NetworkInterfaceAttachment);
			});
		});

		describe('AWS::EC2::PlacementGroup', function() {
			it('AWS::EC2::PlacementGroup\'s call method should work', function(done) {
				return testCall(Resource.EC2.PlacementGroup, done);
			});

			it('AWS::EC2::PlacementGroup\'s constructor method should work', function() {
				testConstructor(Resource.EC2.PlacementGroup);
			});
		});

		describe('AWS::EC2::Route', function() {
			//it('AWS::EC2::Route\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.Route, done);
			//});

			it('AWS::EC2::Route\'s constructor method should work', function() {
				testConstructor(Resource.EC2.Route);
			});
		});

		describe('AWS::EC2::RouteTable', function() {
			it('AWS::EC2::RouteTable\'s call method should work', function(done) {
				return testCall(Resource.EC2.RouteTable, done);
			});

			it('AWS::EC2::RouteTable\'s constructor method should work', function() {
				testConstructor(Resource.EC2.RouteTable);
			});
		});

		describe('AWS::EC2::SecurityGroup', function() {
			it('AWS::EC2::SecurityGroup\'s call method should work', function(done) {
				return testCall(Resource.EC2.SecurityGroup, done);
			});

			it('AWS::EC2::SecurityGroup\'s constructor method should work', function() {
				testConstructor(Resource.EC2.SecurityGroup);
			});
		});

		describe('AWS::EC2::SecurityGroupEgress', function() {
			//it('AWS::EC2::SecurityGroupEgress\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.SecurityGroupEgress, done);
			//});

			it('AWS::EC2::SecurityGroupEgress\'s constructor method should work', function() {
				testConstructor(Resource.EC2.SecurityGroupEgress);
			});
		});

		describe('AWS::EC2::SecurityGroupIngress', function() {
			//it('AWS::EC2::SecurityGroupIngress\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.SecurityGroupIngress, done);
			//});

			it('AWS::EC2::SecurityGroupIngress\'s constructor method should work', function() {
				testConstructor(Resource.EC2.SecurityGroupIngress);
			});
		});

		describe('AWS::EC2::SpotFleet', function() {
			it('AWS::EC2::SpotFleet\'s call method should work', function(done) {
				return testCall(Resource.EC2.SpotFleet, done);
			});

			it('AWS::EC2::SpotFleet\'s constructor method should work', function() {
				testConstructor(Resource.EC2.SpotFleet);
			});
		});

		describe('AWS::EC2::Subnet', function() {
			it('AWS::EC2::Subnet\'s call method should work', function(done) {
				return testCall(Resource.EC2.Subnet, done);
			});

			it('AWS::EC2::Subnet\'s constructor method should work', function() {
				testConstructor(Resource.EC2.Subnet);
			});
		});

		describe('AWS::EC2::SubnetNetworkAclAssociation', function() {
			//it('AWS::EC2::SubnetNetworkAclAssociation\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.SubnetNetworkAclAssociation, done);
			//});

			it('AWS::EC2::SubnetNetworkAclAssociation\'s constructor method should work', function() {
				testConstructor(Resource.EC2.SubnetNetworkAclAssociation);
			});
		});

		describe('AWS::EC2::SubnetRouteTableAssociation', function() {
			//it('AWS::EC2::SubnetRouteTableAssociation\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.SubnetRouteTableAssociation, done);
			//});

			it('AWS::EC2::SubnetRouteTableAssociation\'s constructor method should work', function() {
				testConstructor(Resource.EC2.SubnetRouteTableAssociation);
			});
		});

		describe('AWS::EC2::Volume', function() {
			it('AWS::EC2::Volume\'s call method should work', function(done) {
				return testCall(Resource.EC2.Volume, done);
			});

			it('AWS::EC2::Volume\'s constructor method should work', function() {
				testConstructor(Resource.EC2.Volume);
			});
		});

		describe('AWS::EC2::VolumeAttachment', function() {
			//it('AWS::EC2::VolumeAttachment\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.VolumeAttachment, done);
			//});

			it('AWS::EC2::VolumeAttachment\'s constructor method should work', function() {
				testConstructor(Resource.EC2.VolumeAttachment);
			});
		});

		describe('AWS::EC2::VPC', function() {
			it('AWS::EC2::VPC\'s call method should work', function(done) {
				return testCall(Resource.EC2.VPC, done);
			});

			it('AWS::EC2::VPC\'s constructor method should work', function() {
				testConstructor(Resource.EC2.VPC);
			});
		});

		describe('AWS::EC2::VPCDHCPOptionsAssociation', function() {
			//it('AWS::EC2::VPCDHCPOptionsAssociation\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.VPCDHCPOptionsAssociation, done);
			//});

			it('AWS::EC2::VPCDHCPOptionsAssociation\'s constructor method should work', function() {
				testConstructor(Resource.EC2.VPCDHCPOptionsAssociation);
			});
		});

		describe('AWS::EC2::VPCEndpoint', function() {
			it('AWS::EC2::VPCEndpoint\'s call method should work', function(done) {
				return testCall(Resource.EC2.VPCEndpoint, done);
			});

			it('AWS::EC2::VPCEndpoint\'s constructor method should work', function() {
				testConstructor(Resource.EC2.VPCEndpoint);
			});
		});

		describe('AWS::EC2::VPCGatewayAttachment', function() {
			//it('AWS::EC2::VPCGatewayAttachment\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.VPCGatewayAttachment, done);
			//});

			it('AWS::EC2::VPCGatewayAttachment\'s constructor method should work', function() {
				testConstructor(Resource.EC2.VPCGatewayAttachment);
			});
		});

		describe('AWS::EC2::VPCPeeringConnection', function() {
			it('AWS::EC2::VPCPeeringConnection\'s call method should work', function(done) {
				return testCall(Resource.EC2.VPCPeeringConnection, done);
			});

			it('AWS::EC2::VPCPeeringConnection\'s constructor method should work', function() {
				testConstructor(Resource.EC2.VPCPeeringConnection);
			});
		});

		describe('AWS::EC2::VPNConnection', function() {
			it('AWS::EC2::VPNConnection\'s call method should work', function(done) {
				return testCall(Resource.EC2.VPNConnection, done);
			});

			it('AWS::EC2::VPNConnection\'s constructor method should work', function() {
				testConstructor(Resource.EC2.VPNConnection);
			});
		});

		describe('AWS::EC2::VPNConnectionRoute', function() {
			//it('AWS::EC2::VPNConnectionRoute\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.VPNConnectionRoute, done);
			//});

			it('AWS::EC2::VPNConnectionRoute\'s constructor method should work', function() {
				testConstructor(Resource.EC2.VPNConnectionRoute);
			});
		});

		describe('AWS::EC2::VPNGateway', function() {
			it('AWS::EC2::VPNGateway\'s call method should work', function(done) {
				return testCall(Resource.EC2.VPNGateway, done);
			});

			it('AWS::EC2::VPNGateway\'s constructor method should work', function() {
				testConstructor(Resource.EC2.VPNGateway);
			});
		});

		describe('AWS::EC2::VPNGatewayRoutePropagation', function() {
			//it('AWS::EC2::VPNGatewayRoutePropagation\'s call method should work', function(done) {
			//	return testCall(Resource.EC2.VPNGatewayRoutePropagation, done);
			//});

			it('AWS::EC2::VPNGatewayRoutePropagation\'s constructor method should work', function() {
				testConstructor(Resource.EC2.VPNGatewayRoutePropagation);
			});
		});

	});

});

<|endoftext|>


<commit_before>import thunk from "redux-thunk";
import { combineReducers, createStore, applyMiddleware, compose } from "redux";
import _gluestick from "./reducers";
import promiseMiddleware from "../lib/promiseMiddleware";

export default function (client, customRequire, customMiddleware, hotCallback, devMode) {
  const reducer = combineReducers(Object.assign({}, {_gluestick}, customRequire()));

  let middleware = [
    promiseMiddleware(client),
    thunk,
  ];

  // Include middleware that will warn when you mutate the state object
  // but only include it in dev mode
  if (devMode) {
    middleware.push(require("redux-immutable-state-invariant")());
  }

  // When `customMiddleware` is of type `function`, pass it a shallow
  // copy of the current array of `middlewares` and expect a new value
  // in return. Fallback to default behaviour.
  middleware = typeof customMiddleware === 'function'
    ? customMiddleware([...middleware])
    : middleware.concat(customMiddleware);

  const composeArgs = [
    applyMiddleware.apply(this, middleware),
    typeof window === "object" && typeof window.devToolsExtension !== "undefined" ? window.devToolsExtension() : f => f
  ];

  const finalCreateStore = compose.apply(null, composeArgs)(createStore);
  const store = finalCreateStore(reducer, typeof window !== "undefined" ? window.__INITIAL_STATE__ : {});

  if (hotCallback) {
    hotCallback(() => {
      const nextReducer = combineReducers(Object.assign({}, {_gluestick}, customRequire()));
      store.replaceReducer(nextReducer);
    });
  }

  return store;
}
<commit_msg>Clarify since it's not a shallow clone hehe<commit_after>import thunk from "redux-thunk";
import { combineReducers, createStore, applyMiddleware, compose } from "redux";
import _gluestick from "./reducers";
import promiseMiddleware from "../lib/promiseMiddleware";

export default function (client, customRequire, customMiddleware, hotCallback, devMode) {
  const reducer = combineReducers(Object.assign({}, {_gluestick}, customRequire()));

  let middleware = [
    promiseMiddleware(client),
    thunk,
  ];

  // Include middleware that will warn when you mutate the state object
  // but only include it in dev mode
  if (devMode) {
    middleware.push(require("redux-immutable-state-invariant")());
  }

  // When `customMiddleware` is of type `function`, pass it current
  // array of `middlewares` and expect a new value in return.
  // Fallback to default behaviour.
  middleware = typeof customMiddleware === 'function'
    ? customMiddleware([...middleware])
    : middleware.concat(customMiddleware);

  const composeArgs = [
    applyMiddleware.apply(this, middleware),
    typeof window === "object" && typeof window.devToolsExtension !== "undefined" ? window.devToolsExtension() : f => f
  ];

  const finalCreateStore = compose.apply(null, composeArgs)(createStore);
  const store = finalCreateStore(reducer, typeof window !== "undefined" ? window.__INITIAL_STATE__ : {});

  if (hotCallback) {
    hotCallback(() => {
      const nextReducer = combineReducers(Object.assign({}, {_gluestick}, customRequire()));
      store.replaceReducer(nextReducer);
    });
  }

  return store;
}
<|endoftext|>


<commit_before>'use strict';

/* global describe it */

// core modules
var assert = require('assert');

// local modules
var LineStream = require('../../lib/streams/LineStream.js');
var many = require('../../lib/consumers/many.js');
var single = require('../../lib/consumers/single.js');

describe('many', function() {
  it('accepts three a\'s in aaabbbccc', function() {
    var stream = LineStream('test', 'aaabbbccc');
    var parser = many(single('a'));

    var parseResult = parser(stream);

    assert(parseResult.accepted);
    assert(parseResult.valid);

    assert.deepEqual(
      parseResult.value.map(function(el) {
        return el.value;
      }),
      ['a', 'a', 'a']
    );
  });
});
<commit_msg>Add another test for many<commit_after>'use strict';

/* global describe it */

// core modules
var assert = require('assert');

// local modules
var LineStream = require('../../lib/streams/LineStream.js');
var many = require('../../lib/consumers/many.js');
var single = require('../../lib/consumers/single.js');

describe('many', function() {
  it('accepts three a\'s in aaabbbccc', function() {
    var stream = LineStream('test', 'aaabbbccc');
    var parser = many(single('a'));

    var parseResult = parser(stream);

    assert(parseResult.accepted);
    assert(parseResult.valid);

    assert.deepEqual(
      parseResult.value.map(function(el) {
        return el.value;
      }),
      ['a', 'a', 'a']
    );
  });

  it('accepts zero b\'s in aaabbbccc', function() {
    var stream = LineStream('test', 'aaabbbccc');
    var parser = many(single('b'));

    var parseResult = parser(stream);

    assert(parseResult.accepted);
    assert(parseResult.valid);

    // many never rejects, it just finds zero matches
    assert.deepEqual(parseResult.value, []);
  });
});
<|endoftext|>


<commit_before>(function(root) {

var helper = root.helper

if (typeof require != 'undefined') {
    helper = require('./helper')
}

var Board = function(size, arrangement, captures) {
    this.size = !isNaN(size) ? size : 19
    this.captures = captures ? { '-1': captures['-1'], '1': captures['1'] } : { '-1': 0, '1': 0 }
    this.arrangement = {}
    this.overlays = {}

    // Initialize arrangement
    for (var x = 0; x < this.size; x++) {
        for (var y = 0; y < this.size; y++) {
            this.arrangement[[x, y]] = arrangement ? arrangement[[x, y]] : 0
        }
    }
}

Board.prototype = {
    clone: function() {
        return this.makeMove(0)
    },

    hasVertex: function(vertex) {
        var x = vertex[0], y = vertex[1]
        return 0 <= Math.min(x, y) && Math.max(x, y) < this.size
    },

    clear: function() {
        for (var x = 0; x < this.size; x++) {
            for (var y = 0; y < this.size; y++) {
                this.arrangement[[x, y]] = 0
            }
        }
    },

    getNeighborhood: function(vertex) {
        var self = this
        if (!self.hasVertex(vertex)) return []
        var x = vertex[0], y = vertex[1]

        return [
            [x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]
        ].filter(self.hasVertex.bind(self))
    },

    getConnectedComponent: function(vertex, colors, result) {
        if (!this.hasVertex(vertex)) return []
        if (!result) result = [vertex]

        // Recursive depth-first search
        this.getNeighborhood(vertex).forEach(function(v) {
            if (colors.indexOf(this.arrangement[v]) == -1) return
            if (result.some(function(w) { return w[0] == v[0] && w[1] == v[1] })) return

            result.push(v)
            this.getConnectedComponent(v, colors, result)
        }.bind(this))

        return result
    },

    getChain: function(vertex) {
        return this.getConnectedComponent(vertex, [this.arrangement[vertex]])
    },

    getLiberties: function(vertex) {
        if (this.arrangement[vertex] == 0) return []

        var chain = this.getChain(vertex)
        var liberties = []

        chain.forEach(function(c) {
            liberties.push.apply(liberties, this.getNeighborhood(c).filter(function(n) {
                return this.arrangement[n] == 0
                && !liberties.some(function(v) { return v[0] == n[0] && v[1] == n[1] })
            }.bind(this)))
        }.bind(this))

        return liberties
    },

    getRelatedChains: function(vertex) {
        if (this.arrangement[vertex] == 0) return []

        var area = this.getConnectedComponent(vertex, [this.arrangement[vertex], 0])
        return area.filter(function(v) {
            return this.arrangement[v] == this.arrangement[vertex]
        }, this)
    },

    getAreaMap: function() {
        var self = this
        var map = {}

        for (var i = 0; i < self.size; i++) {
            for (var j = 0; j < self.size; j++) {
                var vertex = [i, j]

                if (vertex in map) continue
                if (self.arrangement[vertex] != 0) {
                    map[vertex] = self.arrangement[vertex]
                    continue
                }

                var chain = self.getChain(vertex)
                var sign = 0
                var indicator = 1

                chain.forEach(function(c) {
                    if (indicator == 0) return

                    self.getNeighborhood(c).forEach(function(n) {
                        if (self.arrangement[n] == 0 || indicator == 0) return

                        if (sign == 0) sign = map[n] = self.arrangement[n]
                        else if (sign != self.arrangement[n]) indicator = 0
                    })
                })

                chain.forEach(function(c) {
                    map[c] = sign * indicator
                })
            }
        }

        return map
    },

    getScore: function(areaMap) {
        var score = {}

        score['area_1'] = 0
        score['area_-1'] = 0
        score['territory_1'] = 0
        score['territory_-1'] = 0
        score['captures_1'] = this.captures['1']
        score['captures_-1'] = this.captures['-1']

        for (var vertex in areaMap) {
            var sign = areaMap[vertex]
            if (sign == 0) continue

            score['area_' + sign]++
            if (this.arrangement[vertex] == 0) score['territory_' + sign]++
        }

        return score
    },

    isValid: function() {
        var liberties = {}

        for (var x = 0; x < this.size; x++) {
            for (var y = 0; y < this.size; y++) {
                var vertex = [x, y]
                if (vertex in liberties || this.arrangement[vertex] == 0) continue

                var l = this.getLiberties(vertex).length
                if (l == 0) return false

                this.getChain(vertex).forEach(function(v) {
                    liberties[v] = l
                })
            }
        }

        return true
    },

    makeMove: function(sign, vertex) {
        var move = new Board(this.size, this.arrangement, this.captures)

        if (sign == 0 || !this.hasVertex(vertex)) return move
        if (this.arrangement[vertex] != 0) return null

        sign = sign > 0 ? 1 : -1
        var suicide = true

        // Remove captured stones
        this.getNeighborhood(vertex).forEach(function(n) {
            if (move.arrangement[n] != -sign) return

            var ll = this.getLiberties(n)
            if (ll.length != 1) return

            var l = ll[0]
            if (l[0] != vertex[0] || l[1] != vertex[1]) return

            this.getChain(n).forEach(function(c) {
                move.arrangement[c] = 0
                move.captures[sign.toString()]++
            })

            suicide = false;
        }.bind(this))

        move.arrangement[vertex] = sign

        // Detect suicide
        if (suicide) {
            var chain = move.getChain(vertex)

            if (move.getLiberties(vertex).length == 0) {
                chain.forEach(function(c) {
                    move.arrangement[c] = 0
                    move.captures[(-sign).toString()]++
                })
            }
        }

        return move
    },

    getHandicapPlacement: function(count) {
        if (this.size < 6 || count < 2) return []

        var near = this.size >= 13 ? 3 : 2
        var far = this.size - near - 1

        var result = [[near, near], [far, far], [near, far], [far, near]]

        if (this.size % 2 != 0) {
            var middle = (this.size - 1) / 2
            if (count == 5) result.push([middle, middle])
            result.push([near, middle], [far, middle])
            if (count == 7) result.push([middle, middle])
            result.push([middle, near], [middle, far], [middle, middle])
        }

        return result.slice(0, count)
    },

    guessDeadStones: function() {
        var self = this
        var map = self.getAreaMap()
        var done = {}
        var result = []

        for (var i = 0; i < this.size; i++) {
            for (var j = 0; j < this.size; j++) {
                var vertex = [i, j]
                if (map[vertex] != 0 || vertex in done) continue

                var posArea = self.getConnectedComponent(vertex, [0, -1])
                var negArea = self.getConnectedComponent(vertex, [0, 1])
                var posDead = posArea.filter(function(v) { return self.arrangement[v] == -1 })
                var negDead = negArea.filter(function(v) { return self.arrangement[v] == 1 })

                var sign = 0
                var actualArea, actualDead

                var negDiff = negArea.filter(function(y) {
                    return !negDead.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                        && !posArea.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                })

                var posDiff = posArea.filter(function(y) {
                    return !posDead.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                        && !negArea.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                })

                if (negDiff.length <= 1 && negDead.length <= posDead.length) {
                    sign--
                    actualArea = negArea
                    actualDead = negDead
                }

                if (posDiff.length <= 1 && posDead.length <= negDead.length) {
                    sign++
                    actualArea = posArea
                    actualDead = posDead
                }

                if (sign == 0) {
                    actualArea = self.getChain(vertex)
                    actualDead = []
                }

                actualArea.forEach(function(v) {
                    done[v] = 1
                })

                result.push.apply(result, actualDead)
            }
        }

        return result
    },

    getHash: function() {
        return helper.hash(JSON.stringify(this.arrangement))
    }
}

if (typeof module != 'undefined') module.exports = Board
else window.Board = Board

}).call(null, typeof module != 'undefined' ? module : window)
<commit_msg>Add Board.getDistance()<commit_after>(function(root) {

var helper = root.helper

if (typeof require != 'undefined') {
    helper = require('./helper')
}

var Board = function(size, arrangement, captures) {
    this.size = !isNaN(size) ? size : 19
    this.captures = captures ? { '-1': captures['-1'], '1': captures['1'] } : { '-1': 0, '1': 0 }
    this.arrangement = {}
    this.overlays = {}

    // Initialize arrangement
    for (var x = 0; x < this.size; x++) {
        for (var y = 0; y < this.size; y++) {
            this.arrangement[[x, y]] = arrangement ? arrangement[[x, y]] : 0
        }
    }
}

Board.prototype = {
    clone: function() {
        return this.makeMove(0)
    },

    hasVertex: function(vertex) {
        var x = vertex[0], y = vertex[1]
        return 0 <= Math.min(x, y) && Math.max(x, y) < this.size
    },

    clear: function() {
        for (var x = 0; x < this.size; x++) {
            for (var y = 0; y < this.size; y++) {
                this.arrangement[[x, y]] = 0
            }
        }
    },

    getDistance: function(v, w) {
        return Math.abs(v[0] - w[0]) + Math.abs(v[1] - w[1])
    },

    getNeighborhood: function(vertex) {
        var self = this
        if (!self.hasVertex(vertex)) return []
        var x = vertex[0], y = vertex[1]

        return [
            [x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]
        ].filter(self.hasVertex.bind(self))
    },

    getConnectedComponent: function(vertex, colors, result) {
        if (!this.hasVertex(vertex)) return []
        if (!result) result = [vertex]

        // Recursive depth-first search
        this.getNeighborhood(vertex).forEach(function(v) {
            if (colors.indexOf(this.arrangement[v]) == -1) return
            if (result.some(function(w) { return w[0] == v[0] && w[1] == v[1] })) return

            result.push(v)
            this.getConnectedComponent(v, colors, result)
        }.bind(this))

        return result
    },

    getChain: function(vertex) {
        return this.getConnectedComponent(vertex, [this.arrangement[vertex]])
    },

    getLiberties: function(vertex) {
        if (this.arrangement[vertex] == 0) return []

        var chain = this.getChain(vertex)
        var liberties = []

        chain.forEach(function(c) {
            liberties.push.apply(liberties, this.getNeighborhood(c).filter(function(n) {
                return this.arrangement[n] == 0
                && !liberties.some(function(v) { return v[0] == n[0] && v[1] == n[1] })
            }.bind(this)))
        }.bind(this))

        return liberties
    },

    getRelatedChains: function(vertex) {
        if (this.arrangement[vertex] == 0) return []

        var area = this.getConnectedComponent(vertex, [this.arrangement[vertex], 0])
        return area.filter(function(v) {
            return this.arrangement[v] == this.arrangement[vertex]
        }, this)
    },

    getAreaMap: function() {
        var self = this
        var map = {}

        for (var i = 0; i < self.size; i++) {
            for (var j = 0; j < self.size; j++) {
                var vertex = [i, j]

                if (vertex in map) continue
                if (self.arrangement[vertex] != 0) {
                    map[vertex] = self.arrangement[vertex]
                    continue
                }

                var chain = self.getChain(vertex)
                var sign = 0
                var indicator = 1

                chain.forEach(function(c) {
                    if (indicator == 0) return

                    self.getNeighborhood(c).forEach(function(n) {
                        if (self.arrangement[n] == 0 || indicator == 0) return

                        if (sign == 0) sign = map[n] = self.arrangement[n]
                        else if (sign != self.arrangement[n]) indicator = 0
                    })
                })

                chain.forEach(function(c) {
                    map[c] = sign * indicator
                })
            }
        }

        return map
    },

    getScore: function(areaMap) {
        var score = {}

        score['area_1'] = 0
        score['area_-1'] = 0
        score['territory_1'] = 0
        score['territory_-1'] = 0
        score['captures_1'] = this.captures['1']
        score['captures_-1'] = this.captures['-1']

        for (var vertex in areaMap) {
            var sign = areaMap[vertex]
            if (sign == 0) continue

            score['area_' + sign]++
            if (this.arrangement[vertex] == 0) score['territory_' + sign]++
        }

        return score
    },

    isValid: function() {
        var liberties = {}

        for (var x = 0; x < this.size; x++) {
            for (var y = 0; y < this.size; y++) {
                var vertex = [x, y]
                if (vertex in liberties || this.arrangement[vertex] == 0) continue

                var l = this.getLiberties(vertex).length
                if (l == 0) return false

                this.getChain(vertex).forEach(function(v) {
                    liberties[v] = l
                })
            }
        }

        return true
    },

    makeMove: function(sign, vertex) {
        var move = new Board(this.size, this.arrangement, this.captures)

        if (sign == 0 || !this.hasVertex(vertex)) return move
        if (this.arrangement[vertex] != 0) return null

        sign = sign > 0 ? 1 : -1
        var suicide = true

        // Remove captured stones
        this.getNeighborhood(vertex).forEach(function(n) {
            if (move.arrangement[n] != -sign) return

            var ll = this.getLiberties(n)
            if (ll.length != 1) return

            var l = ll[0]
            if (l[0] != vertex[0] || l[1] != vertex[1]) return

            this.getChain(n).forEach(function(c) {
                move.arrangement[c] = 0
                move.captures[sign.toString()]++
            })

            suicide = false;
        }.bind(this))

        move.arrangement[vertex] = sign

        // Detect suicide
        if (suicide) {
            var chain = move.getChain(vertex)

            if (move.getLiberties(vertex).length == 0) {
                chain.forEach(function(c) {
                    move.arrangement[c] = 0
                    move.captures[(-sign).toString()]++
                })
            }
        }

        return move
    },

    getHandicapPlacement: function(count) {
        if (this.size < 6 || count < 2) return []

        var near = this.size >= 13 ? 3 : 2
        var far = this.size - near - 1

        var result = [[near, near], [far, far], [near, far], [far, near]]

        if (this.size % 2 != 0) {
            var middle = (this.size - 1) / 2
            if (count == 5) result.push([middle, middle])
            result.push([near, middle], [far, middle])
            if (count == 7) result.push([middle, middle])
            result.push([middle, near], [middle, far], [middle, middle])
        }

        return result.slice(0, count)
    },

    guessDeadStones: function() {
        var self = this
        var map = self.getAreaMap()
        var done = {}
        var result = []

        for (var i = 0; i < this.size; i++) {
            for (var j = 0; j < this.size; j++) {
                var vertex = [i, j]
                if (map[vertex] != 0 || vertex in done) continue

                var posArea = self.getConnectedComponent(vertex, [0, -1])
                var negArea = self.getConnectedComponent(vertex, [0, 1])
                var posDead = posArea.filter(function(v) { return self.arrangement[v] == -1 })
                var negDead = negArea.filter(function(v) { return self.arrangement[v] == 1 })

                var sign = 0
                var actualArea, actualDead

                var negDiff = negArea.filter(function(y) {
                    return !negDead.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                        && !posArea.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                })

                var posDiff = posArea.filter(function(y) {
                    return !posDead.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                        && !negArea.some(function(x) { return x[0] == y[0] && x[1] == y[1] })
                })

                if (negDiff.length <= 1 && negDead.length <= posDead.length) {
                    sign--
                    actualArea = negArea
                    actualDead = negDead
                }

                if (posDiff.length <= 1 && posDead.length <= negDead.length) {
                    sign++
                    actualArea = posArea
                    actualDead = posDead
                }

                if (sign == 0) {
                    actualArea = self.getChain(vertex)
                    actualDead = []
                }

                actualArea.forEach(function(v) {
                    done[v] = 1
                })

                result.push.apply(result, actualDead)
            }
        }

        return result
    },

    getHash: function() {
        return helper.hash(JSON.stringify(this.arrangement))
    }
}

if (typeof module != 'undefined') module.exports = Board
else window.Board = Board

}).call(null, typeof module != 'undefined' ? module : window)
<|endoftext|>


<commit_before>/* jshint node:true */
/* global it, before, beforeEach, after, afterEach */
'use strict';

var _ = require('lodash');

/**
 *
 * Attaches then / catch callbacks to a given promise.
 *
 * Will call done() on promise resolution and done(error)
 * on rejection.
 *
 */
function attachPromiseHandlers (fnc, done) {
  try {
    var promise = fnc.apply(null, [done]);
    if (_.isFunction(promise.then) && _.isFunction(promise.catch)) {
      return promise
        .then(function () {
          done();
        })
        .catch(function (error) {
          done(error);
        });
    }
    done();
  }
  catch (error) {
    done(error);
  }
}

/**
 *
 * Attaches then / catch callbacks to a given promise.
 *
 * Will call done(error) on promise resolution and
 * done() on rejection, thus resolving the test whenever
 * the promise gets rejected.
 *
 */
function attachErrorHandlers (fnc, done) {
  try {
    var promise = fnc.apply(null, [done]);
    if (_.isFunction(promise.then) && _.isFunction(promise.catch)) {
      return promise
        .then(function () {
          done(new Error('Test failed. Expected promise to be rejected.'));
        })
        .catch(function (error) {
          done();
        });
    }
    done(new Error('Test failed. Expected function to throw error.'));
  }
  catch (error) {
    done();
  }
}

var doneFnc;

var itFnc = it;
var beforeFnc = before;
var beforeEachFnc = beforeEach;
var afterFnc = after;
var afterEachFnc = afterEach;

/**
 *
 * Promise wrapper around the mocha.js library.
 *
 * Adds a number of convenience methods for handling
 * promises as results of tests.
 *
 */
module.exports = {

  _test: function setDone(fnc, done) {
    doneFnc = done;

    itFnc = fnc;
    beforeFnc = fnc;
    afterFnc = fnc;
    beforeEachFnc = fnc;
    afterEachFnc = fnc;
  },

  /**
   *
   * Promise variant of the default it-test.
   *
   * Handles a returned promise and
   * calls done() to conclude the test in case of
   * a positive resolution.
   *
   * Fails the the test if the promise gets rejected
   * or any of the assertions within the test run fails.
   *
   */
  it: function promiseIt (description, fnc) {
    return itFnc(description, function (done) {
      return attachPromiseHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Convenience variant for testing promise rejections.
   *
   * Handles a returned promise and
   * calls done() to conclude the test in case of
   * a negative resolution, i.e., the catch block
   * of the promise handling chain is triggered.
   *
   * Fails the the test if the promise gets resolved.
   *
   */
  catchIt: function catchIt (description, fnc) {
    return itFnc(description, function (done) {
      return attachErrorHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Promise variant of the default before-hook.
   *
   * Handles a returned promise and
   * calls done() to conclude the hook in case of
   * a positive resolution.
   *
   * Fails the the hook if the promise gets rejected.
   *
   */
  before: function promiseBefore(fnc) {
    return beforeFnc(function (done) {
      return attachPromiseHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Promise variant of the default beforeEach-hook.
   *
   * Handles a returned promise and
   * calls done() to conclude the hook in case of
   * a positive resolution.
   *
   * Fails the the hook if the promise gets rejected.
   *
   */
  beforeEach: function promiseBeforeEach(fnc) {
    return beforeEachFnc(function (done) {
      return attachPromiseHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Promise variant of the default after-hook.
   *
   * Handles a returned promise and
   * calls done() to conclude the hook in case of
   * a positive resolution.
   *
   * Fails the the hook if the promise gets rejected.
   *
   */
  after: function promiseAfter(fnc) {
    return afterFnc(function (done) {
      return attachPromiseHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Promise variant of the default afterEach-hook.
   *
   * Handles a returned promise and
   * calls done() to conclude the hook in case of
   * a positive resolution.
   *
   * Fails the the hook if the promise gets rejected.
   *
   */
  afterEach: function promiseAfterEach(fnc) {
    return afterEachFnc(function (done) {
      return attachPromiseHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Adds all promise hooks as global variables
   *
   */
  global: function makeGlobal () {

    GLOBAL.it = this.it;
    GLOBAL.catchIt = this.catchIt;
    GLOBAL.before = this.before;
    GLOBAL.beforeEach = this.beforeEach;
    GLOBAL.after = this.after;
    GLOBAL.afterEach = this.afterEach;

    return this;
  }

};<commit_msg>Refactors common functions.<commit_after>/* jshint node:true */
/* global it, before, beforeEach, after, afterEach */
'use strict';

var _ = require('lodash');

function createError (message, error) {
  return new Error([
    message,
    'Error was:',
    JSON.stringify(error, null, 2)
  ].join(' '));
}

/**
 *
 * Attaches then / catch callbacks to a given promise.
 *
 * Will call done() on promise resolution and done(error)
 * on rejection.
 *
 */
function attachPromiseHandlers (fnc, done) {

  function resolve () {
    done();
  }

  function reject (error) {
    if (!_.isError(error)) {
      error = createError('Test failed. Expected promise to be resolved.', error);
    }
    done(error);
  }

  try {
    var promise = fnc.apply(null, [done]);
    if (_.isFunction(promise.then) && _.isFunction(promise.catch)) {
      return promise
        .then(resolve)
        .catch(reject);
    }
    else if (_.isFunction(promise.then)) {
      return promise
        .then(resolve, reject);
    }
    resolve();
  }
  catch (error) {
    reject(error);
  }
}

/**
 *
 * Attaches then / catch callbacks to a given promise.
 *
 * Will call done(error) on promise resolution and
 * done() on rejection, thus resolving the test whenever
 * the promise gets rejected.
 *
 */
function attachErrorHandlers (fnc, done) {
  function resolve () {
    done();
  }

  function reject (error) {
    if (!_.isError(error)) {
      error = createError('Test failed. Expected promise to be rejected.', error);
    }

    done(error);
  }

  try {
    var promise = fnc.apply(null, [done]);
    if (_.isFunction(promise.then) && _.isFunction(promise.catch)) {
      return promise
        .then(reject)
        .catch(resolve);
    }
    else if (_.isFunction(promise.then)) {
      return promise
        .then(reject, resolve);
    }
    reject();
  }
  catch (error) {
    resolve();
  }
}

var doneFnc;

var itFnc = it;
var beforeFnc = before;
var beforeEachFnc = beforeEach;
var afterFnc = after;
var afterEachFnc = afterEach;

/**
 *
 * Promise wrapper around the mocha.js library.
 *
 * Adds a number of convenience methods for handling
 * promises as results of tests.
 *
 */
module.exports = {

  _test: function setDone(fnc, done) {
    doneFnc = done;

    itFnc = fnc;
    beforeFnc = fnc;
    afterFnc = fnc;
    beforeEachFnc = fnc;
    afterEachFnc = fnc;
  },

  /**
   *
   * Promise variant of the default it-test.
   *
   * Handles a returned promise and
   * calls done() to conclude the test in case of
   * a positive resolution.
   *
   * Fails the the test if the promise gets rejected
   * or any of the assertions within the test run fails.
   *
   */
  it: function promiseIt (description, fnc) {
    return itFnc(description, function (done) {
      return attachPromiseHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Convenience variant for testing promise rejections.
   *
   * Handles a returned promise and
   * calls done() to conclude the test in case of
   * a negative resolution, i.e., the catch block
   * of the promise handling chain is triggered.
   *
   * Fails the the test if the promise gets resolved.
   *
   */
  catchIt: function catchIt (description, fnc) {
    return itFnc(description, function (done) {
      return attachErrorHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Promise variant of the default before-hook.
   *
   * Handles a returned promise and
   * calls done() to conclude the hook in case of
   * a positive resolution.
   *
   * Fails the the hook if the promise gets rejected.
   *
   */
  before: function promiseBefore(fnc) {
    return beforeFnc(function (done) {
      return attachPromiseHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Promise variant of the default beforeEach-hook.
   *
   * Handles a returned promise and
   * calls done() to conclude the hook in case of
   * a positive resolution.
   *
   * Fails the the hook if the promise gets rejected.
   *
   */
  beforeEach: function promiseBeforeEach(fnc) {
    return beforeEachFnc(function (done) {
      return attachPromiseHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Promise variant of the default after-hook.
   *
   * Handles a returned promise and
   * calls done() to conclude the hook in case of
   * a positive resolution.
   *
   * Fails the the hook if the promise gets rejected.
   *
   */
  after: function promiseAfter(fnc) {
    return afterFnc(function (done) {
      return attachPromiseHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Promise variant of the default afterEach-hook.
   *
   * Handles a returned promise and
   * calls done() to conclude the hook in case of
   * a positive resolution.
   *
   * Fails the the hook if the promise gets rejected.
   *
   */
  afterEach: function promiseAfterEach(fnc) {
    return afterEachFnc(function (done) {
      return attachPromiseHandlers(fnc, doneFnc || done);
    });
  },

  /**
   *
   * Adds all promise hooks as global variables
   *
   */
  global: function makeGlobal () {

    GLOBAL.it = this.it;
    GLOBAL.catchIt = this.catchIt;
    GLOBAL.before = this.before;
    GLOBAL.beforeEach = this.beforeEach;
    GLOBAL.after = this.after;
    GLOBAL.afterEach = this.afterEach;

    return this;
  }

};<|endoftext|>